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

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

diff -u --recursive --new-file v2.2.10/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.66 1999/05/08 20:00:25 davem Exp $
+ * Version:	$Id: udp.c,v 1.66.2.3 1999/08/07 10:56:36 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -123,108 +123,75 @@
 
 struct sock *udp_hash[UDP_HTABLE_SIZE];
 
-static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
-{
-	struct sock *sk2;
-	int retval = 0, sk_reuse = sk->reuse;
+/* Shared by v4/v6 udp. */
+int udp_port_rover = 0;
 
+static int udp_v4_get_port(struct sock *sk, unsigned short snum)
+{
 	SOCKHASH_LOCK();
-	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
-		if((sk2->num == snum) && (sk2 != sk)) {
-			unsigned char state = sk2->state;
-			int sk2_reuse = sk2->reuse;
-
-			/* Two sockets can be bound to the same port if they're
-			 * bound to different interfaces.
-			 */
+	if (snum == 0) {
+		int best_size_so_far, best, result, i;
 
-			if(sk2->bound_dev_if != sk->bound_dev_if)
-				continue;
-
-			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
-				if((!sk2_reuse)			||
-				   (!sk_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
-			} else if(sk2->rcv_saddr == sk->rcv_saddr) {
-				if((!sk_reuse)			||
-				   (!sk2_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
+		if (udp_port_rover > sysctl_local_port_range[1] ||
+		    udp_port_rover < sysctl_local_port_range[0])
+			udp_port_rover = sysctl_local_port_range[0];
+		best_size_so_far = 32767;
+		best = result = udp_port_rover;
+		for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+			struct sock *sk;
+			int size;
+
+			sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+			if (!sk) {
+				if (result > sysctl_local_port_range[1])
+					result = sysctl_local_port_range[0] +
+						((result - sysctl_local_port_range[0]) &
+						 (UDP_HTABLE_SIZE - 1));
+				goto gotit;
 			}
+			size = 0;
+			do {
+				if (++size >= best_size_so_far)
+					goto next;
+			} while ((sk = sk->next) != NULL);
+			best_size_so_far = size;
+			best = result;
+		next:
 		}
-	}
-	SOCKHASH_UNLOCK();
-	return retval;
-}
-
-static inline int udp_lport_inuse(u16 num)
-{
-	struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+		result = best;
+		for(;; result += UDP_HTABLE_SIZE) {
+			if (result > sysctl_local_port_range[1])
+				result = sysctl_local_port_range[0]
+					+ ((result - sysctl_local_port_range[0]) &
+					   (UDP_HTABLE_SIZE - 1));
+			if (!udp_lport_inuse(result))
+				break;
+		}
+gotit:
+		udp_port_rover = snum = result;
+	} else {
+		struct sock *sk2;
 
-	for(; sk != NULL; sk = sk->next) {
-		if(sk->num == num)
-			return 1;
+		for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+		     sk2 != NULL;
+		     sk2 = sk2->next) {
+			if (sk2->num == snum &&
+			    sk2 != sk &&
+			    sk2->bound_dev_if == sk->bound_dev_if &&
+			    (!sk2->rcv_saddr ||
+			     !sk->rcv_saddr ||
+			     sk2->rcv_saddr == sk->rcv_saddr) &&
+			    (!sk2->reuse || !sk->reuse))
+				goto fail;
+		}
 	}
+	sk->num = snum;
+	SOCKHASH_UNLOCK();
 	return 0;
-}
-
-/* Shared by v4/v6 tcp. */
-unsigned short udp_good_socknum(void)
-{
-	int result;
-	static int start = 0;
-	int i, best, best_size_so_far;
-
-	SOCKHASH_LOCK();
-        if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0])
-                start = sysctl_local_port_range[0];
 
-	best_size_so_far = 32767;	/* "big" num */
-        best = result = start;
-
-        for(i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
-		struct sock *sk;
-		int size;
-
-		sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
-
-                if(!sk) {
-                        if (result > sysctl_local_port_range[1])
-                                result = sysctl_local_port_range[0]
-                                        + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1));
-			goto out;
-                }
-
-		/* Is this one better than our best so far? */
-		size = 0;
-		do {
-			if(++size >= best_size_so_far)
-				goto next;
-		} while((sk = sk->next) != NULL);
-		best_size_so_far = size;
-		best = result;
-        next:
-	}
-
-	result = best;
-
-        for(;; result += UDP_HTABLE_SIZE) {
-                /* Get into range (but preserve hash bin)... */
-                if (result > sysctl_local_port_range[1])
-                        result = sysctl_local_port_range[0]
-                                + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1));
-                if (!udp_lport_inuse(result))
-                        break;
-        }
-out:
-	start = result;
+fail:
 	SOCKHASH_UNLOCK();
-	return result;
+	return 1;
 }
 
 /* Last hit UDP socket cache, this is ipv4 specific so make it static. */
@@ -234,62 +201,27 @@
 
 static void udp_v4_hash(struct sock *sk)
 {
-	struct sock **skp;
-	int num = sk->num;
-
-	num &= (UDP_HTABLE_SIZE - 1);
-	skp = &udp_hash[num];
+	struct sock **skp = &udp_hash[sk->num & (UDP_HTABLE_SIZE - 1)];
 
 	SOCKHASH_LOCK();
-	sk->next = *skp;
+	if ((sk->next = *skp) != NULL)
+		(*skp)->pprev = &sk->next;
 	*skp = sk;
-	sk->hashent = num;
+	sk->pprev = skp;
 	SOCKHASH_UNLOCK();
 }
 
 static void udp_v4_unhash(struct sock *sk)
 {
-	struct sock **skp;
-	int num = sk->num;
-
-	num &= (UDP_HTABLE_SIZE - 1);
-	skp = &udp_hash[num];
-
 	SOCKHASH_LOCK();
-	while(*skp != NULL) {
-		if(*skp == sk) {
-			*skp = sk->next;
-			break;
-		}
-		skp = &((*skp)->next);
-	}
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-	SOCKHASH_UNLOCK();
-}
-
-static void udp_v4_rehash(struct sock *sk)
-{
-	struct sock **skp;
-	int num = sk->num;
-	int oldnum = sk->hashent;
-
-	num &= (UDP_HTABLE_SIZE - 1);
-	skp = &udp_hash[oldnum];
-
-	SOCKHASH_LOCK();
-	while(*skp != NULL) {
-		if(*skp == sk) {
-			*skp = sk->next;
-			break;
-		}
-		skp = &((*skp)->next);
+	if (sk->pprev) {
+		if (sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
+		if(uh_cache_sk == sk)
+			uh_cache_sk = NULL;
 	}
-	sk->next = udp_hash[num];
-	udp_hash[num] = sk;
-	sk->hashent = num;
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
 	SOCKHASH_UNLOCK();
 }
 
@@ -755,8 +687,15 @@
 		connected = 0;
 	}
 
-	if (connected)
-		rt = (struct rtable*)dst_clone(sk->dst_cache);
+	if (connected && sk->dst_cache) {
+		rt = (struct rtable*)sk->dst_cache;
+		if (rt->u.dst.obsolete) {
+			sk->dst_cache = NULL;
+			dst_release(&rt->u.dst);
+			rt = NULL;
+		} else
+			dst_clone(&rt->u.dst);
+	}
 
 	if (rt == NULL) {
 		err = ip_route_output(&rt, daddr, ufh.saddr,
@@ -770,6 +709,8 @@
 		err = -EACCES;
 		if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) 
 			goto out;
+		if (connected && sk->dst_cache == NULL)
+			sk->dst_cache = dst_clone(&rt->u.dst);
 	}
 
 	ufh.saddr = rt->rt_src;
@@ -810,7 +751,6 @@
 		{
 			unsigned long amount;
 
-			if (sk->state == TCP_LISTEN) return(-EINVAL);
 			amount = sock_wspace(sk);
 			return put_user(amount, (int *)arg);
 		}
@@ -820,8 +760,6 @@
 			struct sk_buff *skb;
 			unsigned long amount;
 
-			if (sk->state == TCP_LISTEN)
-				return(-EINVAL);
 			amount = 0;
 			/* N.B. Is this interrupt safe??
 			   -> Yes. Interrupts do not remove skbs. --ANK (980725)
@@ -860,12 +798,6 @@
   	struct sk_buff *skb;
   	int copied, err;
 
-	/*
-	 *	Check any passed addresses
-	 */
-  	if (addr_len) 
-  		*addr_len=sizeof(*sin);
-
 	if (flags & MSG_ERRQUEUE)
 		return ip_recv_error(sk, msg, len);
 
@@ -916,6 +848,12 @@
 	/* Copy the address. */
 	if (sin)
 	{
+		/*
+		 *	Check any passed addresses
+		 */
+		if (addr_len) 
+			*addr_len=sizeof(*sin);
+
 		sin->sin_family = AF_INET;
 		sin->sin_port = skb->h.uh->source;
 		sin->sin_addr.s_addr = skb->nh.iph->saddr;
@@ -1244,9 +1182,7 @@
 	udp_queue_rcv_skb,		/* backlog_rcv */
 	udp_v4_hash,			/* hash */
 	udp_v4_unhash,			/* unhash */
-	udp_v4_rehash,			/* rehash */
-	udp_good_socknum,		/* good_socknum */
-	udp_v4_verify_bind,		/* verify_bind */
+	udp_v4_get_port,		/* good_socknum */
 	128,				/* max_header */
 	0,				/* retransmits */
  	"UDP",				/* name */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)