svn commit: r184096 - in head/sys: netinet netinet6

Bjoern A. Zeeb bz at FreeBSD.org
Mon Oct 20 18:44:00 UTC 2008


Author: bz
Date: Mon Oct 20 18:43:59 2008
New Revision: 184096
URL: http://svn.freebsd.org/changeset/base/184096

Log:
  Bring over the change switching from using sequential to random
  ephemeral port allocation as implemented in netinet/in_pcb.c rev. 1.143
  (initially from OpenBSD) and follow-up commits during the last four and
  a half years including rev. 1.157, 1.162 and 1.199.
  This now is relying on the same infrastructure as has been implemented
  in in_pcb.c since rev. 1.199.
  
  Reviewed by:	silby, rpaulo, mlaier
  MFC after:	2 months

Modified:
  head/sys/netinet/in_pcb.h
  head/sys/netinet6/in6_src.c

Modified: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h	Mon Oct 20 18:37:10 2008	(r184095)
+++ head/sys/netinet/in_pcb.h	Mon Oct 20 18:43:59 2008	(r184096)
@@ -449,6 +449,9 @@ extern int	ipport_firstauto;
 extern int	ipport_lastauto;
 extern int	ipport_hifirstauto;
 extern int	ipport_hilastauto;
+extern int	ipport_randomized;
+extern int	ipport_stoprandom;
+extern int	ipport_tcpallocs;
 extern struct callout ipport_tick_callout;
 
 void	in_pcbpurgeif0(struct inpcbinfo *, struct ifnet *);

Modified: head/sys/netinet6/in6_src.c
==============================================================================
--- head/sys/netinet6/in6_src.c	Mon Oct 20 18:37:10 2008	(r184095)
+++ head/sys/netinet6/in6_src.c	Mon Oct 20 18:43:59 2008	(r184096)
@@ -95,6 +95,9 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
 #include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/in6_pcb.h>
@@ -774,7 +777,7 @@ in6_pcbsetport(struct in6_addr *laddr, s
 	INIT_VNET_INET(curvnet);
 	struct socket *so = inp->inp_socket;
 	u_int16_t lport = 0, first, last, *lastport;
-	int count, error = 0, wild = 0;
+	int count, error = 0, wild = 0, dorandom;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
 
 	INP_INFO_WLOCK_ASSERT(pcbinfo);
@@ -802,56 +805,58 @@ in6_pcbsetport(struct in6_addr *laddr, s
 		last  = V_ipport_lastauto;
 		lastport = &pcbinfo->ipi_lastport;
 	}
+
 	/*
-	 * Simple check to ensure all ports are not used up causing
-	 * a deadlock here.
-	 *
-	 * We split the two cases (up and down) so that the direction
-	 * is not being tested on each round of the loop.
+	 * For UDP, use random port allocation as long as the user
+	 * allows it.  For TCP (and as of yet unknown) connections,
+	 * use random port allocation only if the user allows it AND
+	 * ipport_tick() allows it.
 	 */
-	if (first > last) {
-		/*
-		 * counting down
-		 */
-		count = first - last;
+	if (V_ipport_randomized &&
+	    (!V_ipport_stoprandom || pcbinfo == &V_udbinfo))
+		dorandom = 1;
+	else
+		dorandom = 0;
+	/*
+	 * It makes no sense to do random port allocation if
+	 * we have the only port available.
+	 */
+	if (first == last)
+		dorandom = 0;
+	/* Make sure to not include UDP packets in the count. */
+	if (pcbinfo != &V_udbinfo)
+		V_ipport_tcpallocs++;
 
-		do {
-			if (count-- < 0) {	/* completely used? */
-				/*
-				 * Undo any address bind that may have
-				 * occurred above.
-				 */
-				inp->in6p_laddr = in6addr_any;
-				return (EAGAIN);
-			}
-			--*lastport;
-			if (*lastport > first || *lastport < last)
-				*lastport = first;
-			lport = htons(*lastport);
-		} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
-		    lport, wild, cred));
-	} else {
-		/*
-			 * counting up
-			 */
-		count = last - first;
+	/*
+	 * Instead of having two loops further down counting up or down
+	 * make sure that first is always <= last and go with only one
+	 * code path implementing all logic.
+	 */
+	if (first > last) {
+		u_int16_t aux;
 
-		do {
-			if (count-- < 0) {	/* completely used? */
-				/*
-				 * Undo any address bind that may have
-				 * occurred above.
-				 */
-				inp->in6p_laddr = in6addr_any;
-				return (EAGAIN);
-			}
-			++*lastport;
-			if (*lastport < first || *lastport > last)
-				*lastport = first;
-			lport = htons(*lastport);
-		} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
-		    lport, wild, cred));
-	}
+		aux = first;
+		first = last;
+		last = aux;
+	}
+
+	if (dorandom)
+		*lastport = first + (arc4random() % (last - first));
+
+	count = last - first;
+
+	do {
+		if (count-- < 0) {	/* completely used? */
+			/* Undo an address bind that may have occurred. */
+			inp->in6p_laddr = in6addr_any;
+			return (EADDRNOTAVAIL);
+		}
+		++*lastport;
+		if (*lastport < first || *lastport > last)
+			*lastport = first;
+		lport = htons(*lastport);
+	} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+	    lport, wild, cred));
 
 	inp->inp_lport = lport;
 	if (in_pcbinshash(inp) != 0) {


More information about the svn-src-head mailing list