网络层中主要的发送函数有以下三个:ip_push_pending_frames,ip_queue_xmit,raw_send_hdrinc (ip_output.c/ipv4)
ip_push_pending_frames★
将所有pending状态的IP分组组合成一个IP分组,并发送
ip_local_out
ip_queue_xmit (sk_buff *skb)★
ip_route_output_flow(找路由)
ip_local_out
raw_send_hdrinc★
NF_HOOK(dst_output)
ip_local_out★ (只找得到NF_IP_LOCAL_OUT)
__ip_local_out
nf_hook(dst_output)
dst_output (dst.h)
路由选择
ip_route_output_flow (rtable* , flowi *, sock *,)★ (route.c/ipv4) (P138)
__ip_route_output_key
ip_route_output_slow
flowi结构的介绍在P137
路由选择
ip_route_output_slow★ (route.c/ipv4)
fib_lookup
ip_mkroute_output (创建对应路由cache表项,将fib查找的结果放入路由cache中,P147)
__mkroute_output
rt_hash
rt_intern_hash
arp_bind_neighbour (dst) (绑定对应邻居表项 P151,赋值dst->neighbour,当然,如果已经存在,则不需要创建)
__neigh_lookup_errno
neigh_lookup
neigh_create
发送
dst_output★ (dst.h)
dst->output= ip_output (struct sk_buff *) (dst是skb->dst,路由选择完后,就会填写该项) (P158)
NF_HOOK_COND(ip_finish_output)
ip_finish_output2 (P180)
1.(若skb->dst->hh不等于NULL)neigh_hh_output
hh->hh_output = dev_queue_xmit★
2.(hh等于NULL)dst->neighbour->output= neigh_resolve_output
neigh->ops->queue_xmit = dev_queue_xmit★
(dev.c/core—链路层)
发送报文的主函数。dev_queue_xmit能够通过两个路径,导致驱动发送函数hard_start_xmit的执行:
* 与流量控制的接口(QoS层):这是通过qdisc_run函数完成的。
* 直接调用hard_start_xmit:当设备不使用流量控制机制时。
neigh_resolve_output 是很重要的函数,见linux内核协议源码分析P161.该函数主要干了以下几件事:
1. 子函数neigh_event_send把报文挂在arp报文队列中,必须要注意的是,要发送的 arp 报文并不是这个,而是 arp 模块从此报文中抽取相关信息,然后据此再创建一个 arp 报文。
2. neigh_hh_init 函数初始化hh,并把其赋值给dst->hh
3. 给报文填上2层报文头,并调用neigh->ops->queue_xmit(skb)把报文发送到链路层
该函数还干的一个重要的事情在P172页,将状态置为 NUD_INCOMPLETE,表示正在解析中,
并把待解析的 socket 缓冲 skb 放入 arp_queue 队列中(其实这个就是上面的neigh_event_send)。然后启动邻居的定时器开始 ARP 解析(定时器的启动在P163页: 当 neighnud_state 被设置为 NUD_INCOMPLETE 后,该 neigh 的定时器函数将要采取一些操作了,这个 timer 是在 neigh_alloc 中制定的)。继而在定时器中调用函数neigh->ops->solicit(P172页),然后发送arp_send,接收到对端的回应后,调用neigh_update更新邻居系统
注:针对IPV4协议,这里的skb->dst->output指针指向的函数可能是ip_output(单播报文)或者是ip_mc_output(组播报文)。
注2:如果是IPV6协议,那指针指向的就是ip6_output。
注3:IPV6协议skb->dst->output只会指向ip6_output,ip6_mc_output被包裹在ip6_output中。
接收IPv4包,由netif_rx间接调用
ip_rcv(skb,dev)★ (ip_input.c/ipv4)
NF_HOOK
ip_rcv_finish (skb)
ip_route_input(skb,addr,addr,dev)该函数决定如何处理普通报文(是送往本地、还是转发、丢弃等
(在该函数里面会进行路由选路,即给skb->dst赋值
)
dst_input (skb)
dst->input(skb)(可能是ip_local_deliver或ip_forward)
if(是发给本地的包)
dst->input是ip_local_deliver (skb)
NF_HOOK
ip_local_deliver_finish (skb)
ipprot->handler(可能是tcp_v4_rcv,udp_rcv,icmp_rcv,igmp_rcv) (socket解复用-书籍P54)
else
dst->input是ip_forward
重要:skb->dst的初始化不是在ip_rcv_finish的开端(ip_route_input的开头),就是在ip_options_rcv_srr的尾端。skb->dst->input会设成ip_local_deliver或ip_forward,这取决于包的目的地址。
更新路由
ip_route_input (skb,addr,addr,dev)★
1.在缓存中查找,找到了则skb->dst =(struct dst_entry*)rth;
2.ip_route_input_mc(多播)
rt_hash
rt_intern_hash
3.ip_route_input_slow(其它,查找路由表—详情见IP发送)
ip_mkroute_input
__mkroute_input
rt_hash
rt_intern_hash
每收到一个IP报文都会调用此函数更新路由表。ip_route_input函数的上半部分是在hash table寻找路由项,如果找到就返回。找不到才会调用后面的ip_route_input_mc或ip_route_input_slow来更新路由表。
转发
ip_forward(skb)★
1.从skb->dst拿到下一跳地址
2.ip_call_ra_chain
raw_rcv★
3.ip_forward_finish
dst_output