Linux网络协议栈对数据包的处理过程

7 篇文章 0 订阅
2 篇文章 0 订阅

原帖地址:
https://blog.csdn.net/yedushu/article/details/52588412?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%BD%91%E7%BB%9C%E6%8A%A5%E6%96%87%E5%A4%84%E7%90%86%E8%BF%87%E7%A8%8B&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-4-52588412

0、需要额外学习内容:

skb的结构

中断机制(硬件终端、软中断、上下部等)

netfilter

1、链路层接收分组过程

1.1 图解

在这里插入图片描述

在这里插入图片描述

1.2 第一步:产生中断,处理中断

当网卡在收到一个与自己的MAC地址匹配或者广播的以太网帧时,会产生一个硬件中断,随后网卡驱动处理这个中断。

  1. 从DMA/PIO或其他得到分组数据,写到内存中去。
    DMA:(DirectMemoryAccess,直接内存存取),顾名思义DMA功能就是让设备可以绕过处理器,直接由内存来读取资料。打开硬盘的DMA模式将大幅度的提高硬盘系统的功能,使我们能更快更好的进行视频处理和文件传输
    PIO:(Programming Input/Output Model)PIO模式是一种通过CPU执行I/O端口指令来进行数据的读写的数据交换模式。数据传输速率低下,CPU占有率也很高,大量传输数据时会因为占用过多的CPU资源而导致系统停顿,无法进行其它的操作。
  2. 随后分配一个新的套接字缓冲区skb,并调用一个与协议无关,网络设备均支持的通用网络接收处理函数 netif_rx(skb)。netif_rx()函数让内核进一步处理skb。
    skb:struct sk_buffer 的简写。是 linux TCP/IP stack 中,用于管理Data Buffer的结构。Sk_buffer 在数据包的发送和接收中起着重要的作用。
  3. 随后skb进入到达队列等待CPU处理。对于多核CPU,每个CPU维护一个队列。如果此时FIFO(先进先出)队列已满,就丢弃此分组。skb排队后,调用_cpu_raise_softirq()函数标记NET_RX_SOFTIRQ网络接收软中断,等待CPU处理。
  4. 至此,netif_rx()调用完成,返回调用者状况信息(成功或失败)。此时,这个硬件中断完成任务,CPU出现软中断,数据分组等待被上层协议栈处理。

1.3 第二步:NET_RX_SOFTIRQ网络接收软中断

这一阶段根据协议(IP、ARP)的不同来处理数据分组。

  1. skb进入CPU到达队列后,产生网络接收软中断,CPU调用**do_softirq()**开始处理这个软件中断。
  2. 接着调用net_rx_action()处理前面标记的NET_RX_SOFTIRQ,把出队列的skb送到相应的列表进行处理(根据协议不同送到不同的列表)。在数据包的处理函数netif_receive_skb中,会先看ptype_all中是否有注册的协议,如果有,则调用相应的处理函数,然后再到ptype_base中,找到合适的协议,将skb发送到相关协议的处理函数。比如ip协议(ip_rcv)或者arp(arp_rcv)等等.
    ptype_base为一个hash表,而ptype_all为一个双向链表.每一个里面注册的协议都用一个struct packet_type表示。如图所示。
    在这里插入图片描述

2、网络层处理分组(以IPv4为例)

2.1 图解

以IPv4分组为例。
在这里插入图片描述

2.2 处理过程

2.2.1 ip_rcv()

skb被送到ip_rcv()函数进行处理。首先ip_rcv函数验证IP分组。比如目的地是否是本机地址,校验和是否正确等等。若正确,则交给netfilter的NF_IP_ROUTING;否则,丢弃

2.2.2 ip_rcv_finish()

随后将分组发送到ip_rcv_finish()函数处理。根据skb结构的目的或路由信息发送到不同的处理函数。
ip_rcv_finish()函数的具体处理过程如下:
从 skb->nh ( IP 头,由 netif_receive_skb 初始化)结构得到 IP 地址:

struct net_device *dev = skb->dev;
struct iphdr *iph = skb->nh.iph;

而 skb->dst 或许包含了数据分组到达目的地的路由信息,如果没有,则需要查找路由,如果最后结果显示目的地不可达,那么就丢弃该数据包:

if (skb->dst == NULL) {
  if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
     goto drop;			//丢弃
}

ip_rcv_finish() 函数最后执行 dst_input ,决定数据包的下一步的处理。

本机分组则由ip_local_deliver处理;
需要转发的数据则由ip_forward()函数处理;
组播数据包则由ip_mr_input()函数处理。

2.3 ip_forward()转发数据包

  1. 处理IP头选项。如果需要,会记录本地IP地址和时间戳;
  2. 确认分组可以被转发
  3. 将TTL减1,如果TTL为0,则丢弃分组,
    TTL是 Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。TTL是IPv4报头的一个8 bit字段。
  4. 根据MTU大小和路由信息,对数据分组进行分片
    MTU即最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。
  5. 将数据分组送往外出设备。
    如果因为某种原因分组转发失败,则回应ICMP消息,来回复不能转发的原因。
    如果对转发的分组进行各种检查无误后:
  6. 执行ip_forward_finish()函数,准备发送。
  7. 然后执行dst_output(skb)将分组发到转发的目的主机或本地主机。dst_output(skb) 函数要执行虚函数 output (单播的话为 ip_output ,多播为 ip_mc_output )。
  8. 最后, 调用ip_finish_output() 进入邻居子系统。
    邻居子系统:在数据链接层,必须要获取发送方和接收方的MAC地址,这样数据才能正确到达接收方。邻居子系统的作用就是把IP地址转换成对应的MAC地址。如果目的主机不是和发送发位于同一局域网时,解析的MAC地址就是下一跳网关地址

2.4 ip_local_deliver本地处理

ip_local_deliver中对ip分片进行重组,经过LOCAL_IN钩子点,然后调用ip_local_deliver_finish;

/*
 *     Deliver IP Packets to the higher protocol layers.
 */
int ip_local_deliver(struct sk_buff *skb)
{
    /*
     *    重组 IP fragments.
     */
    struct net *net = dev_net(skb->dev);

    /* 分片重组 */
    if (ip_is_fragment(ip_hdr(skb))) {
        if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
            return 0;
    }

    /* 经过LOCAL_IN钩子点 */
    return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
               net, NULL, skb, skb->dev, NULL,
               ip_local_deliver_finish);
}

最后调用ip_local_deliver_finish()函数:ip_local_deliver_finish函数处理原始套接字的数据接收,并调用上层协议的包接收函数,将数据包传递到传输层;

3. 传输层处理

TCP处理过程如图
接收到的分组由ip_local_deliver进入。
在这里插入图片描述
发送分组或者响应分组有ip_queue_xmit()函数出口出去:
发送时,ip_queue_xmit()函数检查socket结构体中是否有路由信息,如果没有则执行ip_route_flow()查找,并存储到skb数据结构中。如果找不到,则丢弃。

4. 流程总结

链路层

在这里插入图片描述
网卡收到以太帧,发出硬件中断 ——> 创建新的套接字缓冲区skb ——>调用netif_rx(skb)处理skb ——> 将skb进入CPU的FIFO队列 ——> 标记NET_RX_SOFTIRQ网络接收软中断 ——>CPU调用do_softirq()开始处理中断 ——> 调用net_rx_action处理NET_RX_SOFTIRQ ——> 根据skb不同协议,将skb送到不同列表进行处理 ——> 进入网络层ip.rcv()函数处理

网络层

在这里插入图片描述
ip_rcv() ——> ip_rcv()验证IP分组 ——> 若正确,则发送到ip_rcv_finish() ——> 根据skb结构中的目的或路由信息发送到不同处理函数

  • —— > ip_forward()处理需要转发的数据 ——> 处理IP头选项,确认分组可以被转发 ——> TTL-1,对数据分组进行分片 ——> 如果可以发送,则执行ip_forward_finish()函数进行转发 ——> 执行ip_finish_output()函数进入邻居子系统
  • —— > ip_local_deliver()处理本地分组 ——> 对IP分片进行重组,经过LOCAL_IN钩子点 ——> 调用ip_local_deliver_finish()函数处理原始套接字 ——> 将数据包送到传输层

传输层

在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值