1、ioctl函数
其函数需要的头文件及声明如下:
#include <unistd.h> int ioctl(int fd, int request, .../*void *arg/);
把和网络有关的请求分为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 0x08SIOCSARP:把新项回到 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; }
有两种ioctl请求用来操作路由表,这两个请求要求ioctl的第三个参数必须是一个指向rtentry结构的指针,这个结构在<net/route.h>头文件中定义。这些请求一般由route程序发出,只有超级用户才能发出这些请求
SIOCADDRT:向路由表中添加一项
SIOCDELRT:从路由表中删除一项