• 路由套接字


    一、概述

    AF_ROUTE域对访问内核中的路由子系统的接口做了清理。在路由域中支持的唯一一种套接字是原始套接字。路由套接字支持三种类型的操作。

    1. 进程通过写到路由套接字向内核发送消息(路径的增加和删除采用这种方式实现)。
    2. 进程通过读入路由套接字接收来自内核的消息(内核采用这种操作通知进程已收到并处理一个ICMP重定向消息,或请求外部路由进程解析一个路径)。
    3. 进程调用 sysctl 函数获取路由表或列出所有已配置的接口。

    前两种操作可以复合使用。内核可以通过写一个路由套接字往内核发送一个消息,请求内核关于某个特定路径的所有消息,又可以通过路由套接字接受内核的应答。

    二、数据链路套接字地址结构

    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  */
        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。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    三、读和写

    创建路由套接字后,与其他套接字一样,可以调用 read() 或 write() 函数进行读写,但是由于系统内核是根据应用程序写入的消息来完成对路由信息的提供和修改的,因此,与其他套接字不同,写入和读出的数据都是有固定的格式。消息定义在中。
    在这里插入图片描述

    在这里插入图片描述

    路由套接字交换的结构有5个类型:rt_msghdr、if_msghdr、ifa_msghdr、ifma_msghdr和if_announcemsghdr。每个结构有相同的前3个成员:本消息的长度、版本和类型。类型成员是上图第一列中的常值之一。

    //
    struct rt_msghdr {
    	u_short	rtm_msglen;	
    	u_char	rtm_version;	
    	u_char	rtm_type;	
    	u_long	rtm_index;	
    	int		rtm_flags;	
    	int		rtm_addrs;	
    	pid_t	rtm_pid;	
    	int		rtm_seq;	
    	int		rtm_errno;	
    	int		rtm_use;	
    	u_long	rtm_inits;
    	u_long  rtm_table;     
    	struct	rt_metrics rtm_rmx; 
    };
    
    //
    struct if_msghdr{
    	u_short	ifm_msglen;	
    	u_char	ifm_version;	
    	u_char	ifm_type;
    	int		ifm_addrs;
    	int		ifm_flags;
    	u_short ifm_index;
    	struct if_data ifm_data;
    };
    struct ifa_msghdr{
    	u_short	ifam_msglen;	
    	u_char	ifam_version;	
    	u_char	ifam_type;
    	int		ifam_addrs;
    	int		ifam_flags;
    	u_short ifam_index;
    	int 	ifam_metric;
    };
    
    struct ifma_msghdr{
    	u_short	ifam_msglen;	
    	u_char	ifmam_version;	
    	u_char	ifmam_type;
    	int		ifmam_addrs;
    	int		ifmam_flags;
    	u_short ifmam_index;
    
    };
    struct if_announcemsghdr{
    	u_short	ifan_msglen;	
    	u_char	ifan_version;	
    	u_char	ifan_type;
    	u_short ifan_index;
    	char ifan_name[IFNAMSIZ];
    	u_short ifan_what;
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    rtm_addrs、ifm_addrs、ifam_addrs这三个成员是数位掩码,指明本消息后跟的套接字地址结构是8个可能选择中的哪几个。在头文件定义。存在多个套接字地址结构时,按表中所示的顺序排列。
    在这里插入图片描述

    1.获取并输出一个路由表

    1. 首先命令行取代IPv4点分十进制数地址。
    2. 将地址向内核发送RTM_GET消息。
    3. 内核在IPv4路由表查询地址,将RTM_GET消息返回相应路由表项的信息。

    采用默认路径:
    在这里插入图片描述
    指定主机第二个以太网接口所在子网为目的的地址:

    目的地址为网络本身,网关是外出接口,作为sockaddr_dl结构返回,结构索引为2.
    在这里插入图片描述

    如下图所示写到路由套接字的信息和由内核返回的信息。

    在这里插入图片描述

    
    #include "unproute.h"
    
    #define BUFLEN (sizeof(struct rt_msghdr) + 512)
    #define  SEQ 9999
    
    
    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");
    //创建套接字
    sockfd = socket(AF_ROUTE, SOCK_RAW, 0);
    //填写rt_msghdr结构
    buf = calloc(1, BUFLEN); //分配缓冲区初始化为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指向rt_msghdr结构,sa指向接在其后的第一个套接字地址结构
    rtm = (struct rt_msghdr*)buf;
    sa = (struct sockaddr*)(rtm + 1);
    
    //一掩码和指向第一个套接字地址结构的指针为参数(sa),在rti_info数组中填入指向相应套接字地址结构的指针
    get_rtaddr(rtm->rtm_addrs, sa, rti_info);
    
    //若存在则逐个显示4个肯能的地址,sock_ntop_host:显示目的地址和网关地址;sock_masktop:显示掩码
    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("dest:%s\n", sock_ntop_host(sa, sa->sa_len));
    
    if ((sa = rti_info[RTAX_NETMASK]) != NULL)
    printf("genmask:%s\n", sock_masktop(sa, sa->sa_len));
    
    
    if ((sa = rti_info[RTAX_GENMASK]) != NULL)
    printf("netmask:%s\n", sock_masktop(sa, sa->sa_len));
    
    
    exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    在这里插入图片描述

    #include	"unproute.h"
    
    
    #define NEXT_SA(ap)	ap = (struct sockaddr *) \
    	((caddr_t) ap + (ap->sa_len ? ROUNDUP(ap->sa_len, sizeof (u_long)) : \
    									sizeof(u_long)))
    
    
    void get_rtaddrs(int addrs,struct sockaddr *sa, struct sockaddr **rti_info)
    {
    	int		i;
    //遍历8个可能的指针(RTA_xxx)数位的掩码长值中的每一个
    	for (i = 0; i < RTAX_MAX; i++) {
    		if (addrs & (1 << i)) {
    			rti_info[i] = sa;
    			NEXT_SA(sa);
    		} else
    		//rtm_addrs、ifm_addrs、ifam_addrs成员都用于返回数位掩码。
    		//如果某位被置,rti_info数组中对应的元素就被设置为指向相应套接字地址结构的指针,否则数组中对应的元素被设置为空指针。
    			rti_info[i] = NULL;
    	}
    }
    
    
    
    //查询IPv4地址
    int inet_pton(af, src, dst)
    	int af;
    	const char *src;
    	void *dst;
    {
    	switch (af) {
    	case AF_INET:
    		return (inet_pton4(src, dst));
    	case AF_INET6:
    		return (inet_pton6(src, dst));
    	default:
    		errno = EAFNOSUPPORT;
    		return (-1);
    	}
    
    }
    
    
    
    
    char * sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
    {
        static char str[128];		/* Unix domain is largest */
    
    	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);
    		return(str);
    	}
    
    #ifdef	IPV6
    	case AF_INET6: {
    		struct sockaddr_in6	*sin6 = (struct sockaddr_in6 *) sa;
    
    		if (inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str)) == NULL)
    			return(NULL);
    		return(str);
    	}
    #endif
    
    #ifdef	AF_UNIX
    	case AF_UNIX: {
    		struct sockaddr_un	*unp = (struct sockaddr_un *) sa;
    
    			/* OK to have no pathname bound to the socket: happens on
    			   every connect() unless client calls bind() first. */
    		if (unp->sun_path[0] == 0)
    			strcpy(str, "(no pathname bound)");
    		else
    			snprintf(str, sizeof(str), "%s", unp->sun_path);
    		return(str);
    	}
    #endif
    
    #ifdef	HAVE_SOCKADDR_DL_STRUCT
    	case AF_LINK: {
    		struct sockaddr_dl	*sdl = (struct sockaddr_dl *) sa;
    
    		if (sdl->sdl_nlen > 0)
    			snprintf(str, sizeof(str), "%*s",
    					 sdl->sdl_nlen, &sdl->sdl_data[0]);
    		else
    			snprintf(str, sizeof(str), "AF_LINK, index=%d", sdl->sdl_index);
    		return(str);
    	}
    #endif
    	default:
    		snprintf(str, sizeof(str), "sock_ntop_host: unknown AF_xxx: %d, len %d",
    				 sa->sa_family, salen);
    		return(str);
    	}
        return (NULL);
    }
    
    
    //返回通过路由套接字返回的那两种掩码的表达字符串。掩码存放在套接字地址结构中。
    //掩码的套接字地址结构其sa_family成员没有定义,32为IPv4掩码其sa_len成员可能为:0、5、6、7、8
    //长度大于0,真正的掩码离起点的偏移和IPv4地址在sockaddr_in结构中离开头的偏移一样,都是4字节。也就是通用套接字地址结构s_data[2]成员。
    const char * sock_masktop(struct sockaddr *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)//存放32位掩码的第一个字节
    		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)//掩码的所有4个字节都被保存
    		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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

    四、sysctl操作

    检查路由和接口列表。创建路由套接字(AF_ROUTE域的原始套接字)需要超级用户权限,然而使用sysctl检查路由表和接口列表的进程却不限用户权限。

    #include
    #include
    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。
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    分层排列:
    在这里插入图片描述

    函数获取各种系统信息(文件系统、虚拟内存、内核限制、硬件等),网络子系统通过把name数组第一个元素设置为CTL_NET(sys/sysctl.h)指定:第二个元素以下几种:

    1. AF_INET: 获取或者设置影响网际协议的变量。下一级为使用某个IPPROTO_XXX常值指定的具体协议。
    2. AF_LINK: 获取或设置链路层信息,例如:PPP接口的数目。
    3. AF_ROUTE: 返回路由表或接口清单的信息。
    4. AF_UNSPEC: 获取或设置一些套接口层变量,例如套接口发送或接收缓冲区的最大大小。

    nam数组第二个元素为AF_ROUTE时,第三个元素(协议号)总是0(因为AF_ROUTE族不像AF_INET族那样其中有协议),第四个为地址族,第五和第六指定做什么。

    在这里插入图片描述

    路由域支持3种操作,由name[4]指定。(NET_RT_xxx常值在头文件中定义。)这3种操作返回的信息通过sysctl调用中的oldp指针返回。oldp指向的缓冲区中含有可变数目的RTM_xxx消息。

    1. NET_RT_DUMP返回由name[3]指定的地址族的路由表。如果所指定的地址族为 0,那么返回所有地址族的路由表 。

    2. NET_RT_FLAGS返回由name [3]指定的地址族的路由表,但是仅限于那些所带标志(若于个RTF_xxx常值的逻辑或)与由name[5]指定的标志相匹配的路由表项 。 路由表中所有ARP高速缓存表项均设置了RTF_LLINFO标志位。

    3. NET_RT_IFLIST返回所有已配置接口的信息。如果name[5]不为0, 它就是某个接口的索引号,于是仅仅返回该接口的信息。已赋予每个接口的所有地址也同时返回。不过如果name[3]不为0,name仅局限于指定地址族的地

    4. 每个接口的返回信息包括 一 个RTM_IFINFO消息和后跟的零个或多个RTM_NEWADDR消息,其中每个RTM_NEWADDR消息对应已赋予该接口的 一 个地址。

    5. 接在RTM _IFINFO 消息首部之后的是一个数据链路套接字地址结构,接在每个RTM_NEWADDR消息首部之后的则是最多3个套接字地址结构:接口地址、网络掩码和广播地址 。

    在这里插入图片描述

    1.判断UDP校验和是否开启

    sysctl的简单例子:对于网际网协议检查UDP校验和是否开启。有些UDP应用程序(如BIND)在启动时检查UDP校验和是否已经开启,若没有则尝试开启。开启诸如此类的特性需要超级用户权限。

    #include    "unproute.h"
    #include    
    #include    
    #include       //获得sysctl的UDP常值定义
    
    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
    ", val);
    
        exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    五、get_ifi_info函数

    NET_RT_IFLIST命令调用sysctl返回指定地址族的接口列表:

    #include	"unproute.h"
    
    char * net_rt_iflist(int family, int flags, size_t *lenp)
    {
    	int		mib[6];
    	char	*buf;
    //数组mib初始化成用于返回接口列表以及每个接口已配置的指定地址族的地址
    
    	mib[0] = CTL_NET;
    	mib[1] = AF_ROUTE;
    	mib[2] = 0;
    	mib[3] = family;		
    	mib[4] = NET_RT_IFLIST;
    	mib[5] = flags;	
    //第三个参数空指针,从而lenp指向的变量中存放所有接口信息所需缓冲区的大小
    	if (sysctl(mib, 6, NULL, lenp, NULL, 0) < 0)
    		return(NULL);
    //动态分配缓冲区
    	if ( (buf = malloc(*lenp)) == NULL)
    		return(NULL);
    //第三个参数指向新分配缓冲区的一个指针。lenp指向变量将存放在缓冲区中等待信息量。变量是调用者分配。返回值返回给调用者。
    	if (sysctl(mib, 6, buf, lenp, NULL, 0) < 0) {
    		free(buf);
    		return(NULL);
    	}
    
    	return(buf);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    get_ifi_info函数:

    #include	"unpifi.h"
    #include	"unproute.h"
    
    struct ifi_info * get_ifi_info(int family, int doaliases)
    {
    	int 				flags;
    	char				*buf, *next, *lim;
    	size_t				len;
    	struct if_msghdr	*ifm;
    	struct ifa_msghdr	*ifam;
    	struct sockaddr		*sa, *rti_info[RTAX_MAX];
    	struct sockaddr_dl	*sdl;
    	struct ifi_info		*ifi, *ifisave, *ifihead, **ifipnext;
    
    	buf = net_rt_iflist(family, 0, &len);
    
    	ifihead = NULL;
    	ifipnext = &ifihead;
    
    	lim = buf + len;
    //循环遍查sysctl返回并填写到缓冲区中的每个路由消息。
    	for (next = buf; next < lim; next += ifm->ifm_msglen) {
    		ifm = (struct if_msghdr *) next;
    //检查接口是否在工作
    		if (ifm->ifm_type == RTM_IFINFO) {
    			if ( ((flags = ifm->ifm_flags) & IFF_UP) == 0)
    				continue;	/* ignore if interface not up */
    //判断存在哪些套接字地址结构
    //sa指向if_msghdr结构之后的第一个套接字地址结构。get_rtaddrs函数根据出现哪些套接字地址结构初始化rti_info数组
    			sa = (struct sockaddr *) (ifm + 1);
    			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
    //处理接口名字,如果出现携带接口名字套接字地址结构,就动态分配一个ifi_info结构并存放接口标志。预期地址族:AF_LINK,表是数据链路套接字。
    //接口索引存放在ifi_index成员,sdl_nlen不为:0,接口复制到ifi_info结构;
    //否则把接口索引字符串作为接口名字存放。sdl_alen不为:0,把硬件地址复制到ifi_info结构,长度ifihlen中返回。
    			if ( (sa = rti_info[RTAX_IFP]) != NULL) {
    				ifi = calloc(1, sizeof(struct ifi_info));
    				*ifipnext = ifi;			/* prev points to this new one */
    				ifipnext = &ifi->ifi_next;	/* ptr to next one goes here */
    
    				ifi->ifi_flags = flags;
    
    				if (sa->sa_family == AF_LINK) {
    					sdl = (struct sockaddr_dl *) sa;
    					ifi->ifi_index = sdl->sdl_index;
    					if (sdl->sdl_nlen > 0)
    						snprintf(ifi->ifi_name, IFI_NAME, "%*s",
    								 sdl->sdl_nlen, &sdl->sdl_data[0]);
    					else
    						snprintf(ifi->ifi_name, IFI_NAME, "index %d",
    								 sdl->sdl_index);
    
    					if ( (ifi->ifi_hlen = sdl->sdl_alen) > 0)
    						memcpy(ifi->ifi_haddr, LLADDR(sdl),
    							   min(IFI_HADDR, sdl->sdl_alen));
    				}
    			}
    		//返回当前接口的IP地址
    
    		} else if (ifm->ifm_type == RTM_NEWADDR) {
    			if (ifi->ifi_addr) {	/* already have an IP addr for i/f */
    				if (doaliases == 0)
    					continue;
    
    					/* 4we have a new IP addr for existing interface */
    				ifisave = ifi;
    				ifi =calloc(1, sizeof(struct ifi_info));
    				*ifipnext = ifi;			/* prev points to this new one */
    				ifipnext = &ifi->ifi_next;	/* ptr to next one goes here */
    				ifi->ifi_flags = ifisave->ifi_flags;
    				ifi->ifi_index = ifisave->ifi_index;
    				ifi->ifi_hlen = ifisave->ifi_hlen;
    				memcpy(ifi->ifi_name, ifisave->ifi_name, IFI_NAME);
    				memcpy(ifi->ifi_haddr, ifisave->ifi_haddr, IFI_HADDR);
    			}
    
    			ifam = (struct ifa_msghdr *) next;
    			sa = (struct sockaddr *) (ifam + 1);
    			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
    
    			if ( (sa = rti_info[RTAX_IFA]) != NULL) {
    				ifi->ifi_addr = calloc(1, sa->sa_len);
    				memcpy(ifi->ifi_addr, sa, sa->sa_len);
    			}
    //返回广播地址和目的地址
    			if ((flags & IFF_BROADCAST) &&
    				(sa = rti_info[RTAX_BRD]) != NULL) {
    				ifi->ifi_brdaddr = calloc(1, sa->sa_len);
    				memcpy(ifi->ifi_brdaddr, sa, sa->sa_len);
    			}
    
    			if ((flags & IFF_POINTOPOINT) &&
    				(sa = rti_info[RTAX_BRD]) != NULL) {
    				ifi->ifi_dstaddr = calloc(1, sa->sa_len);
    				memcpy(ifi->ifi_dstaddr, sa, sa->sa_len);
    			}
    
    		} else
    			err_quit("unexpected message type %d", ifm->ifm_type);
    	}
    	/* "ifihead" points to the first structure in the linked list */
    	return(ifihead);	/* ptr to first structure in linked list */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    六、接口名字和索引函数

    下面四个函数用于需要描述一个接口的场合,这里存在一个概念,即每个接口都有一个唯一的名字和一个唯一的正值索引(0从不用做索引)。

    #include 
    
    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"...
    }; 
    
    //该数组聂后一个元素的if_index成员为O,if_narne成员为空指针。
    //该数组本身以及数组中各个元素指向的名字所用的内存空间由该函数动态获取,然后由if_freenarneindex函数归还给系统。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.四个函数的实现

    #include	"unpifi.h"
    #include	"unproute.h"
    
    unsigned int if_nametoindex(const char *name)
    {
    	unsigned int		idx, namelen;
    	char				*buf, *next, *lim;
    	size_t				len;
    	struct if_msghdr	*ifm;
    	struct sockaddr		*sa, *rti_info[RTAX_MAX];
    	struct sockaddr_dl	*sdl;
    //获取接口列表
    	if ( (buf = net_rt_iflist(0, 0, &len)) == NULL)
    		return(0);
    
    	namelen = strlen(name);
    	lim = buf + len;
    	for (next = buf; next < lim; next += ifm->ifm_msglen) {
    	//只处理RTM_IFINFO信息	
    	ifm = (struct if_msghdr *) next;
    		if (ifm->ifm_type == RTM_IFINFO) {
    			sa = (struct sockaddr *) (ifm + 1);
    			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
    			if ( (sa = rti_info[RTAX_IFP]) != NULL) {
    				if (sa->sa_family == AF_LINK) {
    					sdl = (struct sockaddr_dl *) sa;
    					if (sdl->sdl_nlen == namelen && strncmp(&sdl->sdl_data[0], name, sdl->sdl_nlen) == 0) {
    						idx = sdl->sdl_index;	/* save before free() */
    						free(buf);
    						return(idx);
    					}
    				}
    			}
    
    		}
    	}
    	free(buf);
    	return(0);		/* no match for name */
    }
    
    
    
    
    //比较接口索引和由调用者指定的参数
    char * if_indextoname(unsigned int idx, char *name)
    {
    	char				*buf, *next, *lim;
    	size_t				len;
    	struct if_msghdr	*ifm;
    	struct sockaddr		*sa, *rti_info[RTAX_MAX];
    	struct sockaddr_dl	*sdl;
    //第二个参数为期望索引,结果只含有期望接口信息
    	if ( (buf = net_rt_iflist(0, idx, &len)) == NULL)
    		return(NULL);
    
    	lim = buf + len;
    	for (next = buf; next < lim; next += ifm->ifm_msglen) {
    		ifm = (struct if_msghdr *) next;
    		if (ifm->ifm_type == RTM_IFINFO) {
    			sa = (struct sockaddr *) (ifm + 1);
    			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
    			if ( (sa = rti_info[RTAX_IFP]) != NULL) {
    				if (sa->sa_family == AF_LINK) {
    					sdl = (struct sockaddr_dl *) sa;
    					if (sdl->sdl_index == idx) {
    						int slen = min(IFNAMSIZ - 1, sdl->sdl_nlen);
    						strncpy(name, sdl->sdl_data, slen);
    						name[slen] = 0;	/* null terminate */
    						free(buf);
    						return(name);
    					}
    				}
    			}
    
    		}
    	}
    	free(buf);
    	return(NULL);		/* no match for index */
    }
    
    
    
    //返回一个if_nameindex结构数组其中含有索有接口名字和索引对。
    struct if_nameindex * if_nameindex(void)
    {
    	char				*buf, *next, *lim;
    	size_t				len;
    	struct if_msghdr	*ifm;
    	struct sockaddr		*sa, *rti_info[RTAX_MAX];
    	struct sockaddr_dl	*sdl;
    	struct if_nameindex	*result, *ifptr;
    	char				*namptr;
    //获取接口列表为结果分配空间
    	if ( (buf = net_rt_iflist(0, 0, &len)) == NULL)
    		return(NULL);
    
    	if ( (result = malloc(len)) == NULL)	/* overestimate */
    		return(NULL);
    	ifptr = result;
    	namptr = (char *) result + len;	/* names start at end of buffer */
    
    	lim = buf + len;
    	for (next = buf; next < lim; next += ifm->ifm_msglen) {
    		ifm = (struct if_msghdr *) next;
    		//值处理RTM_IFINFO信息
    		if (ifm->ifm_type == RTM_IFINFO) {
    			sa = (struct sockaddr *) (ifm + 1);
    			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
    			if ( (sa = rti_info[RTAX_IFP]) != NULL) {
    				if (sa->sa_family == AF_LINK) {
    					sdl = (struct sockaddr_dl *) sa;
    					namptr -= sdl->sdl_nlen + 1;
    					strncpy(namptr, &sdl->sdl_data[0], sdl->sdl_nlen);
    					namptr[sdl->sdl_nlen] = 0;	/* null terminate */
    					ifptr->if_name = namptr;
    					ifptr->if_index = sdl->sdl_index;
    					ifptr++;
    				}
    			}
    
    		}
    	}
    	//终止数组
    	ifptr->if_name = NULL;	/* mark end of array of structs */
    	ifptr->if_index = 0;
    	free(buf);
    	return(result);			/* caller must free() this when done */
    }
    
    
    
    
    
    
    //是否if_nameindex结构数组及其所含的名字分配内存空间
    void if_freenameindex(struct if_nameindex *ptr)
    {
    	free(ptr);
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
  • 相关阅读:
    Rust闭包 - Fn/FnMut/FnOnce traits,捕获和传参
    一篇彻底带你理解微服务
    基于bootstrap的二维码支付系统webAPP设计
    Flink 中的时间和窗口(一)
    1723_PolySpace Bug Finder命令行执行探索
    探秘亚马逊云科技海外服务器 | 解析跨境云计算的前沿技术与应用
    java:为什么要总是使用对象的notifyAll而不使用notify?
    基于Simulink与GUI界面相结合的单相全桥整流、三相桥式整流、单相桥式半空整流、单相桥式不可控整流电路的仿真研究
    开学季蓝牙耳机怎么选?高性价比学生蓝牙耳机推荐
    vue3 mouse.js 鼠标的位置
  • 原文地址:https://blog.csdn.net/weixin_50866517/article/details/126801048