ip_rcv && ip_rcv_finish

(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完成数据包接收,包括选项处理,路由查询,并且根据路由决定数据包是发往本机还是转发

1 static struct packet_type ip_packet_type __read_mostly = {
2     .type = cpu_to_be16(ETH_P_IP),
3     .func = ip_rcv,
4 };
  1 /*
  2  *     Main IP Receive routine.
  3  */
  4 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
  5 {
  6     const struct iphdr *iph;
  7     struct net *net;
  8     u32 len;
  9
 10     /* When the interface is in promisc. mode, drop all the crap
 11      * that it receives, do not try to analyse it.
 12      */
 13     /* 混杂模式下,非本机包 */
 14     if (skb->pkt_type == PACKET_OTHERHOST)
 15         goto drop;
 16
 17
 18     /* 获取net */
 19     net = dev_net(dev);
 20     __IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
 21
 22     /* 检查skb共享 */
 23     skb = skb_share_check(skb, GFP_ATOMIC);
 24     if (!skb) {
 25         __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
 26         goto out;
 27     }
 28
 29     /* 测试是否可以取得ip头 */
 30     if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 31         goto inhdr_error;
 32
 33     /* 取ip头 */
 34     iph = ip_hdr(skb);
 35
 36     /*
 37      *    RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum.
 38      *
 39      *    Is the datagram acceptable?
 40      *
 41      *    1.    Length at least the size of an ip header
 42      *    2.    Version of 4
 43      *    3.    Checksums correctly. [Speed optimisation for later, skip loopback checksums]
 44      *    4.    Doesn‘t have a bogus length
 45      */
 46
 47     /* 头部长度不足20 或者版本不是4 */
 48     if (iph->ihl < 5 || iph->version != 4)
 49         goto inhdr_error;
 50
 51     BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
 52     BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
 53     BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
 54     __IP_ADD_STATS(net,
 55                IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
 56                max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
 57
 58     /* 测试实际应取的ip头 */
 59     if (!pskb_may_pull(skb, iph->ihl*4))
 60         goto inhdr_error;
 61
 62     /* 取ip头 */
 63     iph = ip_hdr(skb);
 64
 65     /* 校验和错误 */
 66     if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
 67         goto csum_error;
 68
 69     /* 取总长度 */
 70     len = ntohs(iph->tot_len);
 71
 72     /* skb长度比ip包总长度小 */
 73     if (skb->len < len) {
 74         __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
 75         goto drop;
 76     }
 77     /* 比头部长度还小 */
 78     else if (len < (iph->ihl*4))
 79         goto inhdr_error;
 80
 81     /* Our transport medium may have padded the buffer out. Now we know it
 82      * is IP we can trim to the true length of the frame.
 83      * Note this now means skb->len holds ntohs(iph->tot_len).
 84      */
 85     /* 设置总长度为ip包的长度 */
 86     if (pskb_trim_rcsum(skb, len)) {
 87         __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
 88         goto drop;
 89     }
 90
 91     /* 取得传输层头部 */
 92     skb->transport_header = skb->network_header + iph->ihl*4;
 93
 94     /* Remove any debris in the socket control block */
 95     /* 重置cb */
 96     memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
 97
 98     /* 保存输入设备信息 */
 99     IPCB(skb)->iif = skb->skb_iif;
100
101     /* Must drop socket now because of tproxy. */
102     skb_orphan(skb);
103
104     /* 经过PRE_ROUTING钩子点 */
105     return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
106                net, NULL, skb, dev, NULL,
107                ip_rcv_finish);
108
109 csum_error:
110     __IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
111 inhdr_error:
112     __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
113 drop:
114     kfree_skb(skb);
115 out:
116     return NET_RX_DROP;
117 }
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>>16)&0xFF].i_packets++;
        st[(idx>>16)&0xFF].i_bytes += skb->len;
    }
#endif

    /* 处理ip选项 */
    if (iph->ihl > 5 && 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;
}
时间: 2024-10-30 02:14:43

ip_rcv && ip_rcv_finish的相关文章

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

Linux内核分析 - 网络[十四]:IP选项 标签: linux内核网络structsocketdst 2012-04-25 17:14 5639人阅读 评论(1) 收藏 举报  分类: 内核协议栈(22)  版权声明:本文为博主原创文章,未经博主允许不得转载. 内核版本:2.6.34      在发送报文时,可以调用函数setsockopt()来设置相应的选项,本文主要分析IP选项的生成,发送以及接收所执行的流程,选取了LSRR为例子进行说明,主要分为选项的生成.选项的转发.选项的接收三部分

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

ip层数据包处理场景如下: 网络层处理数据包文时需要和路由表以及邻居系统打交道.输入数据时,提供输入接口给链路层调用,并调用传输层的输入接口将数据输入到传输层. 在输出数据时,提供输出接口给传输层,并调用链路层的输出接口将数据输出到链路层.在输入输出数据时,需要查找路由表 通过netfiler处理等操作. 一.ip数据报文输入 ip_rcv &ip_rcv_finish &ip_rcv_finish2 1.在inet_init中注册了类型为ETH_P_IP协议的数据包的回调ip_rcv 2

iptables/Netfilter 学习

开始学iptables,因为它是和路由器技术紧密结合在一起的. iptables的命令看起来眼花缭乱,随便找两个: iptables -A FORWARD -p tcp -s 192.168.1.0/24 -d 192.168.1.234 --dport 80 -j ACCEPT iptables -A FORWARD -f -p tcp -s 192.168.1.0/24 -d 192.168.1.234 --dport 80 -j ACCEPT 看了一些博客,还是云里雾里的,所以开始看内核里

Netfilter总览

NETFILTER NETFILTER的使用 "栈"模式底层机制基本就是像下面这个样子: 对于收到的每个数据包,都从"A"点进来,经过路由判决,如果是发送给本机的就经过"B"点,然后往协议栈的上层继续传递:否则,如果该数据包的目的地是不本机(数据包的目标L3不是本机IP),那么就经过"C"点,然后顺着"E"点将该包转发出去. 对于发送的每个数据包,首先也有一个路由判决,以确定该包是从哪个接口出去,然后经过&

深入理解Linux网络技术内幕——IPv4 报文的接收(转发与本地传递)

我们知道,报文经过网卡驱动处理后,调用net_receive_skb传递给具体的协议处理函数,对于IPv4报文来说,其协议处理函数就是ip_rcv了,ip_rcv在进行一些健康检查等操作后,会调用ip_rcv_finish来处理报文.这也是IPv4协议对报文接收处理的开始. 我们先看下ip_rcv_finish源代码: ip_rcv_finish: //ip数据报文的主要处理程序(ip_rcv仅仅只是对ip数据报做一些健康性检查) //ip_rcv_finish 其实是进行路由表查询,,决定报文

洞悉linux下的Netfilter&amp;iptables:什么是Netfilter?

本人研究linux的防火墙系统也有一段时间了,由于近来涉及到的工作比较纷杂,久而久之怕生熟了.趁有时间,好好把这方面的东西总结一番.一来是给自己做个沉淀,二来也欢迎这方面比较牛的前辈给小弟予以指点,共同学习,共同进步. 能在CU上混的人绝非等闲之辈.因此,小弟这里说明一下:本系列博文主要侧重于分析Netfilter的实现机制,原理和设计思想层面的东西,同时从用户态的iptables到内核态的Netfilter其交互过程和通信手段等.至于iptables的入门用法方面的东西,网上随便一搜罗就有一大

Linux内核2.4.x的网络接口源码的结构[转]

http://blog.csdn.net/wswifth/article/details/5102242 一.前言 Linux的源码里,网络接口的实现部份是非常值得一读的,通过读源码,不仅对网络协议会有更深的了解,也有助于在网络编程的时候,对应用函数有更精确的了解和把握. 本文把重点放在网络接口程序的总体结构上,希望能作为读源码时一些指导性的文字. 本文以Linux2.4.16内核作为讲解的对象,内核源码可以在http://www.kernel.org上下载.我读源码时参考的是http://lx

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

本系列文章总结 Linux 网络栈,包括: (1)Linux 网络协议栈总结 (2)非虚拟化Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO (3)QEMU/KVM虚拟化 Linux 环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO 1. Linux 网络路径 1.1 发送端 1.1.1 应用层 (1) Socket 应用层的各种网络应用程序基本上都是通过 Linux Socket 编程接口来和内核空间的网络协议栈通信的.Linux Socket 是从

初探Linux网络协议栈

一点声明原文链接: http://www.ecsl.cs.sunysb.edu/elibrary/linux/network/LinuxKernel.pdf 译者注: 原文写于2003年,文中描述的不少内容已经发生了改变,在不影响愿意的情况下,我擅自增删了一些内容. 翻译过程中找到的好资料: How SKBs Work Evaluation of TCP retransmission delays Congestion Control in Linux TCP Anatomy of the Li