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

Next file: linux/net/ipx/af_ipx.c
Previous file: linux/net/ipv6/tcp_ipv6.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.10/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.40 1999/05/08 20:00:32 davem Exp $
+ *	$Id: udp.c,v 1.40.2.1 1999/06/20 20:14:55 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -49,104 +49,101 @@
 /* Grrr, addr_type already calculated by caller, but I don't want
  * to add some silly "cookie" argument to this method just for that.
  */
-static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+static int udp_v6_get_port(struct sock *sk, unsigned short snum)
 {
-	struct sock *sk2;
-	int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
-	int retval = 0, sk_reuse = sk->reuse;
-
 	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(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
-				if((!sk2_reuse)			||
-				   (!sk_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
-			} else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-						 &sk2->net_pinfo.af_inet6.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:
+		}
+		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;
+		int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+		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 ||
+			     addr_type == IPV6_ADDR_ANY ||
+			     !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+					    &sk2->net_pinfo.af_inet6.rcv_saddr)) &&
+			    (!sk2->reuse || !sk->reuse))
+				goto fail;
 		}
 	}
+
+	sk->num = snum;
+	SOCKHASH_UNLOCK();
+	return 0;
+
+fail:
 	SOCKHASH_UNLOCK();
-	return retval;
+	return 1;
 }
 
 static void udp_v6_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_v6_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 (sk->pprev) {
+		if (sk->next)
+			sk->next->pprev = sk->pprev;
+		*sk->pprev = sk->next;
+		sk->pprev = NULL;
 	}
 	SOCKHASH_UNLOCK();
 }
 
-static void udp_v6_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);
-	}
-	sk->next = udp_hash[num];
-	udp_hash[num] = sk;
-	sk->hashent = num;
-	SOCKHASH_UNLOCK();
-}
-
 static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
 				  struct in6_addr *daddr, u16 dport, int dif)
 {
@@ -893,7 +890,6 @@
 	"UDPv6"			/* name			*/
 };
 
-
 struct proto udpv6_prot = {
 	(struct sock *)&udpv6_prot,	/* sklist_next */
 	(struct sock *)&udpv6_prot,	/* sklist_prev */
@@ -916,9 +912,7 @@
 	udpv6_queue_rcv_skb,		/* backlog_rcv */
 	udp_v6_hash,			/* hash */
 	udp_v6_unhash,			/* unhash */
-	udp_v6_rehash,			/* rehash */
-	udp_good_socknum,		/* good_socknum */
-	udp_v6_verify_bind,		/* verify_bind */
+	udp_v6_get_port,		/* get_port */
 	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)