svn commit: r189956 - in stable/7: . sys sys/contrib/pf sys/dev/ath/ath_hal sys/dev/cxgb sys/netinet sys/netinet6

Bjoern A. Zeeb bz at FreeBSD.org
Wed Mar 18 04:30:48 PDT 2009


Author: bz
Date: Wed Mar 18 11:30:47 2009
New Revision: 189956
URL: http://svn.freebsd.org/changeset/base/189956

Log:
  MFC r184096:
  
    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:	rwatson (UPDATING)

Modified:
  stable/7/UPDATING
  stable/7/sys/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/ath/ath_hal/   (props changed)
  stable/7/sys/dev/cxgb/   (props changed)
  stable/7/sys/netinet/in_pcb.h
  stable/7/sys/netinet6/in6_src.c

Modified: stable/7/UPDATING
==============================================================================
--- stable/7/UPDATING	Wed Mar 18 03:56:26 2009	(r189955)
+++ stable/7/UPDATING	Wed Mar 18 11:30:47 2009	(r189956)
@@ -8,6 +8,13 @@ Items affecting the ports and packages s
 /usr/ports/UPDATING.  Please read that file before running
 portupgrade.
 
+20090318:
+	Change IPv6 ephemeral port allocation from sequential to
+	random allocation, like IPv4 has done for more than four years.
+ 	The implementation shares infrastructure with IPv4. This
+	means that there is only one set of sysctls to control both
+	IPv4 and IPv6. See ip(4) man page for details.
+
 20090312:
 	A workaround is committed to allow the creation of System V shared
 	memory segment of size > 2 GB on the 64-bit architectures.

Modified: stable/7/sys/netinet/in_pcb.h
==============================================================================
--- stable/7/sys/netinet/in_pcb.h	Wed Mar 18 03:56:26 2009	(r189955)
+++ stable/7/sys/netinet/in_pcb.h	Wed Mar 18 11:30:47 2009	(r189956)
@@ -483,6 +483,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: stable/7/sys/netinet6/in6_src.c
==============================================================================
--- stable/7/sys/netinet6/in6_src.c	Wed Mar 18 03:56:26 2009	(r189955)
+++ stable/7/sys/netinet6/in6_src.c	Wed Mar 18 11:30:47 2009	(r189956)
@@ -91,6 +91,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
 {
 	struct socket *so = inp->inp_socket;
 	u_int16_t lport = 0, first, last, *lastport;
-	int count, error, wild = 0;
+	int count, error, wild = 0, dorandom;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
 
 	INP_INFO_WLOCK_ASSERT(pcbinfo);
@@ -807,56 +810,58 @@ in6_pcbsetport(struct in6_addr *laddr, s
 		last  = 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 (ipport_randomized &&
+	    (!ipport_stoprandom || pcbinfo == &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 != &udbinfo)
+		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-all mailing list