⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 udp.c

📁 LINUX1.0内核源代码,学习LINUX编程的一定要看。
💻 C
字号:
/* * INET		An implementation of the TCP/IP protocol suite for the LINUX *		operating system.  INET is implemented using the  BSD Socket *		interface as the means of communication with the user level. * *		The User Datagram Protocol (UDP). * * Version:	@(#)udp.c	1.0.13	06/02/93 * * Authors:	Ross Biro, <bir7@leland.Stanford.Edu> *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * * Fixes: *		Alan Cox	:	verify_area() calls *		Alan Cox	: 	stopped close while in use off icmp *					messages. Not a fix but a botch that *					for udp at least is 'valid'. *		Alan Cox	:	Fixed icmp handling properly *		Alan Cox	: 	Correct error for oversized datagrams *		Alan Cox	:	Tidied select() semantics.  *		Alan Cox	:	udp_err() fixed properly, also now  *					select and read wake correctly on errors *		Alan Cox	:	udp_send verify_area moved to avoid mem leak *		Alan Cox	:	UDP can count its memory *		Alan Cox	:	send to an uknown connection causes *					an ECONNREFUSED off the icmp, but *					does NOT close. *		Alan Cox	:	Switched to new sk_buff handlers. No more backlog! *		Alan Cox	:	Using generic datagram code. Even smaller and the PEEK *					bug no longer crashes it. *		Fred Van Kempen	: 	Net2e support for sk->broadcast. *		Alan Cox	:	Uses skb_free_datagram *		Alan Cox	:	Added get/set sockopt support. *		Alan Cox	:	Broadcasting without option set returns EACCES. *		Alan Cox	:	No wakeup calls. Instead we now use the callbacks. *		Alan Cox	:	Use ip_tos and ip_ttl * * *		This program is free software; you can redistribute it and/or *		modify it under the terms of the GNU General Public License *		as published by the Free Software Foundation; either version *		2 of the License, or (at your option) any later version. */ #include <asm/system.h>#include <asm/segment.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/fcntl.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/termios.h>#include <linux/mm.h>#include "inet.h"#include "dev.h"#include "ip.h"#include "protocol.h"#include "tcp.h"#include "skbuff.h"#include "sock.h"#include "udp.h"#include "icmp.h"#define min(a,b)	((a)<(b)?(a):(b))static voidprint_udp(struct udphdr *uh){  if (inet_debug != DBG_UDP) return;  if (uh == NULL) {	printk("(NULL)\n");	return;  }  printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));  printk("     len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));}/* * This routine is called by the ICMP module when it gets some * sort of error condition.  If err < 0 then the socket should * be closed and the error returned to the user.  If err > 0 * it's just the icmp type << 8 | icmp code.   * Header points to the ip header of the error packet. We move * on past this. Then (as it used to claim before adjustment) * header points to the first 8 bytes of the udp header.  We need * to find the appropriate port. */voidudp_err(int err, unsigned char *header, unsigned long daddr,	unsigned long saddr, struct inet_protocol *protocol){  struct udphdr *th;  struct sock *sk;  struct iphdr *ip=(struct iphdr *)header;    header += 4*ip->ihl;    th = (struct udphdr *)header;       DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));  sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);  if (sk == NULL)   	return;	/* No socket for error */  	  if (err < 0)		/* As per the calling spec */  {  	sk->err = -err;  	sk->error_report(sk);		/* User process wakes to see error */  	return;  }    if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) {	/* Slow down! */	if (sk->cong_window > 1) 		sk->cong_window = sk->cong_window/2;	return;  }  sk->err = icmp_err_convert[err & 0xff].errno;  /* It's only fatal if we have connected to them. */  if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) {	sk->err=ECONNREFUSED;  }  sk->error_report(sk);}static unsigned shortudp_check(struct udphdr *uh, int len,	  unsigned long saddr, unsigned long daddr){  unsigned long sum;  DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",	   						uh, len, saddr, daddr));  print_udp(uh);  __asm__("\t addl %%ecx,%%ebx\n"	  "\t adcl %%edx,%%ebx\n"	  "\t adcl $0, %%ebx\n"	  : "=b"(sum)	  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)	  : "cx","bx","dx" );  if (len > 3) {	__asm__("\tclc\n"		"1:\n"		"\t lodsl\n"		"\t adcl %%eax, %%ebx\n"		"\t loop 1b\n"		"\t adcl $0, %%ebx\n"		: "=b"(sum) , "=S"(uh)		: "0"(sum), "c"(len/4) ,"1"(uh)		: "ax", "cx", "bx", "si" );  }  /* Convert from 32 bits to 16 bits. */  __asm__("\t movl %%ebx, %%ecx\n"	  "\t shrl $16,%%ecx\n"	  "\t addw %%cx, %%bx\n"	  "\t adcw $0, %%bx\n"	  : "=b"(sum)	  : "0"(sum)	  : "bx", "cx");  /* Check for an extra word. */  if ((len & 2) != 0) {	__asm__("\t lodsw\n"		"\t addw %%ax,%%bx\n"		"\t adcw $0, %%bx\n"		: "=b"(sum), "=S"(uh)		: "0"(sum) ,"1"(uh)		: "si", "ax", "bx");  }  /* Now check for the extra byte. */  if ((len & 1) != 0) {	__asm__("\t lodsb\n"		"\t movb $0,%%ah\n"		"\t addw %%ax,%%bx\n"		"\t adcw $0, %%bx\n"		: "=b"(sum)		: "0"(sum) ,"S"(uh)		: "si", "ax", "bx");  }  /* We only want the bottom 16 bits, but we never cleared the top 16. */  return((~sum) & 0xffff);}static voidudp_send_check(struct udphdr *uh, unsigned long saddr, 	       unsigned long daddr, int len, struct sock *sk){  uh->check = 0;  if (sk && sk->no_check)   	return;  uh->check = udp_check(uh, len, saddr, daddr);  if (uh->check == 0) uh->check = 0xffff;}static intudp_send(struct sock *sk, struct sockaddr_in *sin,	 unsigned char *from, int len){  struct sk_buff *skb;  struct device *dev;  struct udphdr *uh;  unsigned char *buff;  unsigned long saddr;  int size, tmp;  int err;    DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",		in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),		from, len));  err=verify_area(VERIFY_READ, from, len);  if(err)  	return(err);  /* Allocate a copy of the packet. */  size = sizeof(struct sk_buff) + sk->prot->max_header + len;  skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);  if (skb == NULL) return(-ENOMEM);  skb->mem_addr = skb;  skb->mem_len  = size;  skb->sk       = NULL;	/* to avoid changing sk->saddr */  skb->free     = 1;  skb->arp      = 0;  /* Now build the IP and MAC header. */  buff = skb->data;  saddr = 0;  dev = NULL;  DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",			saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));  tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,			       &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);  skb->sk=sk;	/* So memory is freed correctly */			      if (tmp < 0 ) {	sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);	return(tmp);  }  buff += tmp;  saddr = dev->pa_addr;  DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));  skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */  skb->dev = dev;#ifdef OLD  /*   * This code used to hack in some form of fragmentation.   * I removed that, since it didn't work anyway, and it made the   * code a bad thing to read and understand. -FvK   */  if (len > dev->mtu) {#else  if (skb->len > 4095)  {#endif    	printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu);	sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);	return(-EMSGSIZE);  }  /* Fill in the UDP header. */  uh = (struct udphdr *) buff;  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. */  sk->prot->queue_xmit(sk, dev, skb, 1);  return(len);}static intudp_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;  int err;  DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));  /* Check the flags. */  if (flags)   	return(-EINVAL);  if (len < 0)   	return(-EINVAL);  if (len == 0)   	return(0);  /* Get and verify the address. */  if (usin) {	if (addr_len < sizeof(sin)) return(-EINVAL);	err=verify_area(VERIFY_READ, usin, sizeof(sin));	if(err)		return err;	memcpy_fromfs(&sin, usin, sizeof(sin));	if (sin.sin_family && sin.sin_family != AF_INET) 		return(-EINVAL);	if (sin.sin_port == 0) 		return(-EINVAL);  } else {	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;  }    if(!sk->broadcast && 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);  /* The datagram has been sent off.  Release the socket. */  release_sock(sk);  return(tmp);}static intudp_write(struct sock *sk, unsigned char *buff, int len, int noblock,	  unsigned flags){  return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));}intudp_ioctl(struct sock *sk, int cmd, unsigned long arg){  int err;  switch(cmd) {	case DDIOCSDBG:		{			int val;			if (!suser()) return(-EPERM);			err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));			if(err)				return err;			val = get_fs_long((int *)arg);			switch(val) {				case 0:					inet_debug = 0;					break;				case 1:					inet_debug = DBG_UDP;					break;				default:					return(-EINVAL);			}		}		break;	case TIOCOUTQ:		{			unsigned long amount;			if (sk->state == TCP_LISTEN) return(-EINVAL);			amount = sk->prot->wspace(sk)/*/2*/;			err=verify_area(VERIFY_WRITE,(void *)arg,					sizeof(unsigned long));			if(err)				return(err);			put_fs_long(amount,(unsigned long *)arg);			return(0);		}	case TIOCINQ:		{			struct sk_buff *skb;			unsigned long amount;			if (sk->state == TCP_LISTEN) return(-EINVAL);			amount = 0;			skb = sk->rqueue;			if (skb != NULL) {				/*				 * We will only return the amount				 * of this packet since that is all				 * that will be read.				 */				amount = skb->len;			}			err=verify_area(VERIFY_WRITE,(void *)arg,						sizeof(unsigned long));			if(err)				return(err);			put_fs_long(amount,(unsigned long *)arg);			return(0);		}	default:		return(-EINVAL);  }  return(0);}/* * This should be easy, if there is something there we\ * return it, otherwise we block. */intudp_recvfrom(struct sock *sk, unsigned char *to, int len,	     int noblock, unsigned flags, struct sockaddr_in *sin,	     int *addr_len){  int copied = 0;  struct sk_buff *skb;  int er;  /*   * This will pick up errors that occured while the program   * was doing something else.   */  if (sk->err) {	int err;	err = -sk->err;	sk->err = 0;	return(err);  }  if (len == 0)   	return(0);  if (len < 0)   	return(-EINVAL);  if (addr_len) {	er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));	if(er)		return(er);	put_fs_long(sizeof(*sin), addr_len);  }  if(sin)  {  	er=verify_area(VERIFY_WRITE, sin, sizeof(*sin));  	if(er)  		return(er);  }  er=verify_area(VERIFY_WRITE,to,len);  if(er)  	return er;  skb=skb_recv_datagram(sk,flags,noblock,&er);  if(skb==NULL)  	return er;  copied = min(len, skb->len);  /* FIXME : should use udp header size info value */  skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);  /* Copy the address. */  if (sin) {	struct sockaddr_in addr;	addr.sin_family = AF_INET;	addr.sin_port = skb->h.uh->source;	addr.sin_addr.s_addr = skb->daddr;	memcpy_tofs(sin, &addr, sizeof(*sin));  }    skb_free_datagram(skb);  release_sock(sk);  return(copied);}intudp_read(struct sock *sk, unsigned char *buff, int len, int noblock,	 unsigned flags){  return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));}intudp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len){  struct sockaddr_in sin;  int er;    if (addr_len < sizeof(sin))   	return(-EINVAL);  er=verify_area(VERIFY_READ, usin, sizeof(sin));  if(er)  	return er;  memcpy_fromfs(&sin, usin, sizeof(sin));  if (sin.sin_family && sin.sin_family != AF_INET)   	return(-EAFNOSUPPORT);  if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)    	return -EACCES;			/* Must turn broadcast on first */  	  sk->daddr = sin.sin_addr.s_addr;  sk->dummy_th.dest = sin.sin_port;  sk->state = TCP_ESTABLISHED;  return(0);}static voidudp_close(struct sock *sk, int timeout){  sk->inuse = 1;  sk->state = TCP_CLOSE;  if (sk->dead) destroy_sock(sk);    else release_sock(sk);}/* All we need to do is get the socket, and then do a checksum. */intudp_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;  uh = (struct udphdr *) skb->h.uh;  sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);  if (sk == NULL)   {	if (chk_addr(daddr) == IS_MYADDR) 	{		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);	}	/*	 * Hmm.  We got an UDP broadcast to a port to which we	 * don't wanna listen.  The only thing we can do now is	 * to ignore the packet... -FvK	 */	skb->sk = NULL;	kfree_skb(skb, FREE_WRITE);	return(0);  }  if (uh->check && udp_check(uh, len, saddr, daddr)) {	DPRINTF((DBG_UDP, "UDP: bad checksum\n"));	skb->sk = NULL;	kfree_skb(skb, FREE_WRITE);	return(0);  }  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. */  if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)   {	skb->sk = NULL;	kfree_skb(skb, FREE_WRITE);	release_sock(sk);	return(0);  }  sk->rmem_alloc += skb->mem_len;  /* At this point we should print the thing out. */  DPRINTF((DBG_UDP, "<< \n"));  print_udp(uh);  /* Now add it to the data chain and wake things up. */    skb_queue_tail(&sk->rqueue,skb);  skb->len = len - sizeof(*uh);  if (!sk->dead)   	sk->data_ready(sk,skb->len);  	  release_sock(sk);  return(0);}struct proto udp_prot = {  sock_wmalloc,  sock_rmalloc,  sock_wfree,  sock_rfree,  sock_rspace,  sock_wspace,  udp_close,  udp_read,  udp_write,  udp_sendto,  udp_recvfrom,  ip_build_header,  udp_connect,  NULL,  ip_queue_xmit,  ip_retransmit,  NULL,  NULL,  udp_rcv,  datagram_select,  udp_ioctl,  NULL,  NULL,  ip_setsockopt,  ip_getsockopt,  128,  0,  {NULL,},  "UDP"};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -