【linux网络】ip_rcv_finish()函数

ip_rcv_finish()函数存在于kernel/net/ipv4/ip_input.c文件中

记得在ip_rcv()函数执行最后条用了NF_HOOK,将数据发往netfilter的NF_INET_PRE_ROUTING节点,如下:
NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);

执行完PRE_ROUTING上的钩子函数后,所有钩子都全部返回NF_ACCEPT后,数据包会交由ip_rcv_finish()函数。
这个函数主要功能是做路由选择。

linux-3.10.36中ip_rcv_finish()函数源码如下(夹带中文注释):


static int ip_rcv_finish(struct sk_buff *skb)
{
    const struct iphdr *iph = ip_hdr(skb);
    struct rtable *rt;

    if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;

        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot && ipprot->early_demux) {
            ipprot->early_demux(skb);
            /* must reload iph, skb->head might have changed */
            iph = ip_hdr(skb);
        }
    }

    /* * 为数据包初始化虚拟路径缓存,它描述了数据包是如何在linux网络中传播的 */
     //noted:通常从外界接收的数据包,skb->dst不会包含路由信息,暂时还不知道在何处会设置这个字段
     //ip_route_input函数会根据路由表设置路由信息
    if (!skb_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                           iph->tos, skb->dev);
        if (unlikely(err)) {
            if (err == -EXDEV)
                NET_INC_STATS_BH(dev_net(skb->dev),
                         LINUX_MIB_IPRPFILTER);
            goto drop;
        }
    }

//noted:更新统计数据
#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

    //如果IP头部大于20字节,则表示IP头部包含IP选项,需要进行选项处理
    if (iph->ihl > 5 && ip_rcv_options(skb))
        goto drop;

    //noted: skb_rtable函数等同于skb_dst函数,获取skb->dst
    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);
    //noted: dst_input实际上会调用skb->dst->input(skb).input函数会根据路由信息设置为合适的
    //函数指针,如果是递交到本地的则为ip_local_deliver,若是转发则为ip_forward.
    return dst_input(skb);

drop:
    kfree_skb(skb);
    return NET_RX_DROP;
}

你可能感兴趣的:(linux,TCP-IP协议栈)