[内核源码]Linux 发送报文过程简析及网络杂谈

linux 发送报文过程简析

Linux 网络栈可以处理 L2 层以上的报文,网络报文向外发送需要经由对应的网络设备驱动完成。
用户进程通过调用 write 等函数构造报文,根据报文类型,通过 tcp_sendmsg() 、udp_sendmsg()生成相应的传输层报文,然后不通格式的类型的报文进行相应的处理后,通过 ip_route_out() 查找路由,最后都交给 ip_output() 函数处理,构造网络层报文,并进行相应处理,最终交由 dev_queue_xmit() 函数处理,提交给对应的网络设备驱动提供的函数接口,向外部发送报文。
下面是 Linux 内核网络的数据流图:
这里写图片描述

重要数据结构 —— sk_buff

sk_buff 是 Linux 网络代码中最重要的数据结构,代表已经接受或正要传输的数据的报头。由巨大的变量堆(heap)组成。
许多层次的网络(MAC、IP、TCP或UDP)都会使用这个结构,当该结构从一个分层传到另一个分层时,不同字段发生变化,如从 L3 传给 L2 需要附加报头。这需要在缓冲区开端新增空间,Linux 内核提供了 skb_reverse 函数执行该操作。

重要函数 —— ip_output()

报文的向外发送和转发最终都经过 ip_output 函数,其中使用 ip_finish_output 对报文进行处理,包括分片、ARP学习、MAC地址修改或填充等。

  int ip_output(struct sk_buff *skb)
  {
          struct net_device *dev = skb_dst(skb)->dev;
          IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb-len); /*统计发送出去的数据包*/
          skb->dev = dev;         /*具体选择从哪个网卡设备上发送出去IP报文*/
          skb->protocol = htons(ETH_P_IP);         /* 指定报文类型为IP packet.  */

          return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,ip_finish_output,!(IPCB(skb)->flags & IPSKB_REROUTED)); //HOOK点 POST_ROUTING.

}

重要函数 —— ip_forward()

当ip 分组到达的系统不是分组的最终目的地址时, ip_input将调用ip_forward()函数转发分组;或者如果在选项处理时发现选项标识了源路由,并且分组还没到达最终目的地时,则选项处理函数会调用ip_forward()转发分组.

Linux 邻居子系统

处于同一个 LAN 就是邻居,在物理上,是同一个局域网的终端,在网络拓扑结构上,指的是它们的距离仅仅为一跳。
我们知道, L3 层地址即 IP 是不固定的,而 L2 层地址相对固定(事实上也可以更改),一般情况下是硬件设备出厂时全球唯一的。这就导致 L2 和 L3 的关联不是固定的,所以需要一个协议来管理这个关联。IP 网络中常使用 ARP 协议(IPV4),此外 ND(邻居发现协议)则为 IPv6开发。
Linux 邻居子系统便是实现了这样的过程,在发送报文时候,在本机进行路由查找或者发送 arp 报文寻找。

抓包程序层次

不同的抓包程序和设备驱动程序所处的优先层次可能不同,抓包程序的优先级可能比设备驱动程序更高,也就是说,我们的抓包程序可能在设备驱动程序还没有填充不到64字节的帧的时候,抓包程序已经捕获了数据。因此不同的抓包工具抓到的数据帧的大小可能不同。对于发送出去的报文,wireshark 抓包在设备驱动程序处理前,sniffer 则在设备处理之后抓包。Sniffer是利用计算机的网络接口截获目的地为其他计算机的数据报文的一种工具。而 wireshark 则是利用 winpcap或类似于linux下的libpcap 进行抓包。

参考文章

  1. https://www.cnblogs.com/newjiang/p/7428125.html
  2. https://blog.csdn.net/one_clouder/article/details/52674706
  3. https://blog.csdn.net/qq_18144747/article/details/78497380

你可能感兴趣的:(c/c++,网络,Linux)