IP层输入

//在inet_init时,已经使用dev_add_pack(&ip_packet_type)向ptype_base中注册二层负载
//IPv4报文处理回调,当netif_receive_skb进行二层包处理时,会遍历所有ptype_base列
//表,找到对应的三层协议,并调用回调进行处理。这里ip_rcv就是IPV4的接收处理回调
ip_rcv
//如果目地MAC不是自身,则将包丢弃,不是自身通常都是从二层桥转发。
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;

//如果skb是共享的,则先复制一份,复制失败则丢弃。
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto out;

//检测当前skb的头部是否有ip头大小的有效空间。
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;

iph = skb->nh.iph;

//检测ip头长度是否小于最少20字节,或者不是ipv4版本
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;

//检测当前skb的头部是否有ip整个头(可能含ip选项)的有效空间。
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;

iph = skb->nh.iph;

//进行ip校验和检测
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto inhdr_error;

//检测整个报文是否小于ip头记载的总长度,或者总长度小于IP头长度
len = ntohs(iph->tot_len);
if (skb->len < len || len < (iph->ihl*4))
goto inhdr_error;

//在二层传输时,很可能为了适应二层媒介(比如以太网帧最小必须是64字节)而
//填充无效信息。这里检测是否含有填充信息,如果含有则修正skb的tail指向有效
//数据的位置,跳过无效的填充。
if (pskb_trim_rcsum(skb, len))
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto drop;

//将sky的cb成员清空。
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));

//进行netfileter的PRE_ROUTING链处理,这里不分析netfileter,假设没有被
//netfilter拦截,则会触发ip_rcv_finish调用。
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish);

--------------------------------------------------------------------------------------------------------------------
ip_rcv_finish
//没有目地对象,先进行路由模块查询,这里暂时先不去了解路由模块的处理机制
//仅需要知道,如果路由模块判断出当前报文是本地报文,则将设置本地接收回调
//为rth->u.dst.input= ip_local_deliver;
if (skb->dst == NULL)
ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,skb->dev);

//如果ip头长度大于20字节,则表示有ip选项,则进行ip选项处理。
if (iph->ihl > 5 && ip_rcv_options(skb))
goto drop;

//调用skb->dst->input回调进行本地接收处理,这个回调就是在上面ip_route_input中
//设置的。如果回调函数的返回值为NET_XMIT_BYPASS,则循环接收处理。
dst_input(skb);
for (;;)
err = skb->dst->input(skb);

if (likely(err == 0))
return err;

if (unlikely(err != NET_XMIT_BYPASS))
return err;
----------------------------------------------------------------------------------------------------------------------
ip_local_deliver
//如果当前ip报是分片,则需要进行组装,在没有收到所有分片时,直接返回,ip
//分片及组装待后面单独分析。
if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET))
skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)

if (!skb)
return 0;

//进行netfileter的LOCAL_IN链处理,这里不分析netfileter,假设没有被netfilter拦截,
//则会触发ip_local_deliver_finish调用。

NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish);

---------------------------------------------------------------------------------------------------------------------
ip_local_deliver_finish
//去除ip头,将skb->data指向ip负载。
__skb_pull(skb, ihl);

skb->h.raw = skb->data;

//ip负载的协议号
int protocol = skb->nh.iph->protocol;

//查找是否有原始套接口需要处理此协议报文
hash = protocol & (MAX_INET_PROTOS - 1);
raw_sk = sk_head(&raw_v4_htable[hash]);

//如果存在原始套接口在监听该协议报文,则调用原始套接口进行处理
if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash))
raw_sk = NULL;

//检测当前内核是否注册了对应的四层协议处理对象,比如UDP协议是在
//inet_init中使用inet_add_protocol(&udp_protocol, IPPROTO_UDP)加入了UDP
//处理回调。
if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL)
//如果对应协议设置需要安全策略标记,则使用xfrm4_policy_check进行安全策略
//处理,处理失败则将报文丢弃。暂不分析。
if (!ipprot->no_policy)
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
kfree_skb(skb);
goto out;
nf_reset(skb);

//调用对应的四层协议处理函数进行处理。暂不分析。
ipprot->handler(skb);
else
//如果四层协议没有对应的内核模块处理,同时也没有原始套接口监听,则发送
//ICMP错误。
if (!raw_sk)
if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
icmp_send(skb, ICMP_DEST_UNREACH,ICMP_PROT_UNREACH, 0);

kfree_skb(skb);

你可能感兴趣的:(IP层输入)