前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >网卡收到一个数据包的时候,是如何传给应用层的(下)

网卡收到一个数据包的时候,是如何传给应用层的(下)

作者头像
theanarkh
发布2019-03-06 10:04:24
1K0
发布2019-03-06 10:04:24
举报
文章被收录于专栏:原创分享原创分享

上篇讲到mac头通过mac头的协议字段决定把数据包分发给上层的哪个协议。这里假设上层协议是ip,ip层处理函数是ip_rcv,代码如下

代码语言:javascript
复制
/*
 *    This function receives all incoming IP datagrams.
 */

int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
    struct iphdr *iph = skb->h.iph;
    struct sock *raw_sk=NULL;
    unsigned char hash;
    unsigned char flag = 0;
    unsigned char opts_p = 0;   /* Set iff the packet has options. */
    struct inet_protocol *ipprot;
    static struct options opt; /* since we don't use these yet, and they
                take up stack space. */
    int brd=IS_MYADDR;
    int is_frag=0;
#ifdef CONFIG_IP_FIREWALL
    int err;
#endif    

    ip_statistics.IpInReceives++;

    /*
     *  Tag the ip header of this packet so we can find it
     */

    skb->ip_hdr = iph;

    /*
     *  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. We ought to check for IP multicast addresses and undefined types.. does this matter ?)
     */
    // 参数检查
    if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 ||
        skb->len<ntohs(iph->tot_len) || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0)
    {
        ip_statistics.IpInHdrErrors++;
        kfree_skb(skb, FREE_WRITE);
        return(0);
    }

    /*
     *  See if the firewall wants to dispose of the packet. 
     */
// 配置了防火墙,则先检查是否符合防火墙的过滤规则,否则则丢掉
#ifdef    CONFIG_IP_FIREWALL

    if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))!=1)
    {
        if(err==-1)
            icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
        kfree_skb(skb, FREE_WRITE);
        return 0;   
    }

#endif

    /*
     *  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.
     */

    skb->len=ntohs(iph->tot_len);

    /*
     *  Next analyse the packet for options. Studies show under one packet in
     *  a thousand have options....
     */
    // ip头超过20字节,说明有选项
    if (iph->ihl != 5)
    {   /* Fast path for the typical optionless IP packet. */
        memset((char *) &opt, 0, sizeof(opt));
        if (do_options(iph, &opt) != 0)
            return 0;
        opts_p = 1;
    }

    /*
     *  Remember if the frame is fragmented.
     */
    // 非0则说明是分片 
    if(iph->frag_off)
    {   
        // 是否禁止分片,是的话is_frag等于1
        if (iph->frag_off & 0x0020)
            is_frag|=1;
        /*
         *  Last fragment ?
         */
        // 非0说明有偏移,即不是第一个块分片
        if (ntohs(iph->frag_off) & 0x1fff)
            is_frag|=2;
    }

    /*
     *  Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
     *
     *  This is inefficient. While finding out if it is for us we could also compute
     *  the routing table entry. This is where the great unified cache theory comes
     *  in as and when someone implements it
     *
     *  For most hosts over 99% of packets match the first conditional
     *  and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
     *  function entry.
     */

    if ( iph->daddr != skb->dev->pa_addr && (brd = ip_chk_addr(iph->daddr)) == 0)
    {
        /*
         *  Don't forward multicast or broadcast frames.
         */

        if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
        {
            kfree_skb(skb,FREE_WRITE);
            return 0;
        }

        /*
         *  The packet is for another target. Forward the frame
         */

#ifdef CONFIG_IP_FORWARD
        ip_forward(skb, dev, is_frag);
#else
/*        printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
            iph->saddr,iph->daddr);*/
        ip_statistics.IpInAddrErrors++;
#endif
        /*
         *  The forwarder is inefficient and copies the packet. We
         *  free the original now.
         */

        kfree_skb(skb, FREE_WRITE);
        return(0);
    }

#ifdef CONFIG_IP_MULTICAST    

    if(brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
    {
        /*
         *  Check it is for one of our groups
         */
        struct ip_mc_list *ip_mc=dev->ip_mc_list;
        do
        {
            if(ip_mc==NULL)
            {   
                kfree_skb(skb, FREE_WRITE);
                return 0;
            }
            if(ip_mc->multiaddr==iph->daddr)
                break;
            ip_mc=ip_mc->next;
        }
        while(1);
    }
#endif
    /*
     *  Account for the packet
     */

#ifdef CONFIG_IP_ACCT
    ip_acct_cnt(iph,dev, ip_acct_chain);
#endif    

    /*
     * Reassemble IP fragments.
      */
    // 分片重组 
    if(is_frag)
    {
        /* Defragment. Obtain the complete packet if there is one */
        skb=ip_defrag(iph,skb,dev);
        if(skb==NULL)
            return 0;
        skb->dev = dev;
        iph=skb->h.iph;
    }



    /*
     *  Point into the IP datagram, just past the header.
     */

    skb->ip_hdr = iph;
    // 往上层传之前先指向上层的头
    skb->h.raw += iph->ihl*4;

    /*
     *  Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
     */

    hash = iph->protocol & (SOCK_ARRAY_SIZE-1);

    /* If there maybe a raw socket we must check - if not we don't care less */
    if((raw_sk=raw_prot.sock_array[hash])!=NULL)
    {
        struct sock *sknext=NULL;
        struct sk_buff *skb1;
        // 找对应的socket
        raw_sk=get_sock_raw(raw_sk, hash,  iph->saddr, iph->daddr);
        if(raw_sk)  /* Any raw sockets */
        {
            do
            {
                /* Find the next */
                // 从队列中raw_sk的下一个节点开始找满足条件的socket,因为之前的的肯定不满足条件了
                sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr);
                // 复制一份skb给符合条件的socket
                if(sknext)
                    skb1=skb_clone(skb, GFP_ATOMIC);
                else
                    break;  /* One pending raw socket left */
                if(skb1)
                    raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);
                // 记录最近符合条件的socket
                raw_sk=sknext;
            }
            while(raw_sk!=NULL);
            /* Here either raw_sk is the last raw socket, or NULL if none */
            /* We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy */
        }
    }

    /*
     *  skb->h.raw now points at the protocol beyond the IP header.
     */
    // 传给ip层的上传协议
    hash = iph->protocol & (MAX_INET_PROTOS -1);
    // 获取哈希链表中的一个队列,遍历
    for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
    {
        struct sk_buff *skb2;

        if (ipprot->protocol != iph->protocol)
            continue;
       /*
    *   See if we need to make a copy of it.  This will
    *   only be set if more than one protocol wants it.
    *   and then not for the last one. If there is a pending
    *   raw delivery wait for that
    */  
        /*
            是否需要复制一份skb,copy字段这个版本中都是0,有多个一样的协议才需要复制一份,
            否则一份就够,因为只有一个协议需要使用,raw_sk的值是上面代码决定的
        */
        if (ipprot->copy || raw_sk)
        {
            skb2 = skb_clone(skb, GFP_ATOMIC);
            if(skb2==NULL)
                continue;
        }
        else
        {
            skb2 = skb;
        }
        // 找到了处理该数据包的上层协议
        flag = 1;

           /*
        * Pass on the datagram to each protocol that wants it,
        * based on the datagram protocol.  We should really
        * check the protocol handler's return values here...
        */
        ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
                (ntohs(iph->tot_len) - (iph->ihl * 4)),
                iph->saddr, 0, ipprot);

    }

    /*
     * All protocols checked.
     * If this packet was a broadcast, we may *not* reply to it, since that
     * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
     * ICMP reply messages get queued up for transmission...)
     */

    if(raw_sk!=NULL)    /* Shift to last raw user */
        raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
    // 没找到处理该数据包的上层协议,报告错误
    else if (!flag)     /* Free and report errors */
    {   
        // 不是广播不是多播,发送目的地不可达的icmp包
        if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
            icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);
        kfree_skb(skb, FREE_WRITE);
    }

    return(0);
}

ip层遍历inet_protos数组,找到和ip头中指定的协议相等的协议,把数据包交给该节点处理。比如tcp协议对应的处理函数是tcp_rcv,该函数把skb挂载到socket的接收队列等待读取,获取建立一个连接等。应用层使用read函数进行读取的时候,就从接收队列摘下一个skb。至此,一个数据包从网卡到应用层的过程就结束了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档