路由表项
本地路由表项由rt_base指针管理,记录本机路由表,当一个IP包需要传输到本子网以外的网络时,就需要通过网关使用路由器来转发,那怎么找到网关呢,就查找由rt_base指针所管理的路由表。
在linux下,我们会用 route add default gw 192.168.1.1命令来设置默认网关,把默认网关设置到路由表中,使用route命令可以查看本机设置的路由表。
如下是一个设置网关的函数
static int set_gateway(void)
{
static int sock_fd = -1;
struct rtentry rt;
U32 dstaddr, gwaddr;
dstaddr = inet_addr("0.0.0.0");
gwaddr = inet_addr("192.168.1.1");
/* Get an internet socket for doing socket ioctls. */
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&rt, 0, sizeof(rt));
/*set Destination addr*/
SET_SA_FAMILY (rt.rt_dst, AF_INET);
SIN_ADDR(rt.rt_dst) = dstaddr;
/*set gw addr*/
SET_SA_FAMILY (rt.rt_gateway, AF_INET);
SIN_ADDR(rt.rt_gateway) = gwaddr;
/*set genmask addr*/
SET_SA_FAMILY (rt.rt_genmask, AF_INET);
SIN_ADDR(rt.rt_genmask) = 0L;
rt.rt_dev = "eth0";
rt.rt_flags = RTF_GATEWAY;
if(ioctl(sock_fd, SIOCADDRT, &rt) < 0)
{
close(sock_fd);
KK_ERROR("[set_gateway]ioctl failed:line:%d\n",__LINE__);
return FAIL;
}
return OK;
}
函数最终会调用ioctl函数来完成。对于路由来说,完成ioctl的函数就是
int ip_rt_ioctl(unsigned int cmd, void *arg),这个函数会根据传入的cmd是SIOCADDRT,还是SIOCDELRT,来决定是创建一个路由表项,还是删除一个路由表项。
对于创建路由表项,系统着会再调用rt_new来继续工作
rt_new函数会对我们传入的参数进行判断,看是否符合创建路由表项的条件。
首先,函数先从传入的rt.rt_dev来判断要创建路由表项的设备是否存在,如果不存在则退出,因为创建一个路由表项,其实就是要对路由表项结构体的各个成员进行赋值的,其结构体如下
struct rtable
{
struct rtable *rt_next;/*指向下一个rtable表项 */
unsigned long rt_dst;/*目的IP地址*/
unsigned long rt_mask;/*子网掩码*/
unsigned long rt_gateway;/*网关地址 */
unsigned char rt_flags;/*标志位*/
unsigned char rt_metric;/*度量值(代价值*/
short rt_refcnt;/*使用计数*/
unsigned long rt_use;/*被使用标志 */
unsigned short rt_mss;/*MSS值*/
unsigned long rt_window;/*窗口大小 */
struct device *rt_dev;/*与该路由项绑定的接口*/
};
其中关键的字段包括rt_dst,rt_mask,rt_gateway,rt_flags,rt_dev。
rt_dev即是跟路由器相连的本地网卡了,我们要发送IP包,如果目的IP不是在本地子网内,就需要把数据通过路由器转发出去了,如果找不到本地接口设备,则表明要发送数据的媒介都没有了,那如何发送,因此,也不需要创建路由表项了。
接着对从用户端传入的参数进行整理,主要是对rt_flags,rt_dst,rt_mask,rt_gateway这几个参数进行整理,接着就调用ip_rt_add函数,创建一个路由表项,填充各成员,并把表项插入到由rt_base管理的指针。
当IP协议要发送一个数据包时,会判断是否找到了目标地址所对应MAC,如果没有找到,则会根据数据包的localroute属性,来判断数据包是要发往本地子网的,还是发往外网的,从而决定是调用ip_rt_local函数,还是调用ip_rt_route,要发往外网,则需要把数据发送到网关,由网关转发,此时就要用到路由器了。由于我们设置的默认网关的目的地址是0.0.0.0,因此,任何一个往外网发送的数据包,经过路由表项目的地址跟目的地址异或后,再跟本地子网相与,都得到0值,因此,发往外网的数据包,如果没有找到其他路由时,最终都会从默认网关发出,ip_rt_local函数返回的设备是本地设备,但是其中包含了网关的IP地址(默认网关是被排列在rt_base指针链表的最后一项)。