网络收包流程-网络层处理流程ip_rcv(五)

报文提交给内核协议栈处理后,最终会调用到__netif_receive_skb_core函数,如果报文没有被网桥处理函数rx_handler消费掉,最终会交给ptype_base中注册的协议处理,包括内核注册的协议,也包括raw socket等创建的协议处理。本文将分析普通ipv4    报文的处理过程,处理入口函数为ip_rcv函数。
主要调用流程:ip_rcv-->ip_rcv_finish-->ip_local_deliver-->ip_local_deliver_finish
分组向上穿过内核的路线如图所示:

主要作用:
(1)类型为ETH_P_IP类型的数据包,被传递到三层,调用ip_rcv函数
  (2)  ip_rcv完成基本的校验( 主要检查计算的校验和与首部中存储的校验和是否一致)和处理工作后,经过PRE_ROUTING钩子点
  (3) 经过PRE_ROUTING钩子点之后,调用ip_rcv_finish完成数据包接收,包括选项处理,路由查询,并且根据路由决定数据包是发往本机还是转发

1.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;
        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;
 
 
        IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
 
        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {//检查是否skb为share,是 则克隆报文
                IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
                goto out;
        }
 
        if (!pskb_may_pull(skb, sizeof(struct iphdr)))//确保skb还可以容纳标准的报头(即20字节)
                goto inhdr_error;
 
        iph = ip_hdr(skb);//得到IP头
 
        /*
         *      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)//ip头长度至少为20字节(ihl>=5,后面计算头长度会乘4),只支持v4
                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_BH(dev_net(dev),
                        IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
                        max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
 
        if (!pskb_may_pull(skb, iph->ihl*4))//确保skb还可以容纳实际的报头(ihl*4)
                goto inhdr_error;
 
        iph = ip_hdr(skb);
 
        if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))//ip头csum校验
                goto csum_error;
 
        len = ntohs(iph->tot_len);//获取ip分组总长,即ip首部加数据的长度
        if (skb->len < len) {//skb的实际总长度小于ip分组总长,则drop
                IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        } else if (len < (iph->ihl*4))//ip头记录的分组长度就大于数据总长,则出错
                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;
        } 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;
        }
 
        skb->transport_header = skb->network_header + iph->ihl*4;//设置传输层header
 
        /* Remove any debris in the socket control block */
        memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));//清空cb,即inet_skb_parm值
 
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
        //调用netfilter,实现iptables功能,通过后调用ip_rcv_finish
        return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
                       dev, NULL,
                       ip_rcv_finish);
 
csum_error:
        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_CSUMERRORS);
inhdr_error:
        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
drop:
        kfree_skb(skb);
out:
        return NET_RX_DROP;
}
2.ip_rcv_finish函数
作用:
1)、确定数据包是转发还是在本机协议栈上传,如果是转发要确定输出网络设备和下一个接受栈的地址。
2)、解析和处理部分IP选项。

 
static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
{
        const struct iphdr *iph = ip_hdr(skb);
        struct rtable *rt;
        int err;
 
        /* 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;
 
        if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
                const struct net_protocol *ipprot;
                int protocol = iph->protocol;//得到传输层协议
            /* 找到early_demux函数,如是tcp协议就调用,tcp_v4_early_demux */
                ipprot = rcu_dereference(inet_protos[protocol]);
                if (ipprot && ipprot->early_demux) {//对于socket报文,可以通过socket快速获取路由表
                        err = ipprot->early_demux(skb);/* 调用该函数,将路由信息缓存到_skb->refdst */
 
          if (unlikely(err))
                                goto drop_error;
                        /* must reload iph, skb->head might have changed */
                        iph = ip_hdr(skb);//重新获取ip头
                }
        }
    
        /*
         *      Initialise the virtual path cache for the packet. It describes
         *      how the packet travels inside Linux networking.
         */
        /* 1.  为数据包初始化虚拟路径缓存,它描述了数据包是如何在linux网络中传播的 ;
            2.  通常从外界接收的数据包,skb->dst不会包含路由信息,暂时还不知道在何处会设置这个字段;
            3.  skb->dst该数据域包含了如何到达目的地址的路由信息,如果该数据域是NULL,就通过路由子系统函数ip_route_input_noref路由,ip_route_input_noref的输入参数有源IP地址、目的IP地址、服务类型、接受数据包的网络设备,根据这5个参数决策路由。*/
        if (!skb_valid_dst(skb)) {
            // 路由查询,决定后续处理:向上传递( ip_local_deliver)、转发(ip_forward)、丢弃
                err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                                           iph->tos, skb->dev);
                if (unlikely(err))
                        goto drop_error;
        }
 
#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>>16)&0xFF].i_packets++;
                st[(idx>>16)&0xFF].i_bytes += skb->len;
        }
#endif
 
        if (iph->ihl > 5 && ip_rcv_options(skb))
                goto drop;
 
        rt = skb_rtable(skb);//得到路由表项,统计组播和广播报文
        if (rt->rt_type == RTN_MULTICAST) {
                IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,
                             skb->len);
        } else if (rt->rt_type == RTN_BROADCAST)
                IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
                                skb->len);
 
        return dst_input(skb);/*ip_rcv_finish的结束是调用了dst_input,实际是调用存放在skb->dst->input的数据域。该函数确定了下一步对数据包的处理,根据数据包的目的地地址,skb->dst->input字段的信息主要由路由处理流程确定,可能是往本地协议栈上传就调用 ip_local_deliver,如果是转发就调用ip_forward */
 
drop:
        kfree_skb(skb);
        return NET_RX_DROP;
 
drop_error:
        if (err == -EXDEV)
                NET_INC_STATS_BH(dev_net(skb->dev), LINUX_MIB_IPRPFILTER);
        goto drop;

}

————————————————
版权声明:本文为CSDN博主「菜鸟别浪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hzj_001/article/details/104950605

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值