UDP协议源码解析之发送

UDP不是面向连接的协议,发送数据的时候指定目的地址的信息即可以发送。下面是入口函数的代码。

static int sock_sendto(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;
	
	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;
	err=verify_area(VERIFY_READ,buff,len);
	if(err)
	  	return err;
  	
	if((err=move_addr_to_kernel(addr,addr_len,address))<0)
	  	return err;

	return(sock->ops->sendto(sock, buff, len, (file->f_flags & O_NONBLOCK),
		flags, (struct sockaddr *)address, addr_len));
}

static int inet_sendto(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->shutdown & SEND_SHUTDOWN) 
	{
		send_sig(SIGPIPE, current, 1);
		return(-EPIPE);
	}
	if (sk->prot->sendto == 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->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
			   (struct sockaddr_in *)sin, addr_len));
}

由代码知道,入口函数做的事情不对,有一个需要注意的是,如果调用sendto函数之前没有调bind绑定过端口则这里会随机绑定一个端口。接着是调用udp层的接口udp_sendto。下面是udp_sendto的代码。



static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
	   unsigned flags, struct sockaddr_in *usin, int addr_len)
{
	struct sockaddr_in sin;
	int tmp;

	/* 
	 *	Check the flags. We support no flags for UDP sending
	 */
	if (flags&~MSG_DONTROUTE) 
	  	return(-EINVAL);
	/*
	 *	Get and verify the address. 
	 */
	// 目的地址信息
	if (usin) 
	{
		if (addr_len < sizeof(sin)) 
			return(-EINVAL);
		memcpy(&sin,usin,sizeof(sin));
		if (sin.sin_family && sin.sin_family != AF_INET) 
			return(-EINVAL);
		if (sin.sin_port == 0) 
			return(-EINVAL);
	} 
	else 
	{	
		// 没传目的信息又没有建立起连接,即没有调connect函数,则无法知道数据包应该发送给谁
		if (sk->state != TCP_ESTABLISHED) 
			return(-EINVAL);
		// 从之前建立连接中获取目的信息
		sin.sin_family = AF_INET;
		sin.sin_port = sk->dummy_th.dest;
		sin.sin_addr.s_addr = sk->daddr;
  	}
  
  	/*
  	 *	BSD socket semantics. You must set SO_BROADCAST to permit
  	 *	broadcasting of data.
  	 */
  	 
  	if(sin.sin_addr.s_addr==INADDR_ANY)
  		sin.sin_addr.s_addr=ip_my_addr();
  		
  	if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
	    	return -EACCES;			/* Must turn broadcast on first */

	sk->inuse = 1;

	/* Send the packet. */
	tmp = udp_send(sk, &sin, from, len, flags);

	/* The datagram has been sent off.  Release the socket. */
	release_sock(sk);
	return(tmp);
}

static int udp_send(struct sock *sk, struct sockaddr_in *sin,
	 unsigned char *from, int len, int rt)
{
	struct sk_buff *skb;
	struct device *dev;
	struct udphdr *uh;
	unsigned char *buff;
	unsigned long saddr;
	int size, tmp;
	int ttl;
  
	/* 
	 *	Allocate an sk_buff copy of the packet.
	 */
	// 协议头最大长度+数据长度
	size = sk->prot->max_header + len;
	// 在写缓冲区申请一个skb
	skb = sock_alloc_send_skb(sk, size, 0, &tmp);


	if (skb == NULL) 
		return tmp;

	skb->sk       = NULL;	/* to avoid changing sk->saddr */
	// 发送完可以销毁,不需要缓存
	skb->free     = 1;
	skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);

	/*
	 *	Now build the IP and MAC header. 
	 */
	 
	buff = skb->data;
	saddr = sk->saddr;
	dev = NULL;
	ttl = sk->ip_ttl;
#ifdef CONFIG_IP_MULTICAST
	if (MULTICAST(sin->sin_addr.s_addr))
		ttl = sk->ip_mc_ttl;
#endif
	// 构建ip和mac头
	tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
			&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);

	skb->sk=sk;	/* So memory is freed correctly */
	
	/*
	 *	Unable to put a header on the packet.
	 */
	 		    
	if (tmp < 0 ) 
	{
		sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
		return(tmp);
  	}
  	
	buff += tmp;
	saddr = skb->saddr; /*dev->pa_addr;*/
	// 全部协议头+数据的长度
	skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */
	skb->dev = dev;
	
	/*
	 *	Fill in the UDP header. 
	 */
	 
	uh = (struct udphdr *) buff;
	// 设置udp头各字段
	uh->len = htons(len + sizeof(struct udphdr));
	uh->source = sk->dummy_th.source;
	uh->dest = sin->sin_port;
	// 写入数据的首地址
	buff = (unsigned char *) (uh + 1);

	/*
	 *	Copy the user data. 
	 */
	 
	memcpy_fromfs(buff, from, len);

  	/*
  	 *	Set up the UDP checksum. 
  	 */
	// 计算校验和
	udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);

	/* 
	 *	Send the datagram to the interface. 
	 */
	 
	udp_statistics.UdpOutDatagrams++;
	// 下发到ip层	 
	sk->prot->queue_xmit(sk, dev, skb, 1);
	return(len);
}

UDP的源地址和端口如果用户没有设置的话都是由系统自己决定的,目的地址和端口则需要用户提供,提供的方式可以通过connect函数或者sendto的时候传入。


// udp不是面向连接的,connect函数主要是根据目的ip,从路由表找到目的地址的信息然后保存在sk中,并修改态为以已连接
int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
{
	struct rtable *rt;
	unsigned long sa;
	if (addr_len < sizeof(*usin)) 
	  	return(-EINVAL);

	if (usin->sin_family && usin->sin_family != AF_INET) 
	  	return(-EAFNOSUPPORT);
	if (usin->sin_addr.s_addr==INADDR_ANY)
		usin->sin_addr.s_addr=ip_my_addr();

	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
		return -EACCES;			/* Must turn broadcast on first */
  	
  	rt=ip_rt_route(usin->sin_addr.s_addr, NULL, &sa);
  	if(rt==NULL)
  		return -ENETUNREACH;
  	sk->saddr = sa;		/* Update source address */
	sk->daddr = usin->sin_addr.s_addr;
	sk->dummy_th.dest = usin->sin_port;
	sk->state = TCP_ESTABLISHED;
	return(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值