1. 程式人生 > >網路程式設計學習筆記(ioctl操作)

網路程式設計學習筆記(ioctl操作)

1、ioctl函式

其函式需要的標頭檔案及宣告如下:

#include <unistd.h>
int ioctl(int fd, int request, .../*void *arg/);

第三個引數總是一個指標,但指標的型別依賴於request

把和網路有關的請求分為6類:

(1)套介面操作

(2)檔案操作

(3)介面操作

(4)ARP快取記憶體操作

(5)路由表操作

(6)流系統

2、套介面操作

有三種ioctl請求是針對套介面的,它們都要求ioctl的第三個引數是一個指向整數的指標

類別 request(請求) 描述 資料型別
套介面         SIOCATMARK
SIOCSPGRP
SIOCGPGRP
在帶外標誌上
設定套介面的程序ID或程序組ID
獲取套介面的程序ID或程序組ID
int
int
int
檔案 FIONBIO
FIOASYNC
FIONREAD
FIOSETOWN
FIOGETOWN
設定/清除非阻塞標誌
設定/清除非同步I/O標誌
獲取接收緩衝區中的位元組數
設定檔案的程序ID或程序組ID
獲取檔案的程序ID或程序組ID
int
int
int
int
int
介面 SIOCGIFCONF
SIOCSIFADDR
SIOCGIFADDR
SIOCSIFFLAGS
SIOCGIFFLAGS
SIOCSIFDSTADDR
SIOCGIFDSTADDR
SIOCGIFBRDADDR
SIOCSIFBRDADDR
SIOCGIFNETMASK
SIOCSIFNETMASK
SIOCGIFMETRIC
SIOCSIFMETRIC
獲取所有介面的列表
設定介面地址
獲取介面地址
設定介面標誌
獲取介面標誌
設定點到點地址
獲取點到點地址
獲取廣播地址
設定廣播地址
獲取子網掩碼
設定子網掩碼
獲取介面的測度
設定介面的測度
struct ifconf
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
ARP SIOCSARP
SIOCGARP
SIOCDARD
建立/修改arp項
獲取arp項
刪除arp項
struct arpreq
struct arpreq
struct arpreq
路由 SIOCADDRT
SIOCDELRT
增加路徑
刪除路徑
struct rtentry
struct rtenrty
I_xxx
 

3、獲取介面資訊

有兩個結構體ifconf和ifreq,其定義如下:

struct ifconf
{
	int ifc_len;
	union {
		caddr_t ifcu_buf; //input from user->kernel
		struct ifreq *ifcu_req;//return from kernel->user
	}ifc_ifcu;
};

#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req

#define IFNAMSIZ 16

struct ifreq
{
	char ifr_name[IFNAMSIZ];
	union {
		struct sockaddr ifru_addr;
		struct sockaddr ifru_dstaddr;
		struct sockaddr ifru_broadaddr;
		short ifru_flags;
		int ifru_metric;
		caddr_t ifru_data;
	}ifr_ifru;
};

#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
#define ifr_flags ifr_ifru.ifru_flags
#define ifr_metric ifr_ifru.ifru_metric
#define ifr_data ifr_ifru.ifru_data


#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>

#define IFI_NAME 16
#define IFI_HADDR 8
#define IFI_ALIAS 1


struct ifi_info
{
	char ifi_name[IFI_NAME];	
	char ifi_haddr[IFI_HADDR];
	short ifi_hlen;
	short ifi_flags;
	short ifi_myflags;
	struct sockaddr* ifi_addr;
	struct sockaddr* ifi_brdaddr;
	struct sockaddr* ifi_dstaddr;
	struct ifi_info *ifi_next;
};

struct ifi_info *get_ifi_info(int,int);
struct ifi_info *Get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *);
char *sock_ntop(struct sockaddr*, int);

int main(int argc, char **argv)
{
	struct ifi_info *ifi, *ifihead;
	struct sockaddr *sa;
	char *ptr;
	int i, family, doaliases;

	if (argc != 3) {
		fprintf(stderr, "usage:prifinfo <inet4|inet6> <doaliases>\n");
		return -1;
	}

	if (strcmp(argv[1], "inet4") == 0) {
		family = AF_INET;
	} else if (strcmp(argv[1], "inet6") == 0) {
		family = AF_INET6;
	}

	doaliases = atoi(argv[2]);

	for (ifihead = ifi = get_ifi_info(family, doaliases); ifi != NULL; ifi = ifi->ifi_next) {
		printf("%s:<" , ifi->ifi_name);
		if (ifi->ifi_flags & IFF_UP) printf("UP ");
		if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
		if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
		if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
		if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
		printf("> \n");
		
		if ((i = ifi->ifi_hlen) > 0) {
			ptr = ifi->ifi_haddr;
			do {
				printf("%s%x", (i == ifi->ifi_hlen) ? " " : ":", *ptr++);
			} while (--i > 0);
			printf("\n");
		}

		if ((sa = ifi->ifi_addr) != NULL) {
			printf(" IP addr: %s\n", sock_ntop(sa, sizeof(*sa)));
		}

		if((sa = ifi->ifi_brdaddr) != NULL) {
			printf(" broadcast addr: %s\n", sock_ntop(sa, sizeof(*sa)));
		}

		if ((sa = ifi->ifi_dstaddr) != NULL) {
			printf(" destination addr: %s\n", sock_ntop(sa, sizeof(*sa)));	
		}

		
	}
	
	free_ifi_info(ifihead);
	return 0;
}

struct ifi_info *get_ifi_info(int family, int doaliases)
{
	struct ifi_info *ifi, *ifihead, **ifipnext;
	int sockfd, len, lastlen, flags, myflags;
	char *ptr, *buf, lastname[128], *cptr;
	struct ifconf ifc;
	struct ifreq *ifr, ifrcopy;
	struct sockaddr_in *sinptr;
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		fprintf(stderr, "socket error:%s\n", strerror(errno));
		return NULL;
	}

	lastlen = 0;
	len = 100 * sizeof(struct ifreq);
	for (;;) {
		buf = malloc(len);
		ifc.ifc_len = len;
		ifc.ifc_buf = buf;
		if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
			if (errno != EINVAL || lastlen != 0) {
				fprintf(stderr, "ioctl error:%s\n", strerror(errno));
				return NULL;
			}
		} else {
			if (ifc.ifc_len == lastlen) break;
			lastlen = ifc.ifc_len;
		}
		len += 10 * sizeof(struct ifreq);
		free(buf);
	}

	ifihead = NULL;
	ifipnext = &ifihead;
	lastname[0] = 0;
	
	for (ptr = buf; ptr < buf + ifc.ifc_len;) {
		ifr = (struct ifreq*)ptr;
		switch(ifr->ifr_addr.sa_family) {
		case AF_INET6:
			len = sizeof(struct sockaddr_in6);
			break;	
		case AF_INET:
		default:
			len = sizeof(struct sockaddr_in);
			break;
		}

		ptr += sizeof(ifr->ifr_name) + len;
		if (ifr->ifr_addr.sa_family != family) {
			continue;
		}

		myflags = 0;
		if ((cptr = strchr(ifr->ifr_name, ':')) != NULL) {
			*cptr = 0;
		}

		if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
			if (doaliases == 0) {
				continue;
			}

			myflags = IFI_ALIAS;
			
		}
		memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
		ifrcopy = *ifr;
		ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
		flags = ifrcopy.ifr_flags;
		if ((flags & IFF_UP) == 0) continue;

		ifi = calloc(1, sizeof(struct ifi_info));
		*ifipnext = ifi;
		ifi->ifi_flags = flags;
		ifi->ifi_myflags = myflags;
		memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);	
		ifi->ifi_name[IFI_NAME - 1] = 0;
		

		switch(ifr->ifr_addr.sa_family) {
		case AF_INET:	
			sinptr = (struct sockaddr_in*)&ifr->ifr_addr;
			if (ifi->ifi_addr == NULL) {
				ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
				memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
			}
			if (flags & IFF_BROADCAST) {
				ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
				sinptr = (struct sockaddr_in*)&ifrcopy.ifr_broadaddr;
				ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
				memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));	
			}
			
			if (flags & IFF_POINTOPOINT) {
				ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
				sinptr = (struct sockaddr_in*)&ifrcopy.ifr_dstaddr;
				ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
				memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
			}

			break;
		default:
			break;
		}
	} 

	free(buf);
	return(ifihead);
}

void free_ifi_info(struct ifi_info *ifihead)
{
	struct ifi_info *ifi, *ifinext;

	for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
		if (ifi->ifi_addr != NULL) free(ifi->ifi_addr);
		if (ifi->ifi_brdaddr != NULL) free(ifi->ifi_brdaddr);
		if (ifi->ifi_dstaddr != NULL) free(ifi->ifi_dstaddr);
		ifinext = ifi->ifi_next;
		free(ifi);
	}
}


char *sock_ntop(struct sockaddr* sa, int len)
{
	char portstr[7];
	static char str[128];

	switch(sa->sa_family) {
	case AF_INET: 
	{
		struct sockaddr_in *sin = (struct sockaddr_in*)sa;
		if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return NULL;
		if (ntohs(sin->sin_port) != 0) {
			snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port));
			strcat(str, portstr);
		}
		return str;
	}
	}

	return NULL;
}

	














4、ARP快取記憶體操作

也是由ioctl函式操作的,這些請求用一個arpreq結構,在<net/if_arp.h>中定義

struct arpreq
{
   struct sockaddr arp_pa;    //protocol address
   struct sockaddr arp_ha;   //hardware address
   int arp_flags;
};

#define ATF_INUSE 0x01
#define ATF_COM 0x02
#define ATF_PERM 0x04
#define ATF_PUBL 0x08
SIOCSARP:把新項回到 ARP快取記憶體中或修改一個已有項。

SIOCDARP:從arp快取記憶體中刪除一項

SIOCGARP:從快取記憶體中取一項


獲取快取:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if_arp.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>

char **my_addrs(int *addtype);

int main(int argc, char **argv)
{
	int family, sockfd;
	char str[INET6_ADDRSTRLEN];
	char **pptr;
	unsigned char *ptr;
	struct arpreq arpreq;
	struct sockaddr_in *sin;
	
	pptr = my_addrs(&family);
	for (; *pptr != NULL; pptr++) {
		printf("%s : ", inet_ntop(family, *pptr, str, sizeof(str)));
		switch (family) {
		case AF_INET:	
			sockfd = socket(AF_INET, SOCK_DGRAM, 0);
			sin = (struct sockaddr_in*)&arpreq.arp_pa;
			bzero(sin, sizeof(struct sockaddr_in));
			sin->sin_family = AF_INET;
			memcpy(&sin->sin_addr, *pptr, sizeof(struct in_addr));
			ioctl(sockfd, SIOCGARP, &arpreq);
			ptr = &arpreq.arp_ha.sa_data[0];
			printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
			break;
		default:
			printf("unsupported address family:%d\n", family);
		}
	}

	return 0;
}


char **my_addrs(int *addrtype)
{
	struct hostent *hptr;
	struct utsname myname;

	if (uname(&myname) < 0) return NULL;
		
	if ((hptr = gethostbyname(myname.nodename)) == NULL) return NULL;

	*addrtype = hptr->h_addrtype;
	return hptr->h_addr_list;
}

5、路由表操作

有兩種ioctl請求用來操作路由表,這兩個請求要求ioctl的第三個引數必須是一個指向rtentry結構的指標,這個結構在<net/route.h>標頭檔案中定義。這些請求一般由route程式發出,只有超級使用者才能發出這些請求

SIOCADDRT:向路由表中新增一項

SIOCDELRT:從路由表中刪除一項