/*
 * 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 unknown 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
 *		Alan Cox	:	SNMP Mibs
 *		Alan Cox	:	MSG_DONTROUTE, and 0.0.0.0 support.
 *		Matt Dillon	:	UDP length checks.
 *		Alan Cox	:	Smarter af_inet used properly.
 *		Alan Cox	:	Use new kernel side addressing.
 *		Alan Cox	:	Incorrect return on truncated datagram receive.
 *	Richard Underwood	:	IP multicast.
 *		Alan Cox	:	Fixed a couple of differences from BSD
 *
 *
 *		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 <linux/inet.h>
#include <linux/netdevice.h>
#include "snmp.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
#include <linux/skbuff.h>
#include <linux/netprotocol.h>
#include "sock.h"
#include "udp.h"
#include "icmp.h"
#define min(a,b)	((a)<(b)?(a):(b))

/*
 *	SNMP MIB for the UDP layer
 */

struct udp_mib		udp_statistics;


struct protocol proto_udp;
static struct protocol *pr_icmp;	/* Our error protocol */

/*
 * This routine is called by the ICMP module when it gets some
 * sort of error condition. udp_input spots frames from ICMP and
 * redirects them here.
 */

void udp_err(sk_buff *skb)
{
	struct icmphdr *icmph=(struct icmphdr *)skb->h.raw;
	struct iphdr *iph=(struct iphdr *)skb_pull(skb,sizeof(*iph), NULL);
	struct udphdr *udph;
	struct sock *sk;
	
	/* Pull off any IP options - check this is right */
	skb_pull(skb, (4*iph->ihl)-sizeof(*iph),NULL);
	
	/*
	 *	Find the 8 bytes of post IP header ICMP included for us
	 */  
	
	udph = (struct udphdr *)skb_pull(skb, sizeof(*udph),NULL);  

	sk = get_sock(&udp_prot, udph->source, iph->daddr, udph->dest, iph->saddr);

	if (sk == NULL) 
	{
	  	return;	/* No socket for error */
  	}
  	
  	if (icmph->type == ICMP_TIME_EXCEEDED)
  		return;
  		 	
	if (icmph->type == ICMP_SOURCE_QUENCH)
	{	/* Slow down! - currently does nothing */
		if (sk->cong_window > 1) 
			sk->cong_window = sk->cong_window/2;
		return;
	}
	
	/*
	 *	Various people wanted BSD UDP semantics. Well they've come 
	 *	back out because they slow down response to stuff like dead
	 *	or unreachable name servers and they screw term users something
	 *	chronic. Oh and it violates RFC1122. So basically fix your 
	 *	client code people.
	 */
	
#ifndef FAVOUR_RFC1122_NOT_BROKEN_BSD 
#ifdef CONFIG_I_AM_A_BROKEN_BSD_WEENIE
	/*
	 *	It's only fatal if we have connected to them. I'm not happy
	 *	with this code. Some BSD comparisons need doing.
	 */
	 
	if (icmp_err_convert[icmph->code].fatal && sk->state == TCP_ESTABLISHED) 
	{
		sk->err = icmp_err_convert[icmph->code].errno;
		sk->error_report(sk);
 	}
#else
	if (icmp_err_convert[icmph->code].fatal)
	{
		sk->err = icmp_err_convert[icmph->code].errno;
		sk->error_report(sk);
	}
#endif
#else
	sk->err=icmp_err_convert[icmph->code].errno;
	sk->error_report(sk);
#endif
}


static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr)
{
	unsigned long sum;

	__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);
}

/*
 *	Generate UDP checksums. These may be disabled, eg for fast NFS over ethernet
 *	We default them enabled.. if you turn them off you either know what you are
 *	doing or get burned...
 */

static void udp_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);
	
	/*
	 *	FFFF and 0 are the same, pick the right one as 0 in the
	 *	actual field means no checksum.
	 */
	 
	if (uh->check == 0)
		uh->check = 0xffff;
}


static int udp_send(struct sock *sk, struct sockaddr_in *sin,
	 struct iovec *iov, int len, int rt, int noblock)
{
	sk_buff *skb;
	struct udphdr *uh;
	unsigned long saddr;
	int size;
  	int err;
  
	/* 
	 *	Allocate an sk_buff copy of the packet.
	 */
	 
	size = protocol_size(&proto_udp) + len;
	if((skb = sock_alloc_send_skb(sk, size, noblock, &err))==NULL)
		return err;
	protocol_adjust(skb,&proto_udp);

	skb->sk       = sk;	/* to avoid changing sk->saddr */
	skb->free     = 1;
	skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);

	/*
	 *	Now build the IP and MAC header. 
	 */
	 
	saddr = sk->saddr;
	
	/*
	 *	Copy the user data. 
	 */
	 
	memcpy_fromiovec(skb_put(skb,len), iov, len);
	
	/*
	 *	Fix the addresses so we can calculate the checksum!
	 */
	 
	saddr=ip_get_saddr(saddr,sin->sin_addr.s_addr, skb->localroute);

	/*
	 *	Push the UDP header
	 */

	uh = (struct udphdr *) skb_push(skb,sizeof(struct udphdr));
	uh->len = htons(len + sizeof(struct udphdr));
	uh->source = sk->opt.ip.dummy_th.source;
	uh->dest = sin->sin_port;
	
  	/*
  	 *	Set up the UDP checksum. 
  	 */
  	 
	udp_send_check(uh, saddr, sin->sin_addr.s_addr, sizeof(struct udphdr)+len, sk);
	 
	/*
	 *	Throw to IP.
	 */
	 
	skb->dev=NULL;
	if((err=proto_ip.output(&proto_ip,skb,ETH_P_IP,IPPROTO_UDP, &saddr,&sin->sin_addr.s_addr,&sk->opt.ip))==0)
	{
		udp_statistics.UdpOutDatagrams++;
		return len;
	}
	return err;
}


static int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock,
	   unsigned flags)
{
	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 (msg->msg_name) 
	{
		if (msg->msg_namelen < sizeof(sin)) 
			return(-EINVAL);
		memcpy(&sin,msg->msg_name,sizeof(sin));
		if (sin.sin_port == 0) 
			return(-EINVAL);
		if(sk->state == TCP_ESTABLISHED)
			return(-EISCONN);
	} 
	else 
	{
		if (sk->state != TCP_ESTABLISHED) 
			return(-ENOTCONN);
		sin.sin_family = AF_INET;
		sin.sin_port = sk->opt.ip.dummy_th.dest;
		sin.sin_addr.s_addr = sk->daddr;
  	}

	sk->inuse = 1;

	/* Send the packet. */
	tmp = udp_send(sk, &sin, msg->msg_iov, len, flags, noblock);

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

/*
 *	IOCTL requests applicable to the UDP protocol
 */
 
int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
	int err;
	switch(cmd) 
	{
		case TIOCOUTQ:
		{
			unsigned long amount;

			if (sk->state == TCP_LISTEN) return(-EINVAL);
			amount = sock_wspace(sk);
			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:
		{
			sk_buff *skb;
			unsigned long amount;

			if (sk->state == TCP_LISTEN) return(-EINVAL);
			amount = 0;
			skb = skb_peek(&sk->receive_queue);
			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.
 */

int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
	     int noblock, unsigned flags, int *addr_len)
{
  	int copied = 0;
  	int truesize;
  	sk_buff *skb;
  	int er;
  	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;

	/*
	 *	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);

	skb_copy_datagram_iovec(skb,0,msg->msg_iov,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);
}

int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
{
	sk->daddr = usin->sin_addr.s_addr;
	sk->opt.ip.dummy_th.dest = usin->sin_port;
	sk->state = TCP_ESTABLISHED;
	return(0);
}


static void udp_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. 
 *
 *	Entry: 
 *		The next pending data is the UDP datagram header.
 *	Calls:
 *		User socket code wakeups. The buffer queued for the user
 *		layer has all headers stripped correctly and is trimmed to
 *		the correct length.
 *	Return:
 *		The buffer has been freed or given to a user socket. Any
 *		errors at the UDP layer have been dealt with.
 *
 */

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

 
static int udp_input(struct protocol *p, struct protocol *below, sk_buff *skb1, void *saddr_p, void *daddr_p)
{
  	struct sock *sk;
  	struct udphdr *uh;
	unsigned short ulen;
	int size;
	unsigned long saddr=*(long *)saddr_p;
	unsigned long daddr=*(long *)daddr_p;
	sk_buff *skb=skb1;
	int len=skb->len;
	if(below==pr_icmp)
	{
		udp_err(skb);
		kfree_skb(skb,FREE_READ);
		return 0;
	}
  	ip_statistics.IpInDelivers++;
		
	/*
	 *	Get the header.
	 */

  	uh = (struct udphdr *)skb_pull(skb,sizeof(struct udphdr),&size);

	/*
	 *	Validate the packet and the UDP length.
	 */
	 
	ulen = ntohs(uh->len);

	if (size!=sizeof(struct udphdr) || 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);
	}
	len=ulen;

	if (uh->check && udp_check(uh, len, saddr, daddr)) 
	{
		printk("UDP: bad checksum.\n");
		udp_statistics.UdpInErrors++;
		kfree_skb(skb, FREE_WRITE);
		return(0);
	}
	
  	sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);

	if (MULTICAST(daddr))
	{
		struct sock *sknext;
		
		do
		{
			sknext=get_sock_next(sk, &udp_prot, uh->dest, saddr, uh->source, daddr);
			if(sknext)
				skb=skb_clone(skb1,GFP_ATOMIC);
			else
				skb=skb1;
			if(skb)
				udp_deliver(sk, uh, skb,skb->dev,saddr,daddr,len);
		}
		while(sknext!=NULL);
		return 0;
	
	}			
	if (sk == NULL) 
  	{
  		udp_statistics.UdpNoPorts++;
		if (ip_chk_addr(daddr) == IS_MYADDR) 
		{
		/*
		 *	RFC 1122: 3.2.2.1 SHOULD send PORT_UNREACH for unknown port numbers.
		 */
			icmp_send(skb,skb->h.iph, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, skb->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, skb->dev, saddr, daddr, len);
}  	
  	
  	
static int udp_deliver(struct sock *sk, struct udphdr *uh, sk_buff *skb, struct device *dev, long saddr, long daddr, int len)
{  
	skb->dev = dev;
	skb_trim(skb,len);
	skb->h.uh=uh;
		
	/*
	 *	These are supposed to be switched. 
	 */
	 
	skb->daddr = saddr;
	skb->saddr = daddr;


	/*
	 *	Charge it to the socket, dropping if the queue is full.
	 */
	 
	if (sock_queue_rcv_skb(sk,skb)<0)
	{
		udp_statistics.UdpInErrors++;
		ip_statistics.IpInDiscards++;
		ip_statistics.IpInDelivers--;
		kfree_skb(skb, FREE_WRITE);
		release_sock(sk);
		return(0);
	}
  	udp_statistics.UdpInDatagrams++;
	release_sock(sk);
	return(0);
}


struct proto udp_prot = {
	udp_close,		/* Close a UDP socket 		*/
	udp_sendmsg,		/* Send a message 		*/
	udp_recvmsg,		/* Receive a message		*/
	udp_connect,		/* Connect a UDP socket		*/
	NULL,			/* No accept() functionality	*/
	NULL,			/* No retransmit function	*/
	NULL,			/* No write wakeup		*/
	NULL,			/* No read wakeup		*/
	NULL,			/* No rcv function(obs)		*/
	datagram_select,	/* Select using generic		*/
	udp_ioctl,		/* UDP ioctl calls		*/
	NULL,			/* Initialise			*/
	NULL,			/* Shutdown a UDP socket	*/
	ip_setsockopt,		/* Set options on a UDP socket	*/
	ip_getsockopt,		/* Get options on a UDP socket	*/
	128,			/* Max header size		*/
	0,			/* Retransmits for protocol	*/
	{NULL,},		/* Socket array			*/
	"UDP"			/* Name				*/
};

/*
 *	Find a binding for UDP
 */
 
static int udp_get_binding(int protocol, int subid, unsigned char *key)
{
	return -EAFNOSUPPORT;
}
 
/*
 *	Protocol descriptor for UDP.
 */


struct protocol proto_udp=
{
	NULL,
	"UDP",
	sizeof(struct udphdr),
	0,
	sizeof(struct udphdr),
	0,
	NULL,	/* No output queue level handler (yet) */
	udp_input,	
	udp_input,	/* Slow input handler isn't needed but is used because we make IP slow anyway. */
	default_protocol_control,
	udp_get_binding,
	NULL,
	NULL
};

/*
 *	Called from IP init to initialise the UDP protocol layers
 */

void udp_init(void)
{
	struct protocol *pr=protocol_find("IP");
	if(pr==NULL)
	{
		printk("UDP: Cannot find IP in order to bind.\n");
		return;
	}
	protocol_register(&proto_udp);
	protocol_bind(pr,&proto_udp, ETH_P_IP, IPPROTO_UDP);
	pr_icmp=protocol_find("ICMP");
	if(pr_icmp==NULL)
	{
		printk("UDP: Cannot find ICMP in order to bind.\n");
		return;
	}
	protocol_bind(pr_icmp,&proto_udp, ETH_P_IP, IPPROTO_UDP);
}
