最近研究了通过sysctl方法获取路由表的UNIX源码(FreeBSD http://www.oschina.net/code/explore/freebsd/sbin/route/route.c),收获不少。
其中找出一段错误如下:
代码1:
L1: if (ifm->ifm_type == RTM_IFINFO) {
L2: get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
L3: printf(" %-15s", sock_ntop(sa, sa->sa_len));
这是用来打印路由对应的Interface ,但是却打印出这个结果:
代码2:
Routing table:
--------------
Destination Gateway Flags Netif Use
default 27.17.172.1 UGSc ppp07.172.1 0
default/0 192.168.1.1 UGSc en1.168.1.1 0
27.17.172.1 27.17.175.111 UH ppp07.175.111 0
本来NetIf部分是由代码1输出的,但现在看起来却像一个结构体被错误的使用带来的后果。
在代码1的L3设置断点,得到sa的数据:
sa_data char [14][14]
0 char 8 '\b'
1 char 0 '\000'
2 char 23 '\027'
3 char 4 '\004'
4 char 0 '\000'
5 char 0 '\000'
6 char 112 'p'
7 char 112 'p'
8 char 112 'p'
9 char 48 '0'
10 char 0 '\000'
11 char 0 '\000'
12 char 0 '\000'
13 char 0 '\000'
可以看到,我们需要的数据是6、7、8、9,也就是对应ppp0,10之后都是'\000' 字符串的结束,说明Interface信息在这里是正常的,经过sock_ntop处理后返回的值出错了。
现在来看,sock_ntop做了一个选择器的工作,就是把socket数据进行识别,就是判断struct sock_addr *的sa_family,然后返回对应family的值。
sa_family对应有 AF_LINK AF_INET 等等。我们这里获取的是接口数据,所以当然是这部分的代码:
代码3:
case AF_LINK: {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_len > 0) {
L1: bcopy(&sdl->sdl_data[0], str, sdl->sdl_nlen);
L2: str[sdl->sdl_len] = '\0';
} else
snprintf(str, sizeof(str), "link#%d", sdl->sdl_index);
return (str);
}
我们要观察的是str这块buffer,因为最终我们得到的函数返回是str
断点设在代码3的L1,此时str还保留前一次取ip的数据。bcopy完成了将sdl中长度为 sdl->nlen 的数据拷贝到 str 这个buffer中,然后L2是为这儿buffer加上字符串结束标记。
显然这里出现了问题:我们拷贝了sdl->nlen长度的数据,却在sdl_len处结束字符串,所以这个应该是接口数据多出来的原因。
到文件 net/if_dl.h 中查看 struct sockaddr_dl * :
/*
* Structure of a Link-Level sockaddr:
*/
struct sockaddr_dl {
u_char sdl_len; /* Total length of sockaddr */
u_char sdl_family; /* AF_LINK */
u_short sdl_index; /* if != 0, system given index for interface */
u_char sdl_type; /* interface type */
u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */
u_char sdl_alen; /* link level address length */
u_char sdl_slen; /* link layer selector length */
char sdl_data[12]; /* minimum work area, can be larger;
contains both if name and ll address */
#ifndef __APPLE__
/* For TokenRing */
u_short sdl_rcf; /* source routing control */
u_short sdl_route[16]; /* source routing information */
#endif
};
我们需要的数据长度显然是sdl_nlen , sdl_len 是整块sockaddr的长度,这样标记肯定会继续输出str中未被socket数据覆盖的部分,也就是之前使用过的ip。
回头看看代码2,也就是输出的部分路由表,可以发现每一行Netif的后半段和Gateway的数据是相同的,这也证明了这一点:str使用完之后没有被清空。这是不太好的做法,要么就在正确位置加上字符串结束符‘\0’,要么就每次使用完str就清空一次 —— 呃,以UNIX源码的风格,我想大部分会是前一种 —— 但是我也不知道为何这些程序员都能保持头脑清醒。
===================================
本文来自:http://blog.csdn.net/zh405123507
tags:UNIX Routing Debug Socket