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指针链表的最后一项)