ip层收包流程概述:

(1) 在inet_init中注册了类型为ETH_P_IP协议的数据包的回调ip_rcv

(2) 当二层数据包接收完毕,会调用netif_receive_skb根据协议进行向上层分发

(3) 类型为ETH_P_IP类型的数据包,被传递到三层,调用ip_rcv函数

(4) ip_rcv完成基本的校验和处理工作后,经过PRE_ROUTING钩子点

(5) 经过PRE_ROUTING钩子点之后,调用ip_rcv_finish完成数据包接收,包括选项处理,路由查询,并且根据路由决定数据包是发往本机还是转发

以下为源码分析:

 static struct packet_type ip_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IP),
.func = ip_rcv,
};
 /*
* Main IP Receive routine.
*/
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
const struct iphdr *iph;
struct net *net;
u32 len; /* When the interface is in promisc. mode, drop all the crap
* that it receives, do not try to analyse it.
*/
/* 混杂模式下,非本机包 */
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop; /* 获取net */
net = dev_net(dev);
__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len); /* 检查skb共享 */
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto out;
} /* 测试是否可以取得ip头 */
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error; /* 取ip头 */
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
*/ /* 头部长度不足20 或者版本不是4 */
if (iph->ihl < || iph->version != )
goto inhdr_error; BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
__IP_ADD_STATS(net,
IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
max_t(unsigned short, , skb_shinfo(skb)->gso_segs)); /* 测试实际应取的ip头 */
if (!pskb_may_pull(skb, iph->ihl*))
goto inhdr_error; /* 取ip头 */
iph = ip_hdr(skb); /* 校验和错误 */
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto csum_error; /* 取总长度 */
len = ntohs(iph->tot_len); /* skb长度比ip包总长度小 */
if (skb->len < len) {
__IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
}
/* 比头部长度还小 */
else if (len < (iph->ihl*))
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).
*/
/* 设置总长度为ip包的长度 */
if (pskb_trim_rcsum(skb, len)) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto drop;
} /* 取得传输层头部 */
skb->transport_header = skb->network_header + iph->ihl*; /* Remove any debris in the socket control block */
/* 重置cb */
memset(IPCB(skb), , sizeof(struct inet_skb_parm)); /* 保存输入设备信息 */
IPCB(skb)->iif = skb->skb_iif; /* Must drop socket now because of tproxy. */
skb_orphan(skb); /* 经过PRE_ROUTING钩子点 */
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish); csum_error:
__IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
 static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
struct net_device *dev = skb->dev;
void (*edemux)(struct sk_buff *skb); /* if ingress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip_rcv(skb);
if (!skb)
return NET_RX_SUCCESS; /*
启用了early_demux
skb路由缓存为空
skb的sock为空
不是分片包
*/
if (net->ipv4.sysctl_ip_early_demux &&
!skb_dst(skb) &&
!skb->sk &&
!ip_is_fragment(iph)) {
const struct net_protocol *ipprot; /* 找到上层协议 */
int protocol = iph->protocol; /* 获取协议对应的prot */
ipprot = rcu_dereference(inet_protos[protocol]); /* 找到early_demux函数,如tcp_v4_early_demux */
if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) { /* 调用该函数,将路由信息缓存到skb->refdst */
edemux(skb);
/* must reload iph, skb->head might have changed */
/* 重新取ip头 */
iph = ip_hdr(skb);
}
} /*
* Initialise the virtual path cache for the packet. It describes
* how the packet travels inside Linux networking.
*/
/* 校验路由失败 */
if (!skb_valid_dst(skb)) {
/* 查路由 */
int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tos, dev);
if (unlikely(err)) {
if (err == -EXDEV)
__NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
goto drop;
}
} #ifdef CONFIG_IP_ROUTE_CLASSID
if (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>>)&0xFF].i_packets++;
st[(idx>>)&0xFF].i_bytes += skb->len;
}
#endif /* 处理ip选项 */
if (iph->ihl > && ip_rcv_options(skb))
goto drop; /* 找到路由缓存项 */
rt = skb_rtable(skb);
if (rt->rt_type == RTN_MULTICAST) {
__IP_UPD_PO_STATS(net, IPSTATS_MIB_INMCAST, skb->len);
} else if (rt->rt_type == RTN_BROADCAST) {
__IP_UPD_PO_STATS(net, IPSTATS_MIB_INBCAST, skb->len);
} else if (skb->pkt_type == PACKET_BROADCAST ||
skb->pkt_type == PACKET_MULTICAST) {
struct in_device *in_dev = __in_dev_get_rcu(dev); /* RFC 1122 3.3.6:
*
* When a host sends a datagram to a link-layer broadcast
* address, the IP destination address MUST be a legal IP
* broadcast or IP multicast address.
*
* A host SHOULD silently discard a datagram that is received
* via a link-layer broadcast (see Section 2.4) but does not
* specify an IP multicast or broadcast destination address.
*
* This doesn't explicitly say L2 *broadcast*, but broadcast is
* in a way a form of multicast and the most common use case for
* this is 802.11 protecting against cross-station spoofing (the
* so-called "hole-196" attack) so do it for both.
*/
if (in_dev &&
IN_DEV_ORCONF(in_dev, DROP_UNICAST_IN_L2_MULTICAST))
goto drop;
} /* 调用路由项的input函数,可能为ip_local_deliver或者ip_forward */
return dst_input(skb); drop:
kfree_skb(skb);
return NET_RX_DROP;
}

ip_rcv && ip_rcv_finish的更多相关文章

  1. Linux内核分析 - 网络[十四]:IP选项

    Linux内核分析 - 网络[十四]:IP选项 标签: linux内核网络structsocketdst 2012-04-25 17:14 5639人阅读 评论(1) 收藏 举报  分类: 内核协议栈 ...

  2. Linux原始套接字实现分析---转

    http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...

  3. 对udp dns的一次思考

    目前昨天查一个线上问题:""dns服务器在我们的设备, 有大量的终端到设备上请求解析域名,但是一直是单线程,dns报文处理不过来", 然而设备是多核,dns服务器一直不能 ...

  4. IP 层收发报文简要剖析1-ip报文的输入

    ip层数据包处理场景如下: 网络层处理数据包文时需要和路由表以及邻居系统打交道.输入数据时,提供输入接口给链路层调用,并调用传输层的输入接口将数据输入到传输层. 在输出数据时,提供输出接口给传输层,并 ...

  5. ip_rcv 中使用skb_share_check

    /* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct pack ...

  6. linux內核輸出soft lockup

    創建的內核線程長期佔用cpu,一直內核認為線程soft lockup,如無法獲取自旋鎖等:因此線程可適度調用schdule(),以進行進程的調度:因為kwatchdog的執行級別低,一直得不到執行 [ ...

  7. (一)洞悉linux下的Netfilter&iptables:什么是Netfilter?

    转自:http://blog.chinaunix.net/uid-23069658-id-3160506.html 本人研究linux的防火墙系统也有一段时间了,由于近来涉及到的工作比较纷杂,久而久之 ...

  8. netfiler源代码分析之框架介绍

    netfiler框架是在内核协议栈实现的基础上完成的,在报文从网口接收,路由等方法实现基础上使用NF_HOOK调用相应的钩子来进入netfiler框架的处理,如 ip_rcv之后会调用NF_HOOK( ...

  9. 理解 Linux 网络栈(1):Linux 网络协议栈简单总结

    本系列文章总结 Linux 网络栈,包括: (1)Linux 网络协议栈总结 (2)非虚拟化Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO (3)QEMU/KVM + Vx ...

随机推荐

  1. Codeforces Round #447 (Div. 2) 题解

    A.很水的题目,3个for循环就可以了 #include <iostream> #include <cstdio> #include <cstring> using ...

  2. [BZOJ3223]文艺平衡树 无旋Treap

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...

  3. 【BZOJ2806】Cheat(后缀自动机,二分答案,动态规划,单调队列)

    [BZOJ2806]Cheat(后缀自动机,二分答案,动态规划,单调队列) 题面 BZOJ 洛谷 题解 很有趣的一道题啊 对于在所有的串上面进行匹配? 很明显的后缀自动机 所以先构建出广义后缀自动机 ...

  4. SID(安全标识符)

    creating a SID for the Everyone group. DWORD SidSize; PSID TheSID; LPTSTR p; SidSize = SECURITY_MAX_ ...

  5. MapReduce(四) 典型编程场景(二)

    一.MapJoin-DistributedCache 应用 1.mapreduce join 介绍 在各种实际业务场景中,按照某个关键字对两份数据进行连接是非常常见的.如果两份数据 都比较小,那么可以 ...

  6. 【bzoj4559】成绩比较

    Portal -->bzoj4559 补档计划 ​  借这题补个档--拉格朗日插值 ​​  插值的话大概就是有一个\(n-1\)次多项式\(A(x)\),你只知道它在\(n\)处的点值,分别是\ ...

  7. 【loj2033】生成魔咒

    Portal --> loj2033 Solution 这题..虽然说好像也是sam的裸题不过既然在智力康复那就强制后缀数组吧qwq ​ (晚点再用sam写一次qwq) 首先如果是要求本质不同的 ...

  8. 《剑指offer》— JavaScript(13)调整数组顺序使奇数位于偶数前面

    调整数组顺序使奇数位于偶数前面 题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的 ...

  9. thinkphp 3.2 部分数据库连贯操作phpstorm helper 文件

    <?php class Helper { /** * 用于设置数据写入和查询是否严格检查是否存在字段. * 默认情况下不合法数据字段自动删除,如果设置了严格检查则会抛出异常 * 如: * str ...

  10. libevent学习文档(二)eventbase相关接口和参数

    Setting up a default event_base The event_base_new() function allocates and returns a new event base ...