進入第一個鉤子NF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, dev, NULL,ip6_rcv_finish)後,ip6_rcv_finish()將調用下列3個函數之一:ip6_input()、ip6_mc_input()、ip6_forward()。
當IPv6包必須要轉寄的時候,ip6_rcv_finish()函數調用ipv6_forward(),這裡ipv6_forward()函數是通過struct inet6_protocol鉤子結構鉤到的,在(1)中提到在核心代碼中查到的為input函數指標賦值的地方在~net/ipv6/route.c中:
struct rt6_info *addrconf_dst_alloc()函數 rt->u.dst.input = ip6_input;
int ip6_route_add()函數 if (addr_type & IPV6_ADDR_MULTICAST)
rt->u.dst.input = ip6_mc_input;
else
rt->u.dst.input = ip6_forward;
那麼ip6_route_add()函數又是何時被執行的呢?
說明一下到原生路由表項的初始化過程。到原生路由表的初始化是在給網卡分配ipv6地址的時候初始化的,代碼在addrconf.c中。比如當使用者在給網卡手動賦ipv6地址的時候,會通過netlink介面,傳遞到核心以後,就由rtnetlink_rcv_msg()來處理。Rtnetlink_recv_msg()會根據family的值,在rtnetlink_links[family]表中進行尋找,找到對應協議簇的處理表。對於ipv6而言是PF_INET6協議簇,調用的是inet6_rtnetlink_table[]。在inet6_rtnetlink_table[]表中,對應添加網卡ipv6地址的處理函數是inet6_rtm_newaddr()函數,因此整個處理過程是inet6_rtm_newaddr()-->ipv6_add_addr() --> addrconf_dst_alloc()-->rt->u.dst.input = ip6_input(),從而轉入ip6_input()函數的處理。
轉寄路由表項的初始化和到原生路由表的初始化過程類似。不同的是從netlink到ip6_route_add(),由於是轉寄故rt->u.dst.input = ip6_forward。需要注意的是,在ip6_forward中,將會進行最後一次的安全檢查,對需要經過隧道處理的包,要在包頭再次添加IPv6頭之後才會交到資料連結層。
ip6_forward()函數通過鉤子NF_HOOK(PF_INET6, NF_INET_FORWARD, skb, skb->dev, dst->dev,ip6_forward_finish),調用到ip6_forward_finish()函數,其定義:
static inline int ip6_forward_finish(struct sk_buff *skb)
{
return dst_output(skb);
}
static inline int dst_output(struct sk_buff *skb)
{
return skb->dst->output(skb);
}
這裡的output()函數又由~net/ipv6/route.c中的函數鉤到ip6_output()函數,然後ipv6包傳出本機
參考:
http://blog.csdn.net/uestc_huan/archive/2009/01/08/3735884.aspx
《linux網路體繫結構》