路由表项

本地路由表项由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指针链表的最后一项)。

posted on 2012-04-10 17:05  image eye  阅读(8008)  评论(0编辑  收藏  举报