前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UDP协议源码解析之接收

UDP协议源码解析之接收

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

下面是入口函数

static int sock_recvfrom(int fd, void * buff, int len, unsigned flags,
         struct sockaddr *addr, int *addr_len)
{
    struct socket *sock;
    struct file *file;
    char address[MAX_SOCK_ADDR];
    int err;
    int alen;
    if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
        return(-EBADF);
    if (!(sock = sockfd_lookup(fd, NULL))) 
        return(-ENOTSOCK);
    if(len<0)
        return -EINVAL;
    if(len==0)
        return 0;

    err=verify_area(VERIFY_WRITE,buff,len);
    if(err)
        return err;

    len=sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),
             flags, (struct sockaddr *)address, &alen);

    if(len<0)
        return len;
    if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
        return err;

    return len;
}

static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, 
           unsigned flags, struct sockaddr *sin, int *addr_len )
{
    struct sock *sk = (struct sock *) sock->data;

    if (sk->prot->recvfrom == NULL) 
        return(-EOPNOTSUPP);
    if(sk->err)
        return inet_error(sk);
    /* We may need to bind the socket. */
    if(inet_autobind(sk)!=0)
        return(-EAGAIN);
    return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags,
                 (struct sockaddr_in*)sin, addr_len));
}

调用读取数据的函数前,我们要先调用bind绑定socket对应的地址信息,因为系统是根据地址和端口去查找一个socket的。由代码可以知道,入口没有什么逻辑,主要逻辑在udp层的实现代码中。代码的实现比较简单,就是从socket的接收队列中摘下数据。

/*
 *     This should be easy, if there is something there we\
 *     return it, otherwise we block.
 */

int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
         int noblock, unsigned flags, struct sockaddr_in *sin,
         int *addr_len)
{
      int copied = 0;
      int truesize;
      struct sk_buff *skb;
      int er;

    /*
     *  Check any passed addresses
     */

      if (addr_len) 
          *addr_len=sizeof(*sin);

    /*
     *  From here the generic datagram does a lot of the work. Come
     *  the finished NET3, it will do _ALL_ the work!
     */

    skb=skb_recv_datagram(sk,flags,noblock,&er);
    if(skb==NULL)
          return er;

      truesize = skb->len;
      copied = min(len, truesize);

      /*
       *  FIXME : should use udp header size info value 
       */

    skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
    sk->stamp=skb->stamp;

    /* Copy the address. */
    // 复制源地址信息给应用层
    if (sin) 
    {
        sin->sin_family = AF_INET;
        sin->sin_port = skb->h.uh->source;
        sin->sin_addr.s_addr = skb->daddr;
      }

      skb_free_datagram(skb);
      release_sock(sk);
      return(truesize);
}

读取的时候是直接从socket的接收队列进行的,那接收队列的数据又是怎么来的呢,当底层的收到udp的数据包的时候,会调用udp的udp_rcv函数,该函数把数据包缓存到socket的接收队列。

int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
    unsigned long daddr, unsigned short len,
    unsigned long saddr, int redo, struct inet_protocol *protocol)
{
      struct sock *sk;
      struct udphdr *uh;
    unsigned short ulen;
    int addr_type = IS_MYADDR;

    if(!dev || dev->pa_addr!=daddr)
        addr_type=ip_chk_addr(daddr);

    /*
     *  Get the header.
     */
      uh = (struct udphdr *) skb->h.uh;

      ip_statistics.IpInDelivers++;

    /*
     *  Validate the packet and the UDP length.
     */

    ulen = ntohs(uh->len);

    if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) 
    {
        printk("UDP: short packet: %d/%d\n", ulen, len);
        udp_statistics.UdpInErrors++;
        kfree_skb(skb, FREE_WRITE);
        return(0);
    }
    // 检查检验和
    if (uh->check && udp_check(uh, len, saddr, daddr)) 
    {
        /* <mea@utu.fi> wants to know, who sent it, to
           go and stomp on the garbage sender... */
        printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
               ntohl(saddr),ntohs(uh->source),
               ntohl(daddr),ntohs(uh->dest),
               ulen);
        udp_statistics.UdpInErrors++;
        kfree_skb(skb, FREE_WRITE);
        return(0);
    }


    len=ulen;

#ifdef CONFIG_IP_MULTICAST
    if (addr_type!=IS_MYADDR)
    {
        /*
         *  Multicasts and broadcasts go to each listener.
         */
        struct sock *sknext=NULL;
        sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
                saddr, uh->source, daddr);
        if(sk)
        {       
            do
            {
                struct sk_buff *skb1;

                sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
                if(sknext)
                    skb1=skb_clone(skb,GFP_ATOMIC);
                else
                    skb1=skb;
                if(skb1)
                    udp_deliver(sk, uh, skb1, dev,saddr,daddr,len);
                sk=sknext;
            }
            while(sknext!=NULL);
        }
        else
            kfree_skb(skb, FREE_READ);
        return 0;
    }   
#endif
    // 获取对应socket
      sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
    if (sk == NULL) 
      {
          udp_statistics.UdpNoPorts++;
        // 没有对应的socket
        if (addr_type == IS_MYADDR) 
        {
            icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
        }
        /*
         * Hmm.  We got an UDP broadcast to a port to which we
         * don't wanna listen.  Ignore it.
         */
        skb->sk = NULL;
        kfree_skb(skb, FREE_WRITE);
        return(0);
      }

    return udp_deliver(sk,uh,skb,dev, saddr, daddr, len);
}

static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len)
{
    skb->sk = sk;
    skb->dev = dev;
    skb->len = len;

    /*
     *  These are supposed to be switched. 
     */

    skb->daddr = saddr;
    skb->saddr = daddr;


    /*
     *  Charge it to the socket, dropping if the queue is full.
     */

    skb->len = len - sizeof(*uh);  
    // 把skb挂载到sk接收队列
    if (sock_queue_rcv_skb(sk,skb)<0) 
    {
        udp_statistics.UdpInErrors++;
        ip_statistics.IpInDiscards++;
        ip_statistics.IpInDelivers--;
        skb->sk = NULL;
        kfree_skb(skb, FREE_WRITE);
        release_sock(sk);
        return(0);
    }
      udp_statistics.UdpInDatagrams++;
    release_sock(sk);
    return(0);
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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