patch-2.1.120 linux/net/ipv4/udp.c

Next file: linux/net/ipv6/addrconf.c
Previous file: linux/net/ipv4/tcp_output.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.119/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.57 1998/05/14 06:32:44 davem Exp $
+ * Version:	$Id: udp.c,v 1.61 1998/08/29 17:11:10 freitag Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -59,6 +59,8 @@
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *		Melvin Smith	:	Check msg_name not msg_namelen in sendto(),
  *					return ENOTCONN for unconnected sockets (POSIX)
+ *		Janos Farkas	:	don't deliver multi/broadcasts to a different
+ *					bound-to-device socket
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -80,7 +82,7 @@
      MUST provide facility for checksumming (OK)
      MAY allow application to control checksumming (OK)
      MUST default to checksumming on (OK)
-     MUST discard silently datagrams with bad csums (OK)
+     MUST discard silently datagrams with bad csums (OK, except during debugging)
    4.1.3.5 (UDP Multihoming)
      MUST allow application to specify source address (OK)
      SHOULD be able to communicate the chosen src addr up to application
@@ -93,14 +95,12 @@
 #include <asm/system.h>
 #include <asm/uaccess.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/config.h>
 #include <linux/inet.h>
@@ -108,14 +108,12 @@
 #include <net/snmp.h>
 #include <net/ip.h>
 #include <net/protocol.h>
-#include <net/tcp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/udp.h>
 #include <net/icmp.h>
 #include <net/route.h>
 #include <net/checksum.h>
-#include <linux/ipsec.h>
 
 /*
  *	Snmp MIB for the UDP layer
@@ -447,7 +445,8 @@
 					     unsigned short num,
 					     unsigned long raddr,
 					     unsigned short rnum,
-					     unsigned long laddr)
+					     unsigned long laddr,
+					     int dif)
 {
 	struct sock *s = sk;
 	unsigned short hnum = ntohs(num);
@@ -455,8 +454,9 @@
 		if ((s->num != hnum)					||
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
-		    (s->dport != rnum && s->dport != 0) ||
-		    (s->rcv_saddr  && s->rcv_saddr != laddr))
+		    (s->dport != rnum && s->dport != 0)			||
+		    (s->rcv_saddr  && s->rcv_saddr != laddr)		||
+		    (s->bound_dev_if && s->bound_dev_if != dif))
 			continue;
 		break;
   	}
@@ -619,7 +619,8 @@
 	struct ipcm_cookie ipc;
 	struct udpfakehdr ufh;
 	struct rtable *rt = NULL;
-	int free = 0, localroute = 0;
+	int free = 0;
+	int connected = 0;
 	u32 daddr;
 	u8  tos;
 	int err;
@@ -674,27 +675,15 @@
 		ufh.uh.dest = usin->sin_port;
 		if (ufh.uh.dest == 0)
 			return -EINVAL;
-		/* XXX: is a one-behind cache for the dst_entry worth it?
-
-		   Nope. ip_route_output is slower than nothing, but it
-		   is enough fast to forget about caching its results.
-		   Really, checking route validity in general case
-		   is not much faster complete lookup.
-		   It was main reason why I removed it from 2.1.
-		   The second reason was that idle sockets held
-		   a lot of stray destinations.		--ANK
-		 */
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		ufh.daddr = sk->daddr;
 		ufh.uh.dest = sk->dport;
-
-		/*
-		   BUGGG Khm... And who will validate it? Fixing it fastly...
-		                                                        --ANK
+		/* Open fast path for connected socket.
+		   Route will not be used, if at least one option is set.
 		 */
-		rt = (struct rtable *)dst_check(&sk->dst_cache, 0);
+		connected = 1;
   	}
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (msg->msg_flags&MSG_PROXY) {
@@ -710,6 +699,7 @@
 		ufh.uh.source = from->sin_port;
 		if (ipc.addr == 0)
 			ipc.addr = sk->saddr;
+		connected = 0;
 	} else
 #endif
 	{
@@ -725,6 +715,7 @@
 			return err;
 		if (ipc.opt)
 			free = 1;
+		connected = 0;
 	}
 	if (!ipc.opt)
 		ipc.opt = sk->opt;
@@ -736,12 +727,13 @@
 		if (!daddr)
 			return -EINVAL;
 		daddr = ipc.opt->faddr;
+		connected = 0;
 	}
 	tos = RT_TOS(sk->ip_tos);
 	if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || 
 	    (ipc.opt && ipc.opt->is_strictroute)) {
 		tos |= RTO_ONLINK;
-		rt = NULL; /* sorry */
+		connected = 0;
 	}
 
 	if (MULTICAST(daddr)) {
@@ -749,8 +741,12 @@
 			ipc.oif = sk->ip_mc_index;
 		if (!ufh.saddr)
 			ufh.saddr = sk->ip_mc_addr;
+		connected = 0;
 	}
 
+	if (connected)
+		rt = (struct rtable*)dst_clone(sk->dst_cache);
+
 	if (rt == NULL) {
 		err = ip_route_output(&rt, daddr, ufh.saddr,
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -759,7 +755,6 @@
 			 tos, ipc.oif);
 		if (err) 
 			goto out;
-		localroute = 1;
 
 		err = -EACCES;
 		if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) 
@@ -777,17 +772,13 @@
 
 	/* RFC1122: OK.  Provides the checksumming facility (MUST) as per */
 	/* 4.1.3.4. It's configurable by the application via setsockopt() */
-	/* (MAY) and it defaults to on (MUST).  Almost makes up for the */
-	/* violation above. -- MS */
+	/* (MAY) and it defaults to on (MUST). */
 
-	lock_sock(sk);
 	err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag,
 			    &ufh, ulen, &ipc, rt, msg->msg_flags);
-	release_sock(sk);
 
 out:
-	if (localroute)
-		ip_rt_put(rt);
+	ip_rt_put(rt);
 	if (free)
 		kfree(ipc.opt);
 	if (!err) {
@@ -822,7 +813,9 @@
 			if (sk->state == TCP_LISTEN)
 				return(-EINVAL);
 			amount = 0;
-			/* N.B. Is this interrupt safe?? */
+			/* N.B. Is this interrupt safe??
+			   -> Yes. Interrupts do not remove skbs. --ANK (980725)
+			 */
 			skb = skb_peek(&sk->receive_queue);
 			if (skb != NULL) {
 				/*
@@ -841,6 +834,9 @@
 	return(0);
 }
 
+#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER) 
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
 
 /*
  * 	This should be easy, if there is something there we
@@ -848,7 +844,7 @@
  */
 
 int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
-	     int noblock, int flags, int *addr_len)
+		int noblock, int flags, int *addr_len)
 {
   	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
   	struct sk_buff *skb;
@@ -880,18 +876,36 @@
 		goto out;
   
   	copied = skb->len - sizeof(struct udphdr);
-	if (copied > len)
-	{
+	if (copied > len) {
 		copied = len;
 		msg->msg_flags |= MSG_TRUNC;
 	}
 
-  	/*
-  	 *	FIXME : should use udp header size info value 
-  	 */
-  	 
+#ifndef CONFIG_UDP_DELAY_CSUM
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					copied);
+#else
+	if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+		if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) 
+			goto csum_copy_err;
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else {
+		unsigned int csum;
+
+		err = 0;
+		csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+		csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, 
+					     copied, csum, &err);
+		if (err)
+			goto out_free;
+		if (csum_fold(csum)) 
+			goto csum_copy_err;
+	}
+#endif
 	if (err)
 		goto out_free;
 	sk->stamp=skb->stamp;
@@ -928,6 +942,18 @@
   	skb_free_datagram(sk, skb);
 out:
   	return err;
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+csum_copy_err:
+	udp_statistics.UdpInErrors++;
+	skb_free_datagram(sk, skb);
+
+	/* 
+	 * Error for blocking case is chosen to masquerade
+   	 * as some normal condition.
+	 */
+	return (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;	
+#endif
 }
 
 int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
@@ -986,29 +1012,16 @@
 
 static void udp_close(struct sock *sk, unsigned long timeout)
 {
-	lock_sock(sk);
+	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-	sk->dead = 1;
-	release_sock(sk);
 	udp_v4_unhash(sk);
+	sk->dead = 1;
 	destroy_sock(sk);
 }
 
 static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
 	/*
-	 *	Check the security clearance
-	 */
-	 
-	if(!ipsec_sk_policy(sk,skb))
-	{	
-		kfree_skb(skb);
-		return(0);
-	}
-	 
-	/*
 	 *	Charge it to the socket, dropping if the queue is full.
 	 */
 
@@ -1026,10 +1039,6 @@
 
 static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
 {
-	if (atomic_read(&sk->sock_readers)) {
-		__skb_queue_tail(&sk->back_log, skb);
-		return;
-	}
 	udp_queue_rcv_skb(sk, skb);
 }
 
@@ -1043,9 +1052,11 @@
 				 u32 saddr, u32 daddr)
 {
 	struct sock *sk;
+	int dif;
 
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	dif = skb->dev->ifindex;
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, dif);
 	if (sk) {
 		struct sock *sknext = NULL;
 
@@ -1053,7 +1064,7 @@
 			struct sk_buff *skb1 = skb;
 
 			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
-						   uh->source, daddr);
+						   uh->source, daddr, dif);
 			if(sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1113,7 +1124,8 @@
 	 */
 	 
   	uh = skb->h.uh;
-  	
+	__skb_pull(skb, skb->h.raw - skb->data);
+
   	ip_statistics.IpInDelivers++;
 
 	/*
@@ -1121,44 +1133,31 @@
 	 */
 	 
 	ulen = ntohs(uh->len);
-	
-	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) {
+
+	if (ulen > len || ulen < sizeof(*uh)) {
 		NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
 		udp_statistics.UdpInErrors++;
 		kfree_skb(skb);
 		return(0);
 	}
+	skb_trim(skb, ulen);
 
+#ifndef CONFIG_UDP_DELAY_CSUM
 	if (uh->check &&
-	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) ||
+	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) ||
 	     ((skb->ip_summed==CHECKSUM_NONE) &&
-	      (udp_check(uh,len,saddr,daddr, csum_partial((char*)uh, len, 0)))))) {
-		/* <mea@utu.fi> wants to know, who sent it, to
-		   go and stomp on the garbage sender... */
-
-		/* RFC1122: OK.  Discards the bad packet silently (as far as */
-		/* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
-
-		NETDEBUG(printk(KERN_DEBUG "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);
-		return(0);
-	}
-
-
-	len = ulen;
-
-	/*
-	 *	FIXME:
-	 *	Trimming things wrongly. We must adjust the base/end to allow
-	 *	for the headers we keep!
-	 *		 --ANK 
-	 */
-	skb_trim(skb,len);
-
+	      (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) 
+		goto csum_error;
+#else
+	if (uh->check==0)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else if (skb->ip_summed==CHECKSUM_HW) {
+		if (udp_check(uh,ulen,saddr,daddr,skb->csum)) 
+			goto csum_error;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+		skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+#endif
 
 	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
 		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
@@ -1173,6 +1172,11 @@
 	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
 	
 	if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+		    csum_fold(csum_partial((char*)uh, ulen, skb->csum))) 
+			goto csum_error;
+#endif
   		udp_statistics.UdpNoPorts++;
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
@@ -1185,6 +1189,19 @@
   	}
 	udp_deliver(sk, skb);
 	return 0;
+
+csum_error:
+	/* 
+	 * RFC1122: OK.  Discards the bad packet silently (as far as 
+	 * the network is concerned, anyway) as per 4.1.3.4 (MUST). 
+	 */
+	NETDEBUG(printk(KERN_DEBUG "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);
+	return(0);
 }
 
 struct proto udp_prot = {
@@ -1214,7 +1231,7 @@
 	udp_v4_verify_bind,		/* verify_bind */
 	128,				/* max_header */
 	0,				/* retransmits */
-	"UDP",				/* name */
+ 	"UDP",				/* name */
 	0,				/* inuse */
 	0				/* highestinuse */
 };

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov