ip_rcv 分析

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 17:48   2030   0

ip_rcv 分析

2013-04-13 11:40:00| 分类: linux-NET |举报 |字号 订阅

/*
* 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;
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;
//pkt_type表示报文类型 。PACKET_OTHERHOST表示非去往本机但是在特定模式下被接受的报文
//此处的作用是根据包类型,来丢弃并非去往本地的包


IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
/*内核对于一些统计,都是采用这种方法来做的,算是一种标准方式了。你列的那些信息是,是关于网络层snmp统计的信息,也可以通过netstat 指令看到这些统计值。关于这些统计的意义,你可以查看相应的宏定义,都有详细的注释,例如:

IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */


而关于宏的实现,也可以跟一下,大致就是找到结构的某个成员,然后更新它的值,这种统计,一般都是Per-CPU变量:

IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);

#define IP_UPD_PO_STATS_BH(net, field, val) SNMP_UPD_PO_STATS_BH((net)->mib.ip_statistics, field, val)

#define SNMP_UPD_PO_STATS_BH(mib, basefield, addend) \
do { \
__typeof__(mib[0]) ptr = per_cpu_ptr(mib[!in_softirq()], raw_smp_processor_id());\
ptr->mibs[basefield##PKTS]++; \
ptr->mibs[basefield##OCTETS] += addend;\
} while (0)
*/

if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto out;
}
/// 如果数据报是共享的,则复制一个出来,此时复制出的sk_buff已经和socket脱离了关系
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
*/

if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;

if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;

iph = ip_hdr(skb);
//#define likely(x) __builtin_expect(!!(x), 1)也就是说明x==1是“经常发生的”或是“很可能发生的”。
//使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面, 使用unlikely ,执行else后面语句的可能性大些,编译器将else{}
//里的内容编译到前面。这样有利于cpu预取,提高预取指令的正确率,因而可提高效率。

if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto inhdr_error;

//取出ip头中标明的数据包长度,并且和收到的数据包长度相比较

len = ntohs(iph->tot_len);
if (skb->len < len) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;/ / //整个报文长度不可能比报头长度小
} 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).
*/

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);

return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish); //在内核收到一个发往本机的数据包,会判断PF_INET 协议的NF_IP_PRE_ROUTINGHOOK类型下是否注册有钩子函数,如果没有,则直接继续执行ip_rcv_finish,如果有,则调用nf_hook_slow函数,进而进一步调用已注册的钩子函数,再根据其返回值,看是否还需要继续执行ip_rcv_finish。

inhdr_error:
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP