我想在 Linux 内核中找到一个 UDP 数据包的路径。为此,我想阅读一些文档(到目前为止我有这个,它是针对 TCP 的),然后在相关的内核函数中有一些 printk 语句来确认这一点。我将通过重新编译内核代码来做到这一点。
这是解决问题的方法吗?你有什么建议/参考吗?
我想在 Linux 内核中找到一个 UDP 数据包的路径。为此,我想阅读一些文档(到目前为止我有这个,它是针对 TCP 的),然后在相关的内核函数中有一些 printk 语句来确认这一点。我将通过重新编译内核代码来做到这一点。
这是解决问题的方法吗?你有什么建议/参考吗?
具体回答您的问题,要了解 IPv4 的 UDP 处理,您可以使用 ftrace,如下所示:
在入口(接收方):
96882 2) | ip_local_deliver_finish() {
96883 2) 0.069 us | raw_local_deliver();
96884 2) | udp_rcv() {
96885 2) | __udp4_lib_rcv() {
96886 2) 0.087 us | __udp4_lib_lookup();
96887 2) | __skb_checksum_complete_head() {
96888 2) | skb_checksum() {
96889 2) | __skb_checksum() {
96890 2) | csum_partial() {
96891 2) 0.161 us | do_csum();
96892 2) 0.536 us | }
96893 2) | csum_partial() {
96894 2) 0.167 us | do_csum();
96895 2) 0.523 us | }
96896 2) | csum_partial() {
96897 2) 0.158 us | do_csum();
96898 2) 0.513 us | }
96899 2) | csum_partial() {
96900 2) 0.154 us | do_csum();
96901 2) 0.502 us | }
96902 2) | csum_partial() {
96903 2) 0.165 us | do_csum();
96904 2) 0.516 us | }
96905 2) | csum_partial() {
96906 2) 0.138 us | do_csum();
96907 2) 0.506 us | }
96908 2) 5.462 us | }
96909 2) 5.840 us | }
96910 2) 6.204 us | }
跟踪的另一部分显示如下:
98212 2) | ip_rcv() {
98213 2) | ip_rcv_finish() {
98214 2) 0.109 us | udp_v4_early_demux();
98215 2) | ip_route_input_noref() {
98216 2) | fib_table_lookup() {
98217 2) 0.079 us | check_leaf.isra.8();
98218 2) 0.492 us | }
对于网络代码的出口,下面提取了一些片段:
4) 0.547 us | udp_poll();
4) | udp_sendmsg() {
4) | udp_send_skb() {
4) 0.387 us | udp_error [nf_conntrack]();
4) 0.185 us | udp_pkt_to_tuple [nf_conntrack]();
4) 0.160 us | udp_invert_tuple [nf_conntrack]();
4) 0.151 us | udp_get_timeouts [nf_conntrack]();
4) 0.145 us | udp_new [nf_conntrack]();
4) 0.160 us | udp_get_timeouts [nf_conntrack]();
4) 0.261 us | udp_packet [nf_conntrack]();
4) 0.181 us | udp_invert_tuple [nf_conntrack]();
4) 0.195 us | udp_invert_tuple [nf_conntrack]();
4) 0.170 us | udp_invert_tuple [nf_conntrack]();
4) 0.175 us | udp_invert_tuple [nf_conntrack]();
4) | udp_rcv() {
4) + 15.021 us | udp_queue_rcv_skb();
4) + 18.829 us | }
4) + 82.100 us | }
4) + 92.415 us | }
4) | udp_sendmsg() {
4) | udp_send_skb() {
4) 0.226 us | udp_error [nf_conntrack]();
4) 0.150 us | udp_pkt_to_tuple [nf_conntrack]();
4) 0.146 us | udp_get_timeouts [nf_conntrack]();
4) 1.098 us | udp_packet [nf_conntrack]();
4) | udp_rcv() {
4) 1.314 us | udp_queue_rcv_skb();
4) 3.282 us | }
4) + 20.646 us | }
上面在 ftrace 中称为函数图:
如何使 ftrace function_graph 跟踪器可以使用 linux 内核函数?
我用于跟踪 udp 的 bashscript 如下(以 root 身份运行):
#!/bin/bash
mkdir /debug
mount -t debugfs nodev /debug
mount -t debugfs nodev /sys/kernel/debug
echo udp_* >/debug/tracing/set_ftrace_filter
echo function_graph >/debug/tracing/current_tracer
echo 1 >/debug/tracing/tracing_on
sleep 20
echo 0 >/debug/tracing/tracing_on
cat /debug/tracing/trace > /tmp/tracing.out$$
现在输出文件位于 /tmp/tracing.out 内,其中是 shell 脚本进程。20 秒的目的是允许用户空间活动发生——此时只是开始大量的 UDP 活动。您还可以从上面的脚本中删除“ echo udp_* >/debug/tracing/set_ftrace_filter ”,因为默认是跟踪所有内容。
linux 网络堆栈是内核的一大块,你需要花一些时间研究它。我认为这本书可能会有所帮助(专注于旧内核 2.4 和 2.6,但最新内核 3.x 的逻辑保持不变):
Linux 网络架构——Linux 内核中网络协议的设计与实现
您还可以查看以下链接:
http://www.linuxfoundation.org/collaborate/workgroups/networking/kernel_flow
http://wiki.openwrt.org/doc/networking/praxis
http://www.ibm.com/developerworks/linux/library/l-linux-networking-stack/?ca=dgr-lnxw01lnxNetStack
http://gicl.cs.drexel.edu/people/sevy/network/Linux_network_stack_walkthrough.html
您还需要浏览内核源代码:
http://lxr.linux.no/#linux+v3.7.3/
使用这个函数开始你的网络子系统之路:
ip_rcv,它在收到数据包时调用。然后调用其他函数 ( ip_rcv_finish
,ip_local_deliver
和 ip_local_deliver_finish
=> 这个函数负责选择好的传输层)
如果您喜欢更直观的方式,请尝试flame-grahps。下面是一个 UDP 传输流示例(使用 netperf 传输 UDP 数据包):
您可以对内核中的任何相关流程执行相同的操作。您还可以搜索特定功能或关键字并放大/缩小。这也让您了解流程中较重的功能。
希望这可以帮助。
这个链接也不错。它讨论了 udp 和底层 IP、NIC 等:
https://blog.packagecloud.io/eng/2017/02/06/monitoring-tuning-linux-networking-stack-sending-data/