本文主要内容:在接收数据包时,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. Android 使用DownloadManager进行版本更新的完整方案

    在Android App都会有版本更新的功能,以前我们公司是用友盟SDK更新功能,自己服务器没有这样的功能.版本检测.Apk下载都是使用友盟.最近看到友盟的版本更新SDK文档:十月份更新功能将会停止服 ...

  2. 解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题

    对于这个问题,我相信我的方法已经能帮90%的人解决了! 当你用Eclipse运行web项目的时候,你就会看到控制台出现: WARNING: [SetPropertiesRule]{Server/Ser ...

  3. Android使用HttpUrlConnection请求服务器发送数据详解

    HttpUrlConnection是java内置的api,在java.net包下,那么,它请求网络同样也有get请求和post请求两种方式.最常用的Http请求无非是get和post,get请求可以获 ...

  4. Android项目开发填坑记-9patchPng报错

    如果阅读体验不佳,请使用–> Github版 背景 之前写了一篇文章Android必知必会–NinePatch图片制作详细介绍了Android 9Patch图片的制作和一些Demo展示,这次说明 ...

  5. Android开发中StackOverflowError

    Android开发中StackOverflowError错误实例分析 一.概述 我在一个复杂的layout嵌套较多的android界面,碰到了java.lang.StackOverflowError这 ...

  6. FFmpeg示例程序合集-批量编译脚本

    此前做了一系列有关FFmpeg的示例程序,组成了<最简单的FFmpeg示例程序合集>,其中包含了如下项目:simplest ffmpeg player:                   ...

  7. 开源项目——小Q聊天机器人V1.4

    小Q聊天机器人V1.0 http://blog.csdn.net/baiyuliang2013/article/details/51386281 小Q聊天机器人V1.1 http://blog.csd ...

  8. Maven原型骨架及常见问题

    关于Maven原型工程的制作就不在这里详细介绍了,具体细节请参考之前的文章:定制Maven原型生成项目 下面分享制作和使用Maven原型工程时碰到的常见问题,以及原型的上传和使用方法. 1.模块路径问 ...

  9. Java之恋

    初次见面那是一个河北的夏天风随沙散落天涯蝴蝶依旧恋着花回首走过的日子手指和键盘之间的梦想之光已恍如昨日 那年我还是一个刚踏进这个曾经只在地理课本上狂念南稻北麦,南油北花的土地那年你只是我必须要学的编程 ...

  10. 【一天一道LeetCode】#160. Intersection of Two Linked Lists

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...