0
Linux TCP/IP协议栈之Socket的实现分析(六 数据包的接收)

原文地址:http://www.skynet.org.cn/viewthread.php?tid=304&extra=page%3D1
作者:kendo

前面了解过sk有一个接收队列,用于存储接收到的skb,对于socket层面上来讲,数据接收,就是要把数据从这个队列中取出来,交给上层用户态。这里涉及到出队操作,但是,要了解如何出队,就得了解传输层协议如何入队。前面一直用tcp协议来分析,现在还没有把整个tcp栈分析出来,要再继续用tcp协议来分析,就有点问题了,所以,数据的接收和发送,都将以udp协议来分析。虽然它很简单,但同样也反应了socket层数据与接收的全部核心内容与思路。我以希望,下一步拿下tcp协议后,再把这部份的tcp实现补上来。

一、udp层的数据接收
udp层的数据接收,对于socket而言,就是接收队列的入队操作。在ip层中,如果是本地数据,则会交由ip_local_deliver_finish()函数处理,它会根据传输层协议的类型,交由相应的处理函数,对于udp协议而言,就是udp_rcv():


/*
*        All we need to do is get the socket, and then do a checksum.
*/

int udp_rcv(struct sk_buff *skb)
{
          struct sock *sk;
          struct udphdr *uh;
        unsigned short ulen;
        struct rtable *rt = (struct rtable*)skb->dst;
        u32 saddr = skb->nh.iph->saddr;
        u32 daddr = skb->nh.iph->daddr;
        int len = skb->len;

        /*
         *        数据包至少应有UDP首部长度.
         */
        if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                goto no_header;
                
        /*获取udp首部指针*/
        uh = skb->h.uh;
        
        /* 数据长度,含首部长度 */
        ulen = ntohs(uh->len);
        
        /* 数据包长度不够:UDP长度比skb长度小,意味着数据的丢失,而udp长度比udp首部还要小,好像这个不太可能,除非封包出错 ^o^*/
        if (ulen > len &#124;&#124; ulen < sizeof(*uh))
                goto short_packet;
                
        /* 截去udp报文后面的多余报文 */
        if (pskb_trim(skb, ulen))
                goto short_packet;
        
        /* 开始udp校验和计算,主要查依赖于skb的ip_summumed字段的设置来决定是否需要进行校验和计算 */
        if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
                goto csum_error;
        
        /* 转换多播或广播处理例程 */
        if(rt->rt_flags & (RTCF_BROADCAST&#124;RTCF_MULTICAST))
                return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
        
        /* 查找数据段对应的socket结构的sk */
        sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);

        if (sk != NULL) {
                /* 找到了,数据包进入UDP的socket的接收队列*/
                int ret = udp_queue_rcv_skb(sk, skb);
                sock_put(sk);

                /* a return value > 0 means to resubmit the input, but
                 * it it wants the return to be -protocol, or 0
                 */
                if (ret > 0)
                        return -ret;
                return 0;
        }

        if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto drop;

        /* 没有对应的socket. 如果校验和错误,则丢弃它 */
        if (udp_checksum_complete(skb))
                goto csum_error;
        
        /* 发送一个目的不可达报文 */
        UDP_INC_STATS_BH(UDP_MIB_NOPORTS);
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);

        /*
         * Hmm.  We got an UDP packet to a port to which we
         * don't wanna listen.  Ignore it.
         */
        kfree_skb(skb);
        return(0);

short_packet:
        NETDEBUG(if (net_ratelimit())
                printk(KERN_DEBUG "UDP: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
                        NIPQUAD(saddr),
                        ntohs(uh->source),
                        ulen,
                        len,
                        NIPQUAD(daddr),
                        ntohs(uh->dest)));
no_header:
        UDP_INC_STATS_BH(UDP_MIB_INERRORS);
        kfree_skb(skb);
        return(0);

csum_error:
        /*
         * RFC1122: OK.  Discards the bad packet silently (as far as
         * the network is concerned, anyway) as per 4.1.3.4 (MUST).
         */
        NETDEBUG(if (net_ratelimit())
                 printk(KERN_DEBUG "UDP: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
                        NIPQUAD(saddr),
                        ntohs(uh->source),
                        NIPQUAD(daddr),
                        ntohs(uh->dest),
                        ulen));
drop:
        UDP_INC_STATS_BH(UDP_MIB_INERRORS);
        kfree_skb(skb);
        return(0);
}

函数的核心思想,是根据skb,查找到与之对应的sk,调用udp_v4_lookup()函数实现——udp与tcp一样,socket有一个hash表,这个查找,就是查找hash表的过程。
点击阅读全文内文分页: [1] [2] [3] [4] [5]
Tags: , , , | 引用(0)
发表评论
表情
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
打开HTML
打开UBB
打开表情
隐藏
记住我
昵称   密码   游客无需密码
网址   电邮   [注册]