天天看点

linux kernel 发送网络数据包处理流程

应用层发送数据包的调用链如下

我们的流程以发送一个UDP包为例,阐述linux kernel数据包发送过程,下图为调用链,至上而下。

函数名称 文件名 做了什么
udp_send_skb udp.c 封装UDP头,调用ip_send_skb发送ip数据包
ip_send_skb ip_output.c
ip_local_out ip_output.c
__ip_local_out ip_output.c
nf_hook ip_output.c

调用关系到了这里就再继续下去就比较难找了,

首先需要理解的是nf_hook钩子函数,

钩子函数各module都可以注册,

如果你自己写一个module,

也可以注册钩子函数,使用结构体nf_hook_ops来定义自己的钩子函数。内核很多netfilter模块都注册了钩子函数,所以每个hook点都会调用一堆钩子函数,如果所有钩子函数均返回成功,才执行okfn函数,这里就是dst_output函数,这里的hook点名称为NF_INET_LOCAL_OUT

dst_output dst.h 这里有点绕,因为dst_output的实现为skb_dst(skb)->output(net, sk, skb);output作为struct dst_entry *的一个函数指针,所以就很难追踪他的下一跳去了哪里,排查了很久,后来我发现位于route.c里的rt_dst_alloc函数有将ip_output注册给rt->dst.output,而struct rtable *的头部也为struct dst_entry *,所以很有可能下一跳就到了ip_output函数里边去了
ip_output ip_output.c hook点名称为NF_INET_POST_ROUTING
ip_finish_output ip_output.c
__ip_finish_output ip_output.c 计算mtu,报文分片
ip_fragment ip_output.c 这里仅针对需要分片的报文进行分片,小于MTU的则不需要分片,需要分片的报文也是通过ip_finish_output2函数发出去,而不需要分片的报文也是从ip_finish_output2函数发出去
ip_finish_output2 ip_output.c 这个函数主要功能是查找路由表,根据目的ip查出下一跳,通过下一跳来创建一个neighbour,neighbour通过arp来获取下一跳的mac地址,之后发送报文
dst_neigh_output dst.h 这个函数去判断nud_state的状态NUD(Neighbour Unreachability Detection),如果状态处于已连接,则调用neigh_hh_output送出。
neigh_hh_output neighbour.h 直接调用dev_queue_xmit将数据发送出去
dev_queue_xmit netdevice.h 调用dev_queue_xmit_sk
dev_queue_xmit_sk dev.c 调用__dev_queue_xmit
__dev_queue_xmit dev.c 主要功能为从发送设备上获取txq发送队列,然后调用__dev_xmit_skb发送
__dev_xmit_skb dev.c 调用sch_direct_xmit进行发送
sch_direct_xmit sch_generic.c 调用dev_hard_start_xmit
dev_hard_start_xmit dev.c 连续调用xmit_one
xmit_one dev.c 调用函数netdev_start_xmit进行发送
netdev_start_xmit netdevice.h __netdev_start_xmit
__netdev_start_xmit netdevice.h 调用函数ndo_start_xmit,至此步骤完成,后续进入驱动函数接口进行处理,ndo_start_xmit是一个函数指针,指向驱动的接口。
  • 参考文章

    Monitoring and Tuning the Linux Networking Stack: Sending Data