《电子技术应用》
您所在的位置:首页 > 可编程逻辑 > 其他 > Linux教学——如何学习 Linux 内核网络协议栈

Linux教学——如何学习 Linux 内核网络协议栈

2022-10-26
作者:土豆居士
来源:电子技术应用专栏作家 一口Linux
关键词: Linux 网络协议栈

  协议栈的细节

  下面将介绍一些内核网络协议栈中常常涉及到的概念。

  sk_buff

  内核显然需要一个数据结构来表示报文,这个结构就是 sk_buff ( socket buffer 的简称),它等同于在<TCP/IP详解 卷2>中描述的 BSD 内核中的 mbuf。

  sk_buff 结构自身并不存储报文内容,它通过多个指针指向真正的报文内存空间:

  微信截图_20221026155705.png

  sk_buff 是一个贯穿整个协议栈层次的结构,在各层间传递时,内核只需要调整 sk_buff 中的指针位置就行。

  net_device

  内核使用 net_device 表示网卡。网卡可以分为物理网卡和虚拟网卡。物理网卡是指真正能把报文发出本机的网卡,包括真实物理机的网卡以及VM虚拟机的网卡,而像 tun/tap,vxlan、veth pair 这样的则属于虚拟网卡的范畴。

  如下图所示,每个网卡都有两端,一端是协议栈(IP、TCP、UDP),另一端则有所区别,对物理网卡来说,这一端是网卡生产厂商提供的设备驱动程序,而对虚拟网卡来说差别就大了,正是由于虚拟网卡的存在,内核才能支持各种隧道封装、容器通信等功能。

  微信截图_20221026155928.png

  socket & sock

  用户空间通过 socket()、bind()、listen()、accept() 等库函数进行网络编程。而这里提到的 socket 和 sock 是内核中的两个数据结构,其中 socket 向上面向用户,而 sock 向下面向协议栈。

  如下图所示,这两个结构实际上是一一对应的。

  微信截图_20221026155951.png

  注意到,这两个结构上都有一个叫 ops 的指针, 但它们的类型不同。socket 的 ops 是一个指向 struct proto_ops 的指针,sock 的 ops 是一个指向 struct proto 的指针, 它们在结构被创建时确定。

  回忆网络编程中 socket() 函数的原型:

微信截图_20221026160045.png

  实际上, socket->ops 和 sock->ops 由前两个参数 socket_family 和 socket_type 共同确定。

  如果 socket_family 是最常用的 PF_INET 协议簇, 则 socket->ops 和 sock->ops 的取值就记录在 INET 协议开关表中:

 微信截图_20221026160135.png

  L3->L4

  我们知道网络协议栈是分层的,但实际上,具体到实现,内核协议栈的分层只是逻辑上的,本质还是函数调用。发送流程(上层调用下层)通常是直接调用(因为没有不确定性,比如TCP知道下面一定IP),但接收过程不一样了,比如报文在 IP 层时,它上面可能是 TCP,也可能是 UDP,或者是 ICMP 等等,所以接收过程使用的是注册-回调机制。

  还是以 INET 协议簇为例,注册接口是:

  微信截图_20221026160156.png

  在内核网络子系统初始化时,L4 层协议(如下面的 TCP 和 UDP)会被注册:

 微信截图_20221026160213.png

  而在IP层,查询过路由后,如果该报文是需要上送本机的,则会根据报文的 L4 协议,送给不同的 L4 处理:

 微信截图_20221026160253.png

  L2->L3

  L2->L3 如出一辙。只不过注册接口变成了:

  微信截图_20221026160310.png

  谁会注册呢?显然至少 IP 会:

微信截图_20221026160329.png

  而在报文接收过程中,设备驱动程序会将报文的 L3 类型设置到 skb->protocol,然后在内核 netif_receive_skb 收包时,会根据这个 protocol 调用不同的回调函数:

 微信截图_20221026160346.png

  Netfilter

  Netfilter 是报文在内核协议栈必然会通过的路径,我们从下面这张图就可以看到,Netfilter 在内核的 5 个地方设置了 HOOK 点,用户可以通过配置 iptables 规则,在 HOOK 点对报文进行过滤、修改等操作。

  微信截图_20221026160406.png

  在内核代码中,我们时常可见 NF_HOOK 这样的调用。我的建议是,如果你暂时不考虑 Netfilter,那么就直接跳过, 跟踪 okfn 就行。

  微信截图_20221026160428.png

  dst_entry

  内核需要确定收到的报文是应该本地上送(local deliver)还是转发(forward),对本机发送(local out)的报文需要确定是从哪个网卡发送出去,这都是内核通过查询 fib (forward information base, 转发信息表) 确定。fib 可以理解为一个数据库,数据来源是用户配置或者内核自动生成的路由。

  fib 查询的输入是报文 sk_buff,输出是 dst_entry. dst_entry 会被设置到 skb 上:

 微信截图_20221026160448.png

  而 dst_entry 中最重要的是一个 input 指针和 output 指针:

  微信截图_20221026160506.png

微信截图_20221026160524.png



 更多信息可以来这里获取==>>电子技术应用-AET<<

微信图片_20210517164139.jpg

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。