数据包接收系列 — IP协议处理流程(二)

本文详细解析了IP数据包在Linux内核2.6.37版本中从网络层到传输层的处理流程,包括分片重组、安全策略检查及如何调用不同四层协议的处理函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要内容:在接收数据包时,IP协议的处理流程。

内核版本:2.6.37

Author:zhangskd @ csdn blog 

 

我们接着来看数据包如何发往本地的四层协议。

 

ip_local_deliver

 

在ip_local_deliver()中,如果发现数据报有被分片,则进行组装。

然后调用NF_INET_LOCAL_IN处的钩子函数,如果数据包被钩子函数放行,

则调用ip_local_deliver_finish()继续处理。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* Deliver IP Packets to the higher protocol layers. */  
  2.   
  3. int ip_local_deliver(struct sk_buff *skb)  
  4. {  
  5.     /*  
  6.      * Reassemble IP fragments.  
  7.      * 如果IP数据报有被分片,则在这里进行组装还原。 
  8.      */  
  9.     if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {  
  10.         if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))  
  11.             return 0;  
  12.     }  
  13.   
  14.     /* 调用netfilter的NF_INET_LOCAL_IN的钩子函数,如果此数据包被钩子函数放行,则调用 
  15.      * ip_local_deliver_finish()继续处理。 
  16.      */  
  17.     return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,  
  18.         ip_local_deliver_finish);  
  19. }  

 

ip_local_deliver_finish

 

ip_local_deliver_finish()主要做了:

处理RAW IP,如果有配置安全策略,则进行IPsec安全检查。

根据IP报头的protocol字段,找到对应的L4协议(net_protocol),调用该协议的接收函数net_protocol->handler()。

对于TCP协议,net_protocol实例为tcp_protocol,协议处理函数为tcp_v4_rcv()。

接下来就进入四层协议的处理流程了,TCP协议的入口函数为tcp_v4_rcv()。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int ip_local_deliver_finish(struct sk_buff *skb)  
  2. {  
  3.     struct net *net = dev_net(skb->dev);  
  4.   
  5.     /* 把skb->data指向L4协议头,更新skb->len */  
  6.     __skb_pull(skb, ip_hdrlen(skb));  
  7.   
  8.     /* 赋值skb->transport_header */  
  9.     skb_reset_transport_header(skb);  
  10.   
  11.     rcu_read_lock();  
  12.     {  
  13.         int protocol = ip_hdr(skb)->protocol; /* L4协议号 */  
  14.         int hash, raw;  
  15.         const struct net_protocol *ipprot;  
  16.   
  17. resubmit:  
  18.         /* 处理RAW IP */  
  19.         raw = raw_local_deliver(skb, protocol);  
  20.   
  21.         hash = protocol & (MAX_INET_PROTOS - 1); /* 作为数组索引 */  
  22.   
  23.         /* 从inet_protos数组中取出对应的net_protocol元素,TCP的为tcp_protocol */  
  24.         ipprot = rcu_dereference(inet_protos[hash]);  
  25.   
  26.         if (ipprot != NULL) {  
  27.             int ret;  
  28.   
  29.             if (! net_eq(net, &init_net) && ! ipprot->netns_ok) {  
  30.                 if (net_ratelimit())  
  31.                     printk("%s: proto %d isn't netns-ready\n", __func__, protocol);  
  32.                     kfree_skb(skb);  
  33.                     goto out;  
  34.             }  
  35.   
  36.             /* 如果需要检查IPsec安全策略 */  
  37.             if (! ipprot->no_policy) {  
  38.                 if (! xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {  
  39.                     kfree_skb(skb);  
  40.                     goto out;  
  41.                 }  
  42.                 nf_reset(skb);  
  43.             }  
  44.   
  45.             /* 调用L4协议的处理函数,对于TCP,调用tcp_protocol->handler,为tcp_v4_rcv() */  
  46.             ret = ipprot->handler(skb);  
  47.             if (ret < 0) {  
  48.                 protocol = - ret;  
  49.                 goto resubmit;  
  50.             }  
  51.             IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);  
  52.   
  53.         } else {  
  54.             if (! raw) {  
  55.                 if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {  
  56.                     IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);  
  57.                     icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);  
  58.                 } else  
  59.                     IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);  
  60.   
  61.                 kfree_skb(skb);  
  62.             }  
  63.         }   
  64.   
  65. out:  
  66.     rcu_read_unlock();  
  67.     return 0;  
  68. }  

 

L4协议处理函数

 

数据包从L2传递到L3时,通过packet_type结构来找到L3协议的处理函数。

同理,数据包从L3传递到L4时,通过net_protocol结构来找到L4协议的处理函数。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* This is used to register protocols. */  
  2. struct net_protocol {  
  3.     int (*handler) (struct sk_buff *skb); /* L4协议的接收函数 */  
  4.     void (*error_handler) (struct sk_buff *skb, u32 info); /* ICMP用的 */  
  5.     ...  
  6.     unsigned int no_policy:1/* 不需要检查IPsec策略 */  
  7.                  netns_ok:1;  
  8. };  
  9.   
  10. #define MAX_INET_PROTOS 256 /* MUST be a power of 2 */  

 

把协议号作为索引,可以从数组找到对应协议的net_protocol元素。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. enum {  
  2.     ...  
  3.     IPPROTO_ICMP = 1/* Internet Control Message Protocol */  
  4.     ...  
  5.     IPPROTO_TCP = 6/* Transmission Control Protocol */  
  6.     ...  
  7.     IPPROTO_UDP = 17/* User Datagram Protocol */  
  8.     ...  
  9.     IPPROTO_RAW = 255/* Raw IP packets */  
  10.     IPPROTO_MAX  
  11. };   
  12.   
  13. /* TCP协议的net_protocol */  
  14. static const struct net_protocol tcp_protocol = {  
  15.     .handler = tcp_v4_rcv,  
  16.     .err_handler = tcp_v4_err,  
  17.     ...  
  18.     .no_policy = 1,  
  19.     .netns_ok = 1,  
  20. };  
  21.   
  22. /* UDP协议的net_protocol */  
  23. static const struct net_protocol udp_protocol = {  
  24.     .handler = udp_rcv,  
  25.     .err_handler = upd_err,  
  26.     ...  
  27.     .no_policy = 1,  
  28.     .netns_ok = 1,  
  29. };  
  30.   
  31. /* ICMP协议的net_protocl */  
  32. static const struct net_protocol icmp_protocol = {  
  33.     .handler = icmp_rcv,  
  34.     .no_policy = 1,  
  35.     .netns_ok = 1,  
  36. };  

初始化

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int __init inet_init(void)  
  2. {  
  3.     ...  
  4.     /* Add all the base protocols */  
  5.     if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)  
  6.         printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");  
  7.   
  8.     if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)  
  9.         printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");  
  10.   
  11.     if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)  
  12.         printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");  
  13.     ...  
  14. }  

 

RAW IP

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct raw_hashinfo {  
  2.     rwlock_t lock;  
  3.     struct hlist_head ht[RAW_HTABLE_SIZE];  
  4. };  
  5.   
  6. static struct raw_hashinfo raw_v4_hashinfo = {  
  7.     .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock);  
  8. };  
  9. #define RAW_HTABLE_SIZE MAX_INET_PROTOS  

RAW IP报的处理函数为raw_v4_input()。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int raw_local_deliver(struct sk_buff *skb, int protocol)  
  2. {  
  3.     int hash;  
  4.     struct sock *raw_sk;  
  5.   
  6.     hash = protocol & (RAW_HTABLE_SIZE - 1); /* L4协议号作为key */  
  7.     raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); /* 取出哈希桶的第一个sock */  
  8.   
  9.     /* If there maybe a raw socket we must check - if not we don't care less. 
  10.      * raw_v4_input(): IP input processing comes here for RAW socket delivery. 
  11.      * 如果该协议上有连接,那么调用raw_v4_input()来处理。 
  12.      */  
  13.     if (raw_sk && ! raw_v4_input(skb, ip_hdr(skb), hash))  
  14.         raw_sk = NULL;  
  15.   
  16.     return raw_sk != NULL;  
  17. }  
### Swin Transformer 论文精读:Hierarchical Vision Transformer Using Shifted Windows Swin Transformer 是一种基于视觉的分层 Transformer 模型,其核心创新在于通过 **Shifted Window-based Self-Attention** 实现了线性计算复杂度,同时能够生成多尺度特征表示。这种方法在图像分类、目标检测和语义分割等任务中取得了显著的性能提升 [^2]。 #### 核心架构概述 Swin Transformer 的整体结构分为多个阶段(Stage),每个阶段包含多个 Swin Transformer Block。这些块使用 **窗口化自注意力机制** 和 **移位窗口策略** 来实现高效计算并捕捉长距离依赖关系。 - **分层特征提取** 类似于传统卷积神经网络(如 ResNet),Swin Transformer 采用分层设计来逐步降低空间分辨率并增加通道维度。这种设计允许模型从局部到全局地构建特征表示。 - **窗口划分与移位窗口机制** 在每个 Swin Transformer Block 中,输入特征图被划分为不重叠的窗口,并在这些窗口内执行自注意力计算。为了增强跨窗口的信息交互,在下一个 Block 中对窗口进行移位操作(Shifted Windows)。这种方式既减少了计算量,又保持了模型对全局信息的感知能力 [^1]。 ```python # 窗口划分伪代码示例 def window_partition(x, window_size): B, H, W, C = x.shape # 将图像划分为多个窗口 x = tf.reshape(x, shape=[B, H // window_size, window_size, W // window_size, window_size, C]) windows = tf.transpose(x, perm=[0, 1, 3, 2, 4, 5]) return tf.reshape(windows, shape=[-1, window_size, window_size, C]) # 移位窗口伪代码 def shifted_window_attention(x, window_size, shift_size): B, H, W, C = x.shape # 对特征图进行滚动操作以实现窗口移位 x = tf.roll(x, shift=(-shift_size, -shift_size), axis=(1, 2)) return window_partition(x, window_size) ``` #### 自注意力机制优化 传统的 Vision TransformerViT)在整个图像上应用自注意力机制,导致计算复杂度为 $O(n^2)$,其中 $n$ 是图像块的数量。而 Swin Transformer 通过将注意力限制在局部窗口内,将复杂度降低到 $O(n)$,使其适用于高分辨率图像处理 [^4]。 此外,移位窗口机制确保了相邻窗口之间的信息流动,从而避免了局部注意力带来的信息隔离问题。这种设计使得 Swin Transformer 能够在保持计算效率的同时实现全局建模能力。 #### 实验结果与性能优势 Swin Transformer 在多个视觉任务中表现出色: - **ImageNet 分类任务**:Swin-Tiny、Swin-Small、Swin-Base 和 Swin-Large 四种变体均在 ImageNet-1K 上实现了优于其他 Transformer 主干网络的 Top-1 准确率。 - **COCO 目标检测**:在 COCO 数据集上,Swin Transformer 在 Faster R-CNN 框架下达到了 SOTA 性能,mAP 超过之前的最佳方法。 - **ADE20K 语义分割**:在 ADE20K 数据集上,Swin Transformer 作为编码器也取得了领先的 mIoU 指标 [^2]。 #### 消融实验分析 论文还进行了详细的消融研究,验证了以下几个关键组件的有效性: - **窗口大小的影响**:较大的窗口有助于捕捉更广泛的上下文,但会增加计算开销。 - **移位窗口的重要性**:实验证明,移位机制可以显著提升模型性能,尤其是在长距离依赖任务中。 - **不同层级的设计**:通过对比不同层级深度和通道配置,论文展示了如何平衡精度与效率 [^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值