路由套接字

时隔将近两年,又回来更了

起因:公司代码要由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数组中第一个元素:指定本请求定向到内核的哪个子系统,第二个和其后的逐次细化指定该子系统的某个部分。如图

        路由套接字_第1张图片



            关于name数组的一个汇总,        

            路由套接字_第2张图片

         高亮部分是我们的程序所使用的部分。当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个可以由进程发送。消息定义在 头文件。消息如图所示:

    路由套接字_第3张图片

        因为程序只用到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个选择中的哪个。

路由套接字_第4张图片

        对于缓冲区,我们的程序中是char *类型,还有一种应用是构造一个缓冲区:以rt_msghdr结构开头,后跟套接字结构。

路由套接字_第5张图片


    一个简单通过读写路由套接字获取到结构的方法

#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;}} 路由套接字_第6张图片


    

你可能感兴趣的:(网络编程)