应用层发送数据包的调用链如下
我们的流程以发送一个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