UDP协议源码解析之接收

下面是入口函数


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);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值