时隔将近两年,又回来更了
起因:公司代码要由vx改为linux系统下运行,有点无理的改法暂且不论,主要是在编译vlan ip相关代码时发现有函数编不过,遇到的问题也是由于这些函数引起,翻书本才发现自己知识上的欠缺。
1、VX中代码主要流程
系统起了一个任务,当打开vlan proxy后,将上层的arp表所有表项置为inactive,然后从底层的arp高速缓存中读取arp信息,将读到的每一条与上层的arp表项进行比对,有新添加项则加入到arp表中并激活,若有想对应的ip其mac地址进行了修改,则修改arp表中对应项并激活,若找到ip和对应的mac均为改变,则将对应的arp表项进行激活。当遍历完arp高速缓存后,对于上层arp表中未激活的表项进行删除。
代码中编译不过的部分正是从底层的arp高速缓存中读取arp信息进行处理的部分。由于没有相对应的函数解释,所以把UNIX网络编程的路由套接字用到的部分做一下总结。
2、概述
4.3 BSD Reno通过创建AF_ROUTE域对访问内核中路由子系统的接口做了清理。
在路由域中只支持 原始套接字。 在路由套接字上支持3中类型的操作:
- 进程通过写套接字往内核发送消息。路径的增加和删除采用这种方式
- 进程通过读套接字接收内核消息。内核通过这种操作通知进程已收到并处理ICMP重定向消息,或请求外部路由进程解析一个路径
- 进程使用sysctl函数倾泻出路由表或列出所有已配置的接口。 (主要研究对象)
注意:前两种操作需要超级用户权限,最后一种操作是任何进程都可以
3、sysctl 操作
对于路由操作字主要兴趣点是使用sysctl函数检查路由表和接口列表。
#include
#icnlude
int sysctl(int *name, u_int namelen, void *oldp, size_t oldlenp, void* newp, size_t newlen)
sysctl为了获取某值,name是获取哪种值,oldp指向一个供内核存放该值的缓冲区。oldlenp是值-结果参数,当函数被调用时,oldlenp指向的值指定该缓冲区的大小;当函数返回时,该值给出内核存放在该缓冲区中的数量。当这个缓冲区不够大时返回ENOMEM的错误。注意:作为特例,oldp可以是一个空指针而oldlenp是一个非空指针,内核确定这样的调用应该返回的数据量,并通过oldelenp返回这个大小。所看程序中,就是调用了两次sysctl,第一次oldp=NULL,只返回了oldlenp,然后根据oldlenp大小分配了内存,再次调用sysctl获取数据。
sysctl为了设置某个值,newp参数指向一个大小为newlen参数值的缓冲区。如果不指定新值,newp应为NULL,newlen为0。
3.1 name参数指定名字的一个整数数组,namelen是该数组的的元素个数,注意不是长度
name的组成形式类似于MIB中组成形式。name数组中第一个元素:指定本请求定向到内核的哪个子系统,第二个和其后的逐次细化指定该子系统的某个部分。如图
关于name数组的一个汇总,
高亮部分是我们的程序所使用的部分。当name第二个元素为AF_ROUTE时,第三个元素(协议号)为0,因为AF_ROUTE协议族没有协议,第 四个元素是一个协议族,第五和第六个元素是指定要做什么。
3.1.1 name 第一个元素 name[0]
sysctl函数可获取各种系统信息,包括文件系统、虚拟内存、内核限制以及硬件等方面信息。由于是网络子系统,所以把name数组的第一个元素设置为CTL_NET来指定。注意:CTL_XXX需要使用头文件#include
3.1.2 name 第二个元素 name[1]
可取值为:
AF_INET: 获取或设置网际网协议的变量,下一个元素为使用某个IPPROTP_xx常值指定的具体协议
AF_LINK:获取或设置链路层信息
AF_ROUTE:获取路由表或接口列表的信息
AF_UNSPEC:获取或设置一些套接字层变量,譬如套接字发送或接收或接收缓冲区的最大大小
3.1.3 name第五个元素 name[4]
路由域支持的3种操作由此元素指定
- NET_RT_DUMP 返回由name[3]指定的协议族的路由表,当name[3]=0时,返回所有地址的路由表
- NET_RT_FLAGS 返回由name[3]指定的协议族的路由表,但是仅限于哪些所带标志与name[5]指定的标志相匹配的路由表项。路由表中所有的ARP高速缓存表项均设置了RTF_LINFO标志
- NET_RT_IFLIST 返回所有已配置接口的信息。如果name[5]不为0,他就是某个接口的索引号,仅返回该接口的信息。
4、读和写
对于sysctl中读取出来数据的处理,所读程序中由于oldp是char*类型,对于读出的数据轻质转化为了rt_msghdr类型。
创建一个路由套接字后,进程可以通过读写该路由套接字向内核发送和接收消息。路由套接字共有12个路由消息,其中5个可以由进程发送。消息定义在
因为程序只用到rt_msghdr类型,只列出该类型的源码。
struct rt_msghdr {
u_short rtm_msglen; /* to skip over non-understood messages */
u_char rtm_version; /* future binary compatibility */
u_char rtm_type; /* message type */
u_long rtm_index; /* index for associated ifp */
int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
int rtm_addrs; /* bitmask identifying sockaddrs in msg */
pid_t rtm_pid; /* identify sender */
int rtm_seq; /* for sender to identify action */
int rtm_errno; /* why failed */
int rtm_use; /* from rtentry */
u_long rtm_inits; /* which metrics we are initializing */
u_long rtm_table; /* Route table index (IPCOM_ROUTE_TABLE_DEFAULT = default) */
struct rt_metrics rtm_rmx; /* metrics themselves */
};
对于rt_msghdr\ifma_msghdr\ifa_msghdr这三个结构有相同的前3个成员:本消息的长度、版本和类型。
4.1 类型
类型的取值为:上图中第一列中的消息类型。
4.2 长度
长度rtm_msglen,允许应用进程跳过不理解的消息类型
4.3 rtm_addrs
对于rt_msghdr\ifma_msghdr\ifa_msghdr这三个结构中的rtm_addrs\ifm_addrs\ifam_addrs成员是数位掩码,指明本消息后跟的套接字地址是下图中8个选择中的哪个。
对于缓冲区,我们的程序中是char *类型,还有一种应用是构造一个缓冲区:以rt_msghdr结构开头,后跟套接字结构。
一个简单通过读写路由套接字获取到结构的方法
#include "unproute.h"
#define BUFLEN (sizeof(struct rt_msghdr) + 512)
#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)))int main(){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;sockfd = Socket(AF_ROUTE, SOCK_RAW,0);buf = calloc(1,BUFLEN);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 *) (rt + 1);get_rtaddr(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_GETEWAY])!= NULL)printf("netmask:%s\n",sock_ntop_host(sa, sa->sa_len));if((sa = rti_info[RTAX_NETMASK])!= NULL)printf("genmask:%s\n",sock_ntop_host(sa, sa->sa_len));}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);}elserti_info[i] = NULL;}}