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

内核版本:2.6.37

Author:zhangskd @ csdn blog

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

ip_local_deliver

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

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

则调用ip_local_deliver_finish()继续处理。

/* Deliver IP Packets to the higher protocol layers. */

int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
* 如果IP数据报有被分片,则在这里进行组装还原。
*/
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
} /* 调用netfilter的NF_INET_LOCAL_IN的钩子函数,如果此数据包被钩子函数放行,则调用
* ip_local_deliver_finish()继续处理。
*/
return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}

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()。

static int ip_local_deliver_finish(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev); /* 把skb->data指向L4协议头,更新skb->len */
__skb_pull(skb, ip_hdrlen(skb)); /* 赋值skb->transport_header */
skb_reset_transport_header(skb); rcu_read_lock();
{
int protocol = ip_hdr(skb)->protocol; /* L4协议号 */
int hash, raw;
const struct net_protocol *ipprot; resubmit:
/* 处理RAW IP */
raw = raw_local_deliver(skb, protocol); hash = protocol & (MAX_INET_PROTOS - 1); /* 作为数组索引 */ /* 从inet_protos数组中取出对应的net_protocol元素,TCP的为tcp_protocol */
ipprot = rcu_dereference(inet_protos[hash]); if (ipprot != NULL) {
int ret; if (! net_eq(net, &init_net) && ! ipprot->netns_ok) {
if (net_ratelimit())
printk("%s: proto %d isn't netns-ready\n", __func__, protocol);
kfree_skb(skb);
goto out;
} /* 如果需要检查IPsec安全策略 */
if (! ipprot->no_policy) {
if (! xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
goto out;
}
nf_reset(skb);
} /* 调用L4协议的处理函数,对于TCP,调用tcp_protocol->handler,为tcp_v4_rcv() */
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = - ret;
goto resubmit;
}
IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS); } else {
if (! raw) {
if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
} else
IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS); kfree_skb(skb);
}
} out:
rcu_read_unlock();
return 0;
}

L4协议处理函数

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

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

/* This is used to register protocols. */
struct net_protocol {
int (*handler) (struct sk_buff *skb); /* L4协议的接收函数 */
void (*error_handler) (struct sk_buff *skb, u32 info); /* ICMP用的 */
...
unsigned int no_policy:1, /* 不需要检查IPsec策略 */
netns_ok:1;
}; #define MAX_INET_PROTOS 256 /* MUST be a power of 2 */

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

const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
enum {
...
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
...
IPPROTO_TCP = 6, /* Transmission Control Protocol */
...
IPPROTO_UDP = 17, /* User Datagram Protocol */
...
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
}; /* TCP协议的net_protocol */
static const struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
...
.no_policy = 1,
.netns_ok = 1,
}; /* UDP协议的net_protocol */
static const struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = upd_err,
...
.no_policy = 1,
.netns_ok = 1,
}; /* ICMP协议的net_protocl */
static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.no_policy = 1,
.netns_ok = 1,
};

初始化

static int __init inet_init(void)
{
...
/* Add all the base protocols */
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n"); if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n"); if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
...
}

RAW IP

struct raw_hashinfo {
rwlock_t lock;
struct hlist_head ht[RAW_HTABLE_SIZE];
}; static struct raw_hashinfo raw_v4_hashinfo = {
.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock);
};
#define RAW_HTABLE_SIZE MAX_INET_PROTOS

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

int raw_local_deliver(struct sk_buff *skb, int protocol)
{
int hash;
struct sock *raw_sk; hash = protocol & (RAW_HTABLE_SIZE - 1); /* L4协议号作为key */
raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); /* 取出哈希桶的第一个sock */ /* If there maybe a raw socket we must check - if not we don't care less.
* raw_v4_input(): IP input processing comes here for RAW socket delivery.
* 如果该协议上有连接,那么调用raw_v4_input()来处理。
*/
if (raw_sk && ! raw_v4_input(skb, ip_hdr(skb), hash))
raw_sk = NULL; return raw_sk != NULL;
}

数据包接收系列 — IP协议处理流程(二)的更多相关文章

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

    本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog IP报头 IP报头: struct iphdr { #if defi ...

  2. 数据包接收系列 — NAPI的原理和实现

    本文主要内容:简单分析NAPI的原理和实现. 内核版本:2.6.37 Author:zhangskd @ csdn 概述 NAPI是linux新的网卡数据处理API,据说是由于找不到更好的名字,所以就 ...

  3. Linux内核二层数据包接收流程

    本文主要讲解了Linux内核二层数据包接收流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式从内核高层面上梳理了二层数据包接收的流程,希望可以对大家有所帮助.阅 ...

  4. 从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造

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

  5. wireshark抓包分析——TCP/IP协议

    本文来自网易云社区 当我们需要跟踪网络有关的信息时,经常会说"抓包".这里抓包究竟是什么?抓到的包又能分析出什么?在本文中以TCP/IP协议为例,简单介绍TCP/IP协议以及如何通 ...

  6. linux 内核网络数据包接收流程

    转:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面 ...

  7. 抓包整理————ip 协议一[十二]

    前言 简单介绍一下ip协议. 正文 先来看下ip协议在网络层的哪一层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 ip 层就在网络层: 其实很好想象哈,就是因为每台机器起码有一个ip ...

  8. IP协议解读(二)

    IP协议是TCP协议栈中的核心协议,也是网络编程的基础之中的一个. 我们接着在IP协议解读(一)继续学习 网络层作用 IP分片: IP数据报的长度超过帧的MTU时,将会被分片传输. 分片可能发生在发送 ...

  9. [转帖]IP /TCP协议及握手过程和数据包格式中级详解

    IP /TCP协议及握手过程和数据包格式中级详解 https://www.toutiao.com/a6665292902458982926/ 写的挺好的 其实 一直没闹明白 网络好 广播地址 还有 网 ...

随机推荐

  1. CSDN 支持Markdown写文章了!

    开源中国等其他技术博客很早就支持markdown格式写文章了,今天发现csdn竟然也可以了,不仅支持而且可以在线预览,本地导入导出,远程导入. 这些对于程序员写东西都非常好用,不用总是花时间来排版了. ...

  2. 2.关于QT中的Dialog(模态窗口),文件选择器,颜色选择器,字体选择器,消息提示窗口

     1 新建一个空项目 A 编写 .pro文件 QT += gui widgets HEADERS += \ MyDialog.h SOURCES += \ MyDialog.cpp B 编写MyD ...

  3. 与音频相关的技术知识点总结(Linux方向的开发)

    几个术语和概念: 1.       关于PCM的 PCM是Pulse code modulation的缩写,它是对波形最直接的编码方式.它在音频中的地位可能和BMP在图片中的地位有点类似吧. Samp ...

  4. octave installation on RHEL6.4

    octave installation on RHEL6.4 rhel6.4上安装octave GNU Octave 是一种高级语言,主要设计用来进行数值计算,它是 MathWorks 出品的 Mat ...

  5. Dynamics CRM2015 2015版本可用的OData Query Designer工具

    2015后很多工具无法使用,包括2011版的OData Query Designer,这里介绍一款可用的工具,Dynamics XRM Tools for CRM 2015,下载地址:https:// ...

  6. Hessian源码分析--HessianServlet

    Hessian可以通过Servlet来对外暴露服务,HessianServlet继承于HttpServlet,但这仅仅是一个外壳,使用web服务器来提供对外的Http请求,在web.xml中我们会进行 ...

  7. Dynamics CRM Microsoft SQL Server 指定的数据库具有更高的版本

    在做NLB部署时遇到这么个问题,CRMAPP1安装的CRM版本是6.1已经打了SP1补丁,而在CRMAPP2上的CRM安装包是6.0版本,在选择连接现有部署后,最后一步检测就出了问题,如下图所示. 看 ...

  8. 一台电脑上同启动两个Tomcat的方式,windows/Linux配置。

     安装两个jdk,一个JDK路径在:C:\ProgramFiles (x86)\Java\jdk1.7.0_25,另外一个JDK的路径在E:\UCMSServer\j2sdk 在环境变量里并设置J ...

  9. 方便使用FFMPEG的经验

    FFMPEG是命令行工具,因此使用起来多少还是会有些不方便.在这记录两点方便使用FFMPEG的方法: 1.任何目录下都可以使用FFMPEG 问题描述:需要转码(播放)的时候,需要把ffmpeg.exe ...

  10. iOS中 UICollectionView UI_19

    UICollectionView 是UITableView加强版 UITableView 和UICollectionView的设计思想: 1.布局: UITableView 的布局可以由UITable ...