由ip_finish_output2处理.
|
/*
*ip_finish_output2:把做为参数传进来的skb传到链路层.此时的skb没有L2 header,但是需要的路由信息已经知道.
* 如果可以找到dst_entry对应的hh_cache.则把路由需要的L2头加到skb中.然后调用相关函数hh->hh_output.
* 如果找不到对应的hh_cache,则需要通过其nerghbour找到下一跳地址,交由相应的处理函数dst->neighbour->output.
*/
static
inline
int
ip_finish_output2(
struct
sk_buff *
skb)
{
/*dst_entry可以理解为路由表的缓冲区,每次主机发送数据时询问路由表后,都会将记录记在一个cache内.*/
/*dst中有能指向其neighbour的指针,通过neighbour可以找到下一跳地址*/
struct
dst_entry *
dst =
skb-
>
dst;
/*hh_cache中存储的是链路头的一些相关信息,可以加快数据包的传输(因为有些情况下不用查看路由表,直接到此缓冲区查看).*/
struct
hh_cache *
hh =
dst-
>
hh;
#
ifdef
CONFIG_NETFILTER_DEBUG
nf_debug_ip_finish_output2(
skb)
;
#
endif
/*CONFIG_NETFILTER_DEBUG*/
if
(
hh)
{
read_lock_bh(
&
hh-
>
hh_lock)
;
memcpy
(
skb-
>
data -
16,
hh-
>
hh_data,
16)
;
read_unlock_bh(
&
hh-
>
hh_lock)
;
skb_push(
skb,
hh-
>
hh_len)
;
return
hh-
>
hh_output(
skb)
;
}
else
if
(
dst-
>
neighbour)
return
dst-
>
neighbour-
>
output(
skb)
;
if
(
net_ratelimit(
)
)
printk(
KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!/n"
)
;
kfree_skb(
skb)
;
return
-
EINVAL;
}
|
要想看明白此函数,需要先知道dst_entry,neighbour,和hh_cache之间的关系.如下图:
500)this.width=500;" width="500" border="0">
det_entry为路由表的cache,也就是说每次发送数据包询问完路由表后,都会将结果保存在这个cache中,
以便在以后的发送中相同的操作可以快速执行而不必每次都询问路由表.由于数据包传送到ip_finish_output2后,还没有L2头,所以需要加上
相应的链路层头.知道了这三者的关系,再由上面的代码可知,如果dst->hh非空,说明以前传输过相似的数据包,可以直接到相应的
hh_cache中去找对应的L2头,然后加到skb中,调用hh->hh_output将数据包发送出去.
如果没有相应的hh指针,则说明去往的目的地以前没有去过,这时候dst_cache中没有相关记录信息,不能根据以往的经验快速发送.这时候就要将数据包传送到Neighbouring Subsystem去查找相关信息.
由于Neighbouring
Subsystem也是一个很大的子系统,代码量也很多,因为也不做为此篇笔记的研究重点,在此只知道个大概过程即可.在以后研究
Neighbouring
Subsystem源代码的时候,再把相关总结贴上来.在此,我们只需要知道,dst->neighbour->output会通过arp来
解决ip地址和mac地址对应的问题,然后经过一系列处理,最后会调用dev_queue_xmit.dev_queue_xmit之后的事情我们已经知
道了.ok,这条道路已经打通了.
如果dst->hh非空,即以前发送过目的地址为此skb目的地址的情况,则直接到cache中去查找相关L2头信息,加到skb上,然后调用hh->hh_output发送出去,此函数初始化为dev_queue_xmit.这样,这条道路也通了.