本地路由表項由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指針鏈表的最后一項)。