标签:选项 pid view sysctl函数 next 简单 call sdl 整数
在创建套接字时,可以通过指定参数 AF_ROUTE 域创建路由套接字(它只能支持原始套接字,只有超级用户才能创建这个套接字),路由套接字可以访问内核中路由子系统的接口信息。
前两种操作可以复合使用。内核可以通过写一个路由套接字往内核发送一个消息,请求内核关于某个特定路径的所有消息,又可以通过路由套接字接受内核的应答。
通过路由套接字返回的一些信息中含有作为返回值给出的数据链路套接字地址结构。
struct sockaddr_dl { uint8_t sdl_len; sa_family_t sdl_family; /* AF_LINK */ uint16_t sdl_index; /* system assigned index, if > 0 */ uint8_t sdl_type; /* IFT_ETHER, etc. from <net/if_types.h> */ uint8_t sdl_nlen; /* name length, starting in sdl_data[0] */ uint8_t sdl_alen; /* link-layer address length */ uint8_t sdl_slen; /* link-layer selector length */ char sdl_data[12]; /* minimum work area, can be larger; contains i/f name and link-layer address */ }; #define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
sdl_data成员含有名字和链路层地址(例如以太网接口的48位MAC地址)。名字从sdl_data[0]开始,而且不以空字符结尾。链路层地址从sdl_data[sdl_nlen]开始。以上宏定义返回链路层地址的指针。
链路层套接字地址结构可变。如果地址和名字总长度超出12字节,结构体将大于20字节。32位系统上通常向上舍入到下一个4字节的倍数。IF_RECVIF套接字选项返回的本结构中,所有3个长度成员都为0。
创建路由套接字后,与其他套接字一样,可以调用 read() 或 write() 函数进行读写,但是由于系统内核是根据应用程序写入的消息来完成对路由信息的提供和修改的,因此,与其他套接字不同,写入和读出的数据都是有固定的格式。
通过路由套接字交换的结构有5个类型:rt_msghdr、if_msghdr、ifa_msghdr、ifma_msghdr和if_announcemsghdr。每个结构有相同的前3个成员:本消息的长度、版本和类型。类型成员是上图第一列中的常值之一。
rtm_addrs、ifm_addrs、ifam_addrs这三个成员是数位掩码,指明本消息后跟的套接字地址结构是8个可能选择中的哪几个。
#include "unproute.h" #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* sizeof(struct sockaddr_in6) * 8 = 192 */ #define SEQ 9999 /* * Round up ‘a‘ to next multiple of ‘size‘, which must be a power of 2 */ #define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) /* * Step to next socket address structure; * if sa_len is 0, assume it is sizeof(u_long). */ #define NEXT_SA(ap) ap = (SA *) \ ((caddr_t) ap + (ap->sa_len ? ROUNDUP(ap->sa_len, sizeof (u_long)) : sizeof(u_long))) void get_rtaddrs(int addrs, SA *sa, SA **rti_info) { int i; for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rti_info[i] = sa; NEXT_SA(sa); } else rti_info[i] = NULL; } } const char * sock_masktop(SA *sa, socklen_t salen) { static char str[INET6_ADDRSTRLEN]; unsigned char *ptr = &sa->sa_data[2]; if (sa->sa_len == 0) return("0.0.0.0"); else if (sa->sa_len == 5) snprintf(str, sizeof(str), "%d.0.0.0", *ptr); else if (sa->sa_len == 6) snprintf(str, sizeof(str), "%d.%d.0.0", *ptr, *(ptr+1)); else if (sa->sa_len == 7) snprintf(str, sizeof(str), "%d.%d.%d.0", *ptr, *(ptr+1), *(ptr+2)); else if (sa->sa_len == 8) snprintf(str, sizeof(str), "%d.%d.%d.%d", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); else snprintf(str, sizeof(str), "(unknown mask, len = %d, family = %d)", sa->sa_len, sa->sa_family); return(str); } int main(int argc, char **argv) { int sockfd; char *buf; pid_t pid; ssize_t n; struct rt_msghdr *rtm; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_in *sin; if (argc != 2) err_quit("usage: getrt <IPaddress>"); sockfd = Socket(AF_ROUTE, SOCK_RAW, 0); /* need superuser privileges */ buf = Calloc(1, BUFLEN); /* and initialized to 0 */ rtm = (struct rt_msghdr *) buf; rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST; rtm->rtm_pid = pid = getpid(); rtm->rtm_seq = SEQ; sin = (struct sockaddr_in *) (rtm + 1); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; Inet_pton(AF_INET, argv[1], &sin->sin_addr); Write(sockfd, rtm, rtm->rtm_msglen); do { n = Read(sockfd, rtm, BUFLEN); } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != SEQ || rtm->rtm_pid != pid); rtm = (struct rt_msghdr *) buf; sa = (struct sockaddr *) (rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); if ( (sa = rti_info[RTAX_DST]) != NULL) printf("dest: %s\n", Sock_ntop_host(sa, sa->sa_len)); if ( (sa = rti_info[RTAX_GATEWAY]) != NULL) printf("gateway: %s\n", Sock_ntop_host(sa, sa->sa_len)); if ( (sa = rti_info[RTAX_NETMASK]) != NULL) printf("netmask: %s\n", Sock_masktop(sa, sa->sa_len)); if ( (sa = rti_info[RTAX_GENMASK]) != NULL) printf("genmask: %s\n", Sock_masktop(sa, sa->sa_len)); exit(0); }
我们对路由套接字的主要兴趣点在于使用sysctl函数检查路由表和接口列表。创建一个路由套接字(AF_ROUTE)需要超级用户权限。使用sysctl检查路由表和接口列表的进程不限用户权限。
#include <sys/param.h> #include <sys/sysctl.h> int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); //返回:若成功为0,若出错-1
这个函数使用类似SNMP(简单网络管理协议)MIB(管理信息库)的名字。
name参数是指定名字的一个整数数组,namelen参数指定该数组中的元素数目。该数组中的第一个元素指定本请求定向到内核的哪个子系统,第二个及其后元素逐次系话指定该子系统的某个部分。
下面展示了这样的分成排列。
要取一个值,oldp需指向一个缓冲区,以让内核存放该值。oldlenp是一个值-结果参数:调用函数时oldlenp指向的值是缓冲区的大小,返回的值是内核在缓冲区中返回的数据量,如果缓冲区不够大,就返回ENOMEM错误。作为一个特例,oldp可以是一个空指针而oldlenp是一个非空指针,内核确定这个调用本应返回的数据量,并通过oldlenp返回这个值。
要设置一个新值,newp需指向一个大小为newlen的缓冲区,如果没有指定新值,newp应为一个空指针,newlen应为0。
sysctl手册详细叙述了可以使用该函数获取的各种系统信息,如:文件系统、虚拟内存、内核限制、硬件各方面信息。我们感兴趣的是网络子系统,通过吧name数组的第一个元素设置为CTL_NET(CTL_XXX常值在,<sys/sysctl.h>头文件中定义)来指定,第二个元素可以是以下几种:
name数组第二个元素为AF_ROUTE时,第三个元素(协议号)总是0(因为AF_ROUTE族不像譬如说AF_INET族那样其中有协议),第四个元素是一个地址族,第五、六级指定做什么。
判断UDP校检和是否开启
#include "unproute.h" #include <netinet/udp.h> #include <netinet/ip_var.h> #include <netinet/udp_var.h> /* for UDPCTL_xxx constants */ int main(int argc, char **argv) { int mib[4], val; size_t len; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_UDP; mib[3] = UDPCTL_CHECKSUM; len = sizeof(val); Sysctl(mib, 4, &val, &len, NULL, 0); printf("udp checksum flag: %d\n", val); exit(0); }
下面四个函数用于需要描述一个解耦的场合,这里存在一个概念,即每个接口都有一个唯一的名字和一个唯一的正值索引(0从不用做索引)
#include <net/if.h> unsigned int if_nametoindex(const char * ifname); // 返回:成功时为正的接口索引,出错时为0 char * if_indextoname(unsigned int ifindex, char * ifname); // 返回: 成功时为指向接口名的指针,出错时为NULL struct if_nameindex * if_nameindex(void); //返回: 成功时为非空指针,出错时为NULL void if_freenameindex(struct if_nameindex * ptr);
if_nametoindex返回名为ifname的接口的索引,if_indextoname对给定的ifindex返回一个指向其接口名的指针,ifname参数指向一个大小为IFNAMSIZ头文件中定义的缓冲区,调用者必须分配这个缓冲区以保存结果,成功时这个指针也是函数的返回值,if_nameindex返回一个指向if_nameindex结构的数组的指针。
struct if_nameindex { unsigned int if_index;//1,2,3... char *if_name;//null terminated name:"le0"... };
标签:选项 pid view sysctl函数 next 简单 call sdl 整数
原文地址:https://www.cnblogs.com/tianzeng/p/12785319.html