Linux kernel net code reading
断断续续也阅读了 Linux kernel 中网络子系统的一部分代码,主要集中在目录 net 下。主要的工具有 https://elixir.bootlin.com/linux, https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ 和 https://github.com/torvalds/linux。
具体这几个工具的比较可以参考这里。下面是目前主要阅读的一些源文件,并根据自己的阅读经验总结了主要功能,后面可以集中时间专门总结一下网络子系统。
文件: net/core/dev.c
主要函数:
netif_rx 驱动层收包之后,申请skb后交给网络协议栈的入口。支持在硬件中断中处理报文,实际是通过发送给queue调度处理。
netif_receive_skb 驱动层收包之后,申请skb后交给网络协议栈的入口。和netif_rx的区别是,支持在软中断中处理报文,即直接在当前上下文中处理,一般用在NAPI的poll函数中。
napi_gro_receive 驱动层收包之后,通过napi方式触发poll轮询收包,之后使用该接口发送给网络协议栈。
__napi_schedule 在驱动中断处理中,通过该函数触发NAPI的调度,也就是调用驱动的poll轮询回调。
netif_napi_add 驱动程序通过该接口向napi注册poll回调函数。
napi_complete_done 驱动在poll中完成收包后,通过该接口告诉NAPI完成收包。有部分驱动调用的是napi_complete,其实是封装的napi_complete_done
dev_queue_xmit 驱动层发包函数,这是驱动层对上层提供的统一发包入口,比如neighbour邻居子系统通过调用该函数实现发包。这个函数通过dev_hard_start_xmit->xmit_one->netdev_start_xmit->__netdev_start_xmit调用路径,最终调用具体驱动程序注册的ndo_start_xmit回调完成发包。
主要函数:
inet_rtm_newaddr 通过netlink添加IP地址的处理函数。
inet_rtm_deladdr 通过netlink删除IP地址的处理函数。
devinet_ioctl 通过ioctl方式添加或者删除IP地址的处理函数。
主要函数:
ip_rcv 网络协议栈IP层收包入口,具体是在af_inet.c中注册,在dev.c中被调用。注意在被调用的实现中,使用了 INDIRECT_CALL_INET,而且在参数中可以看到可能被回调的函数ip_rcv或ipv6_rcv,这是一种间接调用方式,可以防止一些潜在攻击。 ip_rcv通过调用ip_rcv_core完成ip头有效性校验,再调用ip_rcv_finish进入收包处理流程。在ip_rcv_finish中,通过ip_rcv_finish_core完成路由查找,具体说是通过ip_route_input_noref决定是上送本机还是进行转发,分别设置dst.input为ip_local_deliver或者ip_forward。最后,在ip_rcv中通过dst_input调用进入上面dst.input的事件处理流程。
主要函数:
ip_output 在rt_dst_alloc中会将dst.output初始化为ip_output,后面调用dst_output的地方,实际就是进入ip_output中进行处理。而ip_output最终会调用ip_finish_output。ip_finish_output的调用路径图如下:
ip_finish_output
–>__ip_finish_output
—->dst_output NAT策略处理之后,需要重新再走一遍output流程。
—->ip_finish_output_gso gso处理流程
——>ip_finish_output2
——>ip_fragment
—->ip_fragment 需要分片处理的报文
——>ip_finish_output2
—->ip_finish_output2
可见最后都会进入ip_finish_output2,该函数的调用路径如下:
ip_finish_output2
–>ip_neigh_for_gw route.h
—->ip_neigh_gw4
—->ip_neigh_gw6
–>neigh_output neighbour.h
—->neigh_hh_output
—->n->output
ip_queue_xmit tcp层发包最终调用的接口,该接口内部再调用__ip_queue_xmit,后者内部先通过ip_route_output_ports查找路由,最后通过ip_local_out发包。ip_local_out内部最终调用dst_output发包。
主要函数:
tcp_v4_rcv 在af_inet.c中注册的tcp处理函数,根据搜索结果,在ip_protocol_deliver_rcu中被调用,其实是通过handler指针调用的,同时通过INDIRECT_CALL_2实现间接调用。tcp_v4_rcv做完报文有效性检查后,会调用__inet_lookup_skb查找是否有src+dst对应的socket,最后会同步或异步(tcp_add_backlog)调用tcp_v4_do_rcv。
tcp_v4_do_rcv tcp_v4_do_rcv会根据tcp的不同状态进行处理,最终会调用tcp_rcv_state_process处理报文。
tcp_v4_early_demux 在ip_rcv_finish_core中做路由查找之前,当配置了sysctl_ip_early_demux支持通过tcp_v4_early_demux做tcp的早期解复用处理,及tcp的快速处理。
主要函数:
tcp_rcv_state_process 根据TCP的不同状态处理TCP报文,当非established状态的时候触发tcp状态机,否则使用tcp_data_queue将报文放入队列中,后续由用户态调用的recv/read调用处理。
主要函数:
tcp_write_xmit 发送TCP报文的接口,主要提供给__tcp_push_pending_frames和tcp_push_one使用,后两者会在tcp.c中被调用。
tcp_transmit_skb TCP内部及状态机报文发送接口,主要在tcp_output.c内部使用,tcp_write_xmit也是通过这个接口发包的。最终会调用queue_xmit回调完成报文的发送,具体的接口ipv4和ipv6分别对应ip_queue_xmit和inet6_csk_xmit。
文件: net/ipv4/tcp.c
主要函数:
tcp_sendmsg 用户态send/write/sendmsg/sendto最终调用的发送接口,最终调用tcp_sendmsg_locked。
tcp_sendmsg_locked( 实现tcp报文发送,支持零拷贝方式,支持根据MSS分片处理,通过tcp_write_queue_tail读取sk_write_queue,即tcp的发送队列,当push的时候通过__tcp_push_pending_frames或者tcp_push_one完成发包。通过skb_entail将skb放入sk_write_queue,以便实现异步发包。
tcp_recvmsg 用户态recv/read/recvmsg/recvfrom最终调用的接收接口,通过skb_peek_tail从sk_receive_queue获得数据,即tcp的接收队列。