Hello,

I would like to thank for the help and for the recommendations.

I attach second version of the patch, I proposed earlier, including
following changes:

1) All RFC6056 algorithms are implemented.
2) Both IPv4 and IPv6 stacks are modified to use the new port
randomization code.
3) There are two variables that can be modified via sysctl:
- net.inet.ip.portrange.rfc6056_algorithm - which allows the super
user to choose one out of the five possible algorithms.
- net.inet.ip.portrange.rfc6056_algorithm5_tradeoff - which allows the
super user to modify the trade-off value used in algorithm 5.
All values are explicitly checked for correctness before usage.
Default values for those variables represent current/legacy port
randomization algorithm and proposed values in the RFC itself.

Thank you very much.

Ivo Vachkov
diff -r 4ea077f546dd src/sys/netinet/in_pcb.c
--- a/src/sys/netinet/in_pcb.c	Fri Jan 28 12:21:08 2011 +0200
+++ b/src/sys/netinet/in_pcb.c	Fri Jan 28 16:10:08 2011 +0200
@@ -81,6 +81,8 @@
 #include <netipsec/key.h>
 #endif /* IPSEC */
 
+#include <sys/md5.h>	
+
 #include <security/mac/mac_framework.h>
 
 /*
@@ -109,6 +111,8 @@
 VNET_DEFINE(int, ipport_stoprandom);		/* toggled by ipport_tick */
 VNET_DEFINE(int, ipport_tcpallocs);
 static VNET_DEFINE(int, ipport_tcplastcount);
+VNET_DEFINE(u_int, ipport_rfc6056alg) = 1;	/* user controlled via sysctl */
+VNET_DEFINE(u_int, ipport_rfc6056alg5_tradeoff) = 500;	/* user controlled via sysctl */
 
 #define	V_ipport_tcplastcount		VNET(ipport_tcplastcount)
 
@@ -141,6 +145,65 @@
 
 #undef RANGECHK
 
+/*
+ * Updates V_ipport_rfc6056alg to the provided value and
+ * ensures it is in the supported range (1 - 5)
+ */
+static int
+sysctl_net_rfc6056_algorithm_check(SYSCTL_HANDLER_ARGS)
+{
+	u_int algorithm = *(u_int *)arg1;
+	int error;
+
+#ifdef VIMAGE
+	error = vnet_sysctl_handle_uint(oidp, &algorithm, 0, req);
+#else
+	error = sysctl_handle_int(oidp, &algorithm, 0, req);
+#endif
+
+	if (error == 0) {
+		switch (algorithm) {
+		case INP_RFC6056_ALG_1:
+		case INP_RFC6056_ALG_2:
+		case INP_RFC6056_ALG_3:
+		case INP_RFC6056_ALG_4:
+		case INP_RFC6056_ALG_5:
+			V_ipport_rfc6056alg = algorithm;
+			break;
+		default:
+			return (EINVAL);
+		}
+	} 	
+
+	return (error);
+}
+
+/*
+ * Updates V_ipport_rfc6056alg5_tradeoff to provided value
+ * and ensures it is in the supported range (1 - 65536)
+ */
+static int
+sysctl_net_rfc6056alg5_tradeoff_check(SYSCTL_HANDLER_ARGS)
+{
+	u_int tradeoff = *(u_int *)arg1;
+	int error;
+
+#ifdef VIMAGE
+	error = vnet_sysctl_handle_uint(oidp, &tradeoff, 0, req);
+#else
+	error = sysctl_handle_int(oidp, &tradeoff, 0, req);
+#endif
+
+	if (error == 0) {
+		if (tradeoff < 1 || tradeoff > 65536)
+			return (EINVAL);
+		else
+			V_ipport_rfc6056alg5_tradeoff = tradeoff;
+	}
+
+	return (error);
+}
+
 SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports");
 
 SYSCTL_VNET_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst,
@@ -174,6 +237,15 @@
 	&VNET_NAME(ipport_randomtime), 0,
 	"Minimum time to keep sequental port "
 	"allocation before switching to a random one");
+SYSCTL_VNET_PROC(_net_inet_ip_portrange, OID_AUTO, rfc6056_algorithm,
+	CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(ipport_rfc6056alg), 0,
+	&sysctl_net_rfc6056_algorithm_check, "IU", 
+	"RFC 6056 Port randomization algorithm");
+SYSCTL_VNET_PROC(_net_inet_ip_portrange, OID_AUTO,
+	rfc6056_algorithm5_tradeoff, CTLTYPE_UINT|CTLFLAG_RW,
+	&VNET_NAME(ipport_rfc6056alg5_tradeoff), 0,
+	&sysctl_net_rfc6056alg5_tradeoff_check, "IU",
+	"RFC 6056 Algorithm 5 computational trade-off");
 
 /*
  * in_pcb.c: manage the Protocol Control Blocks.
@@ -468,21 +540,177 @@
 			last = aux;
 		}
 
-		if (dorandom)
-			*lastport = first +
-				    (arc4random() % (last - first));
-
 		count = last - first;
 
-		do {
-			if (count-- < 0)	/* completely used? */
-				return (EADDRNOTAVAIL);
-			++*lastport;
-			if (*lastport < first || *lastport > last)
-				*lastport = first;
-			lport = htons(*lastport);
-		} while (in_pcblookup_local(pcbinfo, laddr,
-		    lport, wild, cred));
+		/* 
+		 * According to RFC6056 there are 5 (five) possible algorithms
+		 * for random port allocation. Usage of a particular algorithm
+		 * is specified with the 'net.inet.ip.portrange.rfc6056_algorithm'
+		 * sysctl variable. Default value is 1, which represents the
+		 * legacy random port allocation algorithm in FreeBSD.
+		 */
+		if (dorandom) {
+			switch (V_ipport_rfc6056alg) {
+			case INP_RFC6056_ALG_5:	/* Random-Increments Port Selection */
+				do {
+					if (count-- < 0)	/* completely used? */
+						return (EADDRNOTAVAIL);
+
+					*lastport = first + ((arc4random() % 65536) + 
+					    (arc4random() % V_ipport_rfc6056alg5_tradeoff) + 1);
+
+					if (*lastport < first || *lastport > last)
+						*lastport = first;
+					lport = htons(*lastport);
+				} while (in_pcblookup_local(pcbinfo, laddr,
+				    lport, wild, cred));
+				break;
+			case INP_RFC6056_ALG_4:	/* Double-Hash Port Selection Algorithm */
+				{
+					MD5_CTX f_ctx;
+					MD5_CTX g_ctx;
+					u_int32_t F[4] = { 0, 0, 0, 0 };
+					u_int32_t G[4] = { 0, 0, 0, 0 };
+					u_int32_t secret_f[4] = { 0, 0, 0, 0 };
+					u_int32_t secret_g[4] = { 0, 0, 0, 0 };
+					u_int16_t table[16]; 
+					u_int32_t index = 0;
+					u_int32_t offset = 0;
+
+					secret_f[0] = arc4random();
+					secret_f[1] = arc4random();
+					secret_f[2] = arc4random();
+					secret_f[3] = arc4random();
+
+					secret_g[0] = arc4random();
+					secret_g[1] = arc4random();
+					secret_g[2] = arc4random();
+					secret_g[3] = arc4random();
+
+					for (index = 0; index < sizeof(table); index++)
+						table[index] = arc4random() % 65536;
+
+					MD5Init(&f_ctx);
+					MD5Update(&f_ctx, (u_char *)&inp->inp_laddr,
+					    sizeof(inp->inp_laddr));
+					MD5Update(&f_ctx, (u_char *)&inp->inp_faddr,
+					    sizeof(inp->inp_faddr));
+					MD5Update(&f_ctx, (u_char *)&inp->inp_fport,
+					    sizeof(inp->inp_fport));
+					MD5Update(&f_ctx, (u_char *)secret_f,
+					    sizeof(secret_f));
+					MD5Final((u_char *)&F, &f_ctx);
+
+					offset = ((F[0] ^ F[1]) ^ (F[2] ^ F[3]));
+
+					MD5Init(&g_ctx);
+					MD5Update(&g_ctx, (u_char *)&inp->inp_laddr,
+					    sizeof(inp->inp_laddr));
+					MD5Update(&g_ctx, (u_char *)&inp->inp_faddr,
+					    sizeof(inp->inp_faddr));
+					MD5Update(&g_ctx, (u_char *)&inp->inp_fport,
+					    sizeof(inp->inp_fport));
+					MD5Update(&g_ctx, (u_char *)secret_g,
+					    sizeof(secret_g));
+					MD5Final((u_char *)&G, &g_ctx);
+
+					index = ((G[0] ^ G[1]) ^ (G[2] ^ G[3])) % sizeof(table);
+
+					do {
+						if (count-- < 0)	/* completely used? */
+							return (EADDRNOTAVAIL);
+
+						*lastport = first + 
+						    (offset + table[index]++) % count;
+
+						if (*lastport < first || *lastport > last)
+							*lastport = first;
+						lport = htons(*lastport);
+					} while (in_pcblookup_local(pcbinfo, laddr,
+					    lport, wild, cred));
+				}
+				break;
+			case INP_RFC6056_ALG_3:	/* Simple Hash-Based Port Selection Algorithm */
+				{
+					MD5_CTX f_ctx;
+					u_int32_t F[4] = { 0, 0, 0, 0 };
+					u_int32_t secret_f[4] = { 0, 0, 0, 0 };
+					u_int32_t offset = 0;
+
+					secret_f[0] = arc4random();
+					secret_f[1] = arc4random();
+					secret_f[2] = arc4random();
+					secret_f[3] = arc4random();
+
+					MD5Init(&f_ctx);
+					MD5Update(&f_ctx, (u_char *)&inp->inp_laddr,
+					    sizeof(inp->inp_laddr));
+					MD5Update(&f_ctx, (u_char *)&inp->inp_faddr,
+					    sizeof(inp->inp_faddr));
+					MD5Update(&f_ctx, (u_char *)&inp->inp_fport,
+					    sizeof(inp->inp_fport));
+					MD5Update(&f_ctx, (u_char *)secret_f,
+					    sizeof(secret_f));
+					MD5Final((u_char *)&F, &f_ctx);
+
+					offset = ((F[0] ^ F[1]) ^ (F[2] ^ F[3]));
+
+					do {
+						if (count-- < 0)	/* completely used? */
+							return (EADDRNOTAVAIL);
+
+						*lastport = first + ((arc4random() % 65536) + 
+						    (offset % 65536)) % count;
+
+						if (*lastport < first || *lastport > last)
+							*lastport = first;
+						lport = htons(*lastport);
+					} while (in_pcblookup_local(pcbinfo, laddr,
+					    lport, wild, cred));				
+				}
+				break;
+			case INP_RFC6056_ALG_2:	/* Simple Port Randomization Algorithm II */
+				do {
+					if (count-- < 0)	/* completely used? */
+						return (EADDRNOTAVAIL);
+
+					*lastport = first + (arc4random() % (last - first));
+
+					if (*lastport < first || *lastport > last)
+						*lastport = first;
+					lport = htons(*lastport);
+				} while (in_pcblookup_local(pcbinfo, laddr,
+				    lport, wild, cred));
+				break;
+			case INP_RFC6056_ALG_1:	/* Simple Port Randomization Algorithm I */
+			default:
+				*lastport = first + (arc4random() % (last - first));
+
+				do {
+					if (count-- < 0)	/* completely used? */
+						return (EADDRNOTAVAIL);
+
+					++*lastport;
+
+					if (*lastport < first || *lastport > last)
+						*lastport = first;
+					lport = htons(*lastport);
+				} while (in_pcblookup_local(pcbinfo, laddr,
+				    lport, wild, cred));
+			}
+		} else {
+			do {
+				if (count-- < 0)        /* completely used? */
+					return (EADDRNOTAVAIL);
+	
+				++*lastport;
+
+				if (*lastport < first || *lastport > last)
+					*lastport = first;
+				lport = htons(*lastport);
+			} while (in_pcblookup_local(pcbinfo, laddr,
+			    lport, wild, cred));
+		}
 	}
 	*laddrp = laddr.s_addr;
 	*lportp = lport;
diff -r 4ea077f546dd src/sys/netinet/in_pcb.h
--- a/src/sys/netinet/in_pcb.h	Fri Jan 28 12:21:08 2011 +0200
+++ b/src/sys/netinet/in_pcb.h	Fri Jan 28 16:10:08 2011 +0200
@@ -452,6 +452,15 @@
 
 #define	INP_CHECK_SOCKAF(so, af)	(INP_SOCKAF(so) == af)
 
+/*
+ * RFC6056 Port Randomization Algorithms
+ */
+#define	INP_RFC6056_ALG_1	1	/* Simple Port Randomization Algorithm I */
+#define	INP_RFC6056_ALG_2	2	/* Simple Port Randomization Algorithm II */
+#define	INP_RFC6056_ALG_3	3	/* Simple Hash-Based Port Selection Algorithm */
+#define	INP_RFC6056_ALG_4	4	/* Double-Hash Port Selection Algorithm */
+#define	INP_RFC6056_ALG_5	5	/* Random-Increments Port Selection Algorithm */
+
 #ifdef _KERNEL
 VNET_DECLARE(int, ipport_reservedhigh);
 VNET_DECLARE(int, ipport_reservedlow);
@@ -466,6 +475,8 @@
 VNET_DECLARE(int, ipport_randomtime);
 VNET_DECLARE(int, ipport_stoprandom);
 VNET_DECLARE(int, ipport_tcpallocs);
+VNET_DECLARE(u_int, ipport_rfc6056alg);
+VNET_DECLARE(u_int, ipport_rfc6056alg5_tradeoff);
 
 #define	V_ipport_reservedhigh	VNET(ipport_reservedhigh)
 #define	V_ipport_reservedlow	VNET(ipport_reservedlow)
@@ -480,6 +491,8 @@
 #define	V_ipport_randomtime	VNET(ipport_randomtime)
 #define	V_ipport_stoprandom	VNET(ipport_stoprandom)
 #define	V_ipport_tcpallocs	VNET(ipport_tcpallocs)
+#define V_ipport_rfc6056alg	VNET(ipport_rfc6056alg)
+#define V_ipport_rfc6056alg5_tradeoff	VNET(ipport_rfc6056alg5_tradeoff)
 
 extern struct callout ipport_tick_callout;
 
diff -r 4ea077f546dd src/sys/netinet6/in6_src.c
--- a/src/sys/netinet6/in6_src.c	Fri Jan 28 12:21:08 2011 +0200
+++ b/src/sys/netinet6/in6_src.c	Fri Jan 28 16:10:08 2011 +0200
@@ -108,6 +108,8 @@
 #include <netinet6/scope6_var.h>
 #include <netinet6/nd6.h>
 
+#include <sys/md5.h>
+
 static struct mtx addrsel_lock;
 #define	ADDRSEL_LOCK_INIT()	mtx_init(&addrsel_lock, "addrsel_lock", NULL, MTX_DEF)
 #define	ADDRSEL_LOCK()		mtx_lock(&addrsel_lock)
@@ -919,23 +921,195 @@
 		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);
+	/*
+	 * According to RFC6056 there are 5 (five) possible algorithms
+	 * for random port allocation. Usage of a particular algorithm
+	 * is specified with the 'net.inet.ip.portrange.rfc6056_algorithm'
+	 * sysctl variable. Default value is 1, which represents the
+	 * legacy random port allocation algorithm in FreeBSD.
+	 */
+	if (dorandom) {
+		switch (V_ipport_rfc6056alg) {
+		case INP_RFC6056_ALG_5:	/* Random-Increments Port Selection */
+			do {
+				if (count-- < 0) {	/* completely used? */
+					/* Undo an address bind that may have occurred. */
+					inp->in6p_laddr = in6addr_any;
+					return (EADDRNOTAVAIL);
+				}
+
+				*lastport = first + ((arc4random() % 65536) +
+				    (arc4random() % V_ipport_rfc6056alg5_tradeoff) + 1);
+
+				if (*lastport < first || *lastport > last)
+					*lastport = first;
+				lport = htons(*lastport);
+			} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+			    lport, wild, cred));
+			break;
+		case INP_RFC6056_ALG_4: /* Double-Hash Port Selection Algorithm */
+			{
+				MD5_CTX f_ctx;
+				MD5_CTX g_ctx;
+				u_int32_t F[4] = { 0, 0, 0, 0 };
+				u_int32_t G[4] = { 0, 0, 0, 0 };
+				u_int32_t secret_f[4] = { 0, 0, 0, 0 };
+				u_int32_t secret_g[4] = { 0, 0, 0, 0 };
+				u_int16_t table[16];
+				u_int32_t index = 0;
+				u_int32_t offset = 0;
+
+				secret_f[0] = arc4random();
+				secret_f[1] = arc4random();
+				secret_f[2] = arc4random();
+				secret_f[3] = arc4random();
+
+				secret_g[0] = arc4random();
+				secret_g[1] = arc4random();
+				secret_g[2] = arc4random();
+				secret_g[3] = arc4random();
+
+				for (index = 0; index < sizeof(table); index++)
+					table[index] = arc4random() % 65536;
+
+				MD5Init(&f_ctx);
+				MD5Update(&f_ctx, (u_char *)&inp->in6p_laddr,
+				    sizeof(inp->in6p_laddr));
+				MD5Update(&f_ctx, (u_char *)&inp->in6p_faddr,
+				    sizeof(inp->in6p_faddr));
+				MD5Update(&f_ctx, (u_char *)&inp->inp_fport,
+				    sizeof(inp->inp_fport));
+				MD5Update(&f_ctx, (u_char *)secret_f,
+				    sizeof(secret_f));
+				MD5Final((u_char *)&F, &f_ctx);
+
+				offset = ((F[0] ^ F[1]) ^ (F[2] ^ F[3]));
+
+				MD5Init(&g_ctx);
+				MD5Update(&g_ctx, (u_char *)&inp->in6p_laddr,
+				    sizeof(inp->in6p_laddr));
+				MD5Update(&g_ctx, (u_char *)&inp->in6p_faddr,
+				    sizeof(inp->in6p_faddr));
+				MD5Update(&g_ctx, (u_char *)&inp->inp_fport,
+				    sizeof(inp->inp_fport));
+				MD5Update(&g_ctx, (u_char *)secret_g,
+				    sizeof(secret_g));
+				MD5Final((u_char *)&G, &g_ctx);
+
+				index = ((G[0] ^ G[1]) ^ (G[2] ^ G[3])) % sizeof(table);
+
+				do {
+					if (count-- < 0) {	/* completely used? */
+						/* Undo an address bind that may have occurred. */
+						inp->in6p_laddr = in6addr_any;
+						return (EADDRNOTAVAIL);
+					}
+
+					*lastport = first +
+					    (offset + table[index]++) % count;
+
+					if (*lastport < first || *lastport > last)
+						*lastport = first;
+					lport = htons(*lastport);
+				} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+				    lport, wild, cred));
+			}
+			break;
+		case INP_RFC6056_ALG_3: /* Simple Hash-Based Port Selection Algorithm */
+			{
+				MD5_CTX f_ctx;
+				u_int32_t F[4] = { 0, 0, 0, 0 };
+				u_int32_t secret_f[4] = { 0, 0, 0, 0 };
+				u_int32_t offset = 0;
+
+				secret_f[0] = arc4random();
+				secret_f[1] = arc4random();
+				secret_f[2] = arc4random();
+				secret_f[3] = arc4random();
+
+				MD5Init(&f_ctx);
+				MD5Update(&f_ctx, (u_char *)&inp->in6p_laddr,
+				    sizeof(inp->in6p_laddr));
+				MD5Update(&f_ctx, (u_char *)&inp->in6p_faddr,
+				    sizeof(inp->in6p_faddr));
+				MD5Update(&f_ctx, (u_char *)&inp->inp_fport,
+				    sizeof(inp->inp_fport));
+				MD5Update(&f_ctx, (u_char *)secret_f,
+				    sizeof(secret_f));
+				MD5Final((u_char *)&F, &f_ctx);
+
+				offset = ((F[0] ^ F[1]) ^ (F[2] ^ F[3]));
+
+				do {
+					if (count-- < 0) {	/* completely used? */
+						/* Undo an address bind that may have occurred. */
+						inp->in6p_laddr = in6addr_any;
+						return (EADDRNOTAVAIL);
+					}
+
+					*lastport = first + ((arc4random() % 65536) +
+					    (offset % 65536)) % count;
+
+					if (*lastport < first || *lastport > last)
+						*lastport = first;
+					lport = htons(*lastport);
+				} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+				    lport, wild, cred));
+			}
+			break;
+		case INP_RFC6056_ALG_2:	/* Simple Port Randomization Algorithm II */
+			do {
+				if (count-- < 0) {	/* completely used? */
+					/* Undo an address bind that may have occurred. */
+					inp->in6p_laddr = in6addr_any;
+					return (EADDRNOTAVAIL);
+				}
+
+				*lastport = first + (arc4random() % (last - first));
+
+				if (*lastport < first || *lastport > last)
+					*lastport = first;
+				lport = htons(*lastport);
+			} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
+			    lport, wild, cred));
+			break;
+		case INP_RFC6056_ALG_1:	/* Simple Port Randomization Algorithm I */
+		default:
+			*lastport = first + (arc4random() % (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));
 		}
-		++*lastport;
-		if (*lastport < first || *lastport > last)
-			*lastport = first;
-		lport = htons(*lastport);
-	} while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr,
-	    lport, wild, cred));
+	} else {
+		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) {
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to