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

内核版本:2.6.37

Author:zhangskd @ csdn blog

IP报头

IP报头:

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)__u8 ihl:4,version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)__u8 version:4, /* 协议版本,IPv4为4 */ihl:4; /* 首部长度,不包括选项为5,表示20字节 */
#else
#error "Please fix <asm/byteorder.h>"
#endif__u8 tos; /* TOS服务类型,6位DSCP,2为ECN */__be16 tot_len; /* IP包总长度,最大为65535 */__be16 id; /* 标识符,同一个IP包的不同分片具有相同的标识符 */__be16 frag_off; /* 3个标志位,13位偏移 */__u8 ttl; /* 存活时间,一般为64跳 */__u8 protocol; /* L4协议值 */__sum16 check; /* 报头校验和,不包含载荷 */__be32 saddr; /* 源IP */__be32 daddr; /* 目的IP */
}; 

ip_rcv

调用ip_rcv()时skb中的一些变量:

ip_rcv()是IP层的入口,主要做了:

丢弃L2目的地址不是本机的数据包(这说明网卡处于混杂模式,嗅探器会处理这些包)。

检查skb的引用计数,如果大于1,说明其它地方也在使用此skb,则克隆一个skb返回;否则直接返回原来的skb。

数据包合法性检查:

data room必须大于IP报头长度。

IP报头长度至少是20,类型为IPv4。

data room至少能容纳IP报头(包括IP选项)。

检查IP报头校验和是否正确。

数据包没被截断(skb->len >= 报总长),报总长不小于20。

如果L2有进行填充(以太网帧最小长度为64),则把IP包裁剪成原大小,去除填充。此时如果接收的NIC

已计算出校验和,则让其失效,让L4自己重新计算。

最后,调用netfilter的NF_INET_PRE_ROUTING的钩子函数,如果此数据包被钩子函数放行,则调用

ip_rcv_finish()继续处理。

/* Main IP Receive routinue. */int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{struct iphdr *iph;u32 len;/* When the interface is in promisc mode, drop all the crap that it receives,* do not try to analyse it.* 当数据帧的L2目的地址和接收接口的地址不同时,skb->pkt_type就被设成PACKET_OTHERHOST。* 网卡本身会丢弃这些包,除非设成混杂模式。嗅探器自会处理这种包,IP层无需理会。*/if (skb->pkt_type == PACKET_OTHERHOST)goto drop;IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);/* 如果此skb的引用计数大于1,说明在其它地方也被使用,则克隆一个skb返回。* 否则直接返回原来的skb。*/if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);goto out;}/* 确保data room >= IP报头 */if (! pskb_may_pull(skb, sizeof(struct iphdr)))goto inhdr_error;iph = ip_hdr(skb);/** RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum.* Is the datagram acceptable?* 1. Length at least the size of an ip header* 2. Version of 4* 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]* 4. Doesn't have a bogus length*//* IP报头长度至少是20,类型为IPv4 */if (iph->ihl < 5 || iph->version != 4)goto inhdr_error;/* data room至少能容纳IP报头(包括IP选项) */if (! pskb_may_pull(skb, iph->ihl * 4))goto inhdr_error;iph = ip_hdr(skb);/* 检查IP报头校验和是否正确 */if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))goto inhdr_error;len = ntohs(iph->tot_len); /* IP报文总长度 *//* L2为了满足最小帧的长度可能会进行填充,所以skb->len >= len。* Ethernet数据帧的最小帧长度为64字节。*/if (skb->len < len) { /* 数据包被截断了 */IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);} else if (len < (iph->ihl * 4))goto inhdr_error;/* Our transport medium may have padded the buffer out. Now we know it is* IP we can trim to the true length of the frame.* Note this now means skb->len holds ntohs(iph->tot_len).*//* 如果L2有进行填充,则把IP包裁剪成原大小。* 如果接收的NIC已计算出校验和,则让其失效,让L4自己重新计算。*/if (pskb_trim_rcsum(skb, len)) {IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);goto drop;}/* Remove any debris in the socket control block */memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));/* Must drop socket now because of tproxy. */skb_orphan(skb);/* 调用netfilter的NF_INET_PRE_ROUTING钩子 */return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish);inhdr_error:IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);drop:kfree_skb(skb);out:return NET_RX_DROP;
}

如果skb的引用计数大于1,说明在其它地方也被使用,则克隆一个skb返回,否则直接返回原来的skb。

/*** skb_shard_check - check if buffer is shard and if so clone it* @skb: buffer to check* @pri: priority for memory allocation* * If the buffer is shared the buffer is cloned and the old copy drops a* reference. A new clone with a single reference is returned.* If the buffer is not shared the original buffer is returned. When being called* from interrupt status or with spinlocks held pri must be GFP_ATOMIC.* NULL is returned on a memory allocation failure.*/static inline struct sk_buff *skb_shared_check(struct sk_buff *skb, gfp_t pri)
{/* 不能睡眠,否则调用might_sleep()打印栈的回溯信息 */might_sleep_if(pri & __GFP_WAIT); if (skb_shared(skb)) { /* skb->users是否为1 */struct sk_buff *nskb = skb_clone(skb, pri);kfree_skb(skb);skb = nskb;}return skb;
}
/*** skb_orphan - orphan a buffer* @skb: buffer to orphan* If a buffer currently has an owner then we call the owner's destructor* function and make the @skb unowned. The buffer continues to exist* but is no longer charged to its former owner.*/
static inline void skb_orphan(struct sk_buff *skb)
{if (skb->destructor)skb->destructor(skb);skb->destructor = NULL;skb->sk = NULL;
}

ip_rcv_finish

ip_rcv_finish()主要做了:

查找路由,决定要把数据包发送到哪,赋值skb_dst()->input(),发往本地为ip_local_deliver,转发为ip_forward()。

更新Traffic Control (Qos)层的统计数据。

处理IP选项,检查选项是否正确,然后将选项存储在IPCB(skb)->opt中。

最后执行skb_dst()->input(),要么发往四层,要么进行转发,取决于IP的目的地址。

static int ip_rcv_finish(struct sk_buff *skb)
{const struct iphdr *iph = ip_hdr(skb);struct rtable *rt;/* * Initialise the virtual path cache for the packet.* It describes how the packet travels inside linux networking.*/if (skb_dst(skb) == NULL) {/* 查找路由,决定要把包送往哪里 */int err = ip_route_input_noref(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);if (unlikely(err)) {if (err == -EHOSTUNREACH) /* no route to host,主机不可达 */IP_INC_STATS_BH(dev_net(skb->dev), IPSTATS_MIB_INADDRERRORS);else if (err == -ENETUNREACH) /* Network is unreachable,网络不可达 */IP_INC_STATS_BH(dev_net(skb->dev), IPSTATS_MIB_INNOROUTES);else if (err == -EXDEV) /* Cross-device link */NET_INC_STATS_BH(dev_net(skb->dev), LINUX_MIB_IPRPFILTER);goto drop; /* 目的地不可达,丢弃 */}}/* 更新Traffic Control (Qos)层的统计数据 */
#ifdef CONFIG_NET_CLS_ROUTEif (unlikely(skb_dst(skb)->tclassid)) {struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct);u32 idx = skb_dst(skb)->tclassid;st[idx & 0xFF].o_packets++;st[idx & 0xFF].o_bytes += skb->len;st[(idx >> 16) & 0xFF].i_packets++;st[(idx >> 16) & 0xFF].i_bytes += skb->len;}
#endif/* 处理IP选项,调用ip_options_compile()来检查选项是否正确,然后将选项存储* 在IPCB(skb)->opt中。*/if (iph->ihl > 5 && ip_rcv_options(skb))goto drop;rt = skb_rtable(skb);if (rt->rt_type == RTN_MULTICAST) {IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST, skb->len);} else if (rt->rt_type == RTN_BROADCAST)IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST, skb->len);/* skb_dst(skb)->input()在ip_route_input_noref()中被赋值,要么是ip_local_deliver(),* 要么是ip_forward(),取决于数据包的目的地址。*/return dst_input(skb);drop:kfree_skb(skb);return NET_RX_DROP;
}/* Input packet from network to transport. */
static inline int dst_input(struct sk_buff *skb)
{return skb_dst(skb)->input(skb);
}

转载于:https://www.cnblogs.com/aiwz/p/6333289.html

数据包接收系列 — IP协议处理流程(一)相关推荐

  1. 数据包接收系列 — 数据包的接收过程(宏观整体)

    本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细. 本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个 ...

  2. 从空中截获BLE数据包看蓝牙5协议流程【第四部分:Ellisys蓝牙5连接分析】

    ---------------------------------------------------------------------------------------------------- ...

  3. server2008网卡驱动包_从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造...

    在<在深谈TCP/IP三步握手&四步挥手原理及衍生问题-长文解剖IP>里面提到 单个TCP包每次打包1448字节的数据进行发送(以太网Ethernet最大的数据帧是1518字节,以 ...

  4. 网卡驱动和队列层中的数据包接收

    一.从网卡说起 这并非是一个网卡驱动分析的专门文档,只是对网卡处理数据包的流程进行一个重点的分析.这里以Intel的e100驱动为例进行分析. 大多数网卡都是一个PCI设备,PCI设备都包含了一个标准 ...

  5. UDP数据包接收逻辑的优化修改以及对性能的影响

    UDP数据包接收逻辑的优化修改以及对性能的影响 #include <stdio.h> #include <stdlib.h> #include <unistd.h> ...

  6. 【愚公系列】2022年02月 wireshark系列-数据抓包分析之IP协议

    文章目录 一.题目一 二.题目二 一.题目一 任务描述:使用Wireshark抓取IP数据包以及IP分片数据包 1.使用Wireshark抓取IP数据包 启动Wireshark,Filter选择IP协 ...

  7. #获得请求来源ip_以太网数据包TCP、IP、ICMP、UDP、ARP协议头结构详解

    以太网首部 目地MAC地址(8字节) 源MAC地址(8字节) 类型(2字节) 1.IP头的结构 版本(4位) 头长度(4位) 服务类型(8位) 封包总长度(16位) 封包标识(16位) 标志(3位) ...

  8. IP协议 (通俗易懂),IP协议的主要功能及实现原理,IP地址分类,IP数据包分片,IP数据报格式。

    「作者主页」:士别三日wyx 「作者简介」:CSDN top100.阿里云博客专家.华为云享专家.网络安全领域优质创作者 「专栏简介」:此文章已录入专栏<计算机网络零基础快速入门> 本章重 ...

  9. HDMI原理详解以及时序流程(视频是三对差分信号,音频Audio是PCM级(无压缩)传输,包含在数据包内,依靠协议规定采样)HDMI可以传输RGB与YUV两种格式

    资料来源:HDMI介绍与流程 - TaigaComplex - 博客园 最近要用ZYNQ开发版的HDMI做显示,看着硬件管脚和例程只能发呆,于是决心去弄清楚HDMI的工作原理,查找了很多资料,都是碎片 ...

最新文章

  1. 如何搭建mysql的学习环境_Mysql学习笔记【一、环境安装配置】
  2. 【MongoDB】2、安装MongoDB 2.6.1 on Unbuntu 14.04(学习流水账)
  3. c语言link错误什么原因,C语言 OpenCV错误:“LINK:致命错误LNK1104:无法打开文件’opencv_core231d.lib’”...
  4. 关于压缩工具 7z(7-zip) 的选项 -si(从标准输入流读取数据)的解读
  5. python3字典写入excel_Python玩转Excel(第2期)~这里只有干货
  6. 数据产品-指标体系和埋点设计
  7. vue element ui下拉菜单和不是table列表全选功能问题解决方案
  8. 21个最佳jQuery插件推荐
  9. 单结晶体管的导电特性_室温制备自愈合、可注射PEDOT:PSS导电水凝胶
  10. MATLAB 语言基础知识 矩阵和数组 从矩阵中删除行或列
  11. e算量总是不自动计算机,E算量疑难杂症解决方法.doc
  12. 2020 CCF-CSP-S-第一轮-C++ 模拟试卷(五)--有答案
  13. C++中bad_alloc类
  14. Spring中的IOC和AOP是什么意思?
  15. windows7添加打印机以及共享的步骤
  16. html第四天网站首页的布局设计到实施
  17. PostgreSQL数据库----pgAdmin客户端工具的使用
  18. 防止电脑锁屏html 代码 适合IE下运行
  19. 现在程序员的工资是不是被高估了?
  20. Win10配置adb环境

热门文章

  1. 解决spring-boot-maven-plugin not found爆红
  2. docker部署下的nginx负载均衡时,无法获取真实ip的问题
  3. STM32开发 -- 地球坐标系(WGS84),火星坐标系(GCJ02), 百度坐标系(BD09)坐标转换
  4. hdu1156(简单线段树 模板题)
  5. Consensus explained
  6. Get Cache Info in Linux on ARMv8 64-bit Platform
  7. 【David Silver强化学习公开课】-4:Model-Free Prediction
  8. Android自定义View绘制闪闪发光的文字
  9. c语言形考作业1答案,c语言程序设计形考任务二答案.doc
  10. python 单例模式内存泄露_彻底搞懂Java内存泄露