Hello, I attach the latest version of the port randomization code as a patch against RELENG_8.
Changelog: 1) sysctl variable names are changed to: - 'net.inet.ip.portrange.randomalg.version' - representing the algorithm of choice. - 'net.inet.ip.portrange.randomalg.alg5_tradeoff' - representing the Algorithm 5 computational tradeoff value (the 'N' value in the Algorithm 5 description in the RFC 6056). 2) Code comments are synchronized with the current variable names. Ivo Vachkov On Sat, Jan 29, 2011 at 4:27 AM, Doug Barton <do...@freebsd.org> wrote: > On 01/28/2011 11:57, Ivo Vachkov wrote: >> >> On Fri, Jan 28, 2011 at 9:00 PM, Doug Barton<do...@freebsd.org> wrote: > >>> How does net.inet.ip.portrange.randomalg sound? I would also suggest that >>> the second sysctl be named net.inet.ip.portrange.randomalg.alg5_tradeoff >>> so >>> that one could do 'sysctl net.inet.ip.portrange.randomalg' and see both >>> values. But I won't quibble on that. :) >>> >> >> I have no objections with this. Since this is my first attempt to >> contribute something back to the community I decided to see how it's >> done before. So I found: >> net.inet.tcp.rfc1323 >> net.inet.tcp.rfc3465 >> net.inet.tcp.rfc3390 >> net.inet.tcp.rfc3042 >> which probably led me in a wrong direction :) > > Yeah, I had actually intended to say something to the effect of "there are > plenty of unfortunate examples in the tree already so your doing it that way > is totally understandable" but I trimmed it. > >> I understand your point and agree with it. However, my somewhat >> limited understanding of the sysctl internal organization is telling >> me that tree node does not support values. Am I wrong? > > You are likely correct. :) It's an inconvenient fact that often forget > because that's not the sandbox that I usually play in. > >> If my reasoning >> is correct, maybe I can create the sysctl variables with the following >> names: >> - net.inet.ip.portrange.randomalg (Tree Node) >> - net.inet.ip.portrange.randomalg.alg[orithm] (Leaf Node, to store the >> selected algorithm) > > I would go with "version" to increase the visual distinctiveness. I searched > the current tree and there doesn't seem to be a clear winner for how to > portray "this is the current N/M that is in use" but "version" seems to have > the most representatives. > >> - net.inet.ip.portrange.randomalg.alg5_tradeoff (Leaf Node, to store >> the Algorithm 5 trade-off value) > > I'm assuming this is the "N" value mentioned in the RFC. If so, I commend > you on your choice of "tradeoff" to represent it. :) > > > hth, > > Doug > > -- > > Nothin' ever doesn't change, but nothin' changes much. > -- OK Go > > Breadth of IT experience, and depth of knowledge in the DNS. > Yours for the right price. :) http://SupersetSolutions.com/ > > -- "UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity." Dennis Ritchie
diff -r 0d67f9c982f7 src/sys/netinet/in_pcb.c --- a/src/sys/netinet/in_pcb.c Mon Jan 31 11:35:24 2011 +0200 +++ b/src/sys/netinet/in_pcb.c Mon Jan 31 14:29:52 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_randomalg_ver) = 1; /* user controlled via sysctl */ +VNET_DEFINE(u_int, ipport_randomalg_alg5_tradeoff) = 500; /* user controlled via sysctl */ #define V_ipport_tcplastcount VNET(ipport_tcplastcount) @@ -141,7 +145,68 @@ #undef RANGECHK +/* + * Updates V_ipport_randomalg_ver to the provided value + * and ensures it is in the supported range (1 - 5) + */ +static int +sysctl_net_randomalg_version_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_randomalg_ver = algorithm; + break; + default: + return (EINVAL); + } + } + + return (error); +} + +/* + * Updates V_ipport_randomalg_alg5_tradeoff to provided value + * and ensures it is in the supported range (1 - 65536) + */ +static int +sysctl_net_randomalg_alg5_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_randomalg_alg5_tradeoff = tradeoff; + } + + return (error); +} + SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); +SYSCTL_NODE(_net_inet_ip_portrange, IPPROTO_IP, randomalg, CTLFLAG_RW, 0, + "Port Randomization Algorithms"); SYSCTL_VNET_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, &VNET_NAME(ipport_lowfirstauto), 0, @@ -174,6 +239,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_randomalg, OID_AUTO, version, + CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(ipport_randomalg_ver), 0, + &sysctl_net_randomalg_version_check, "IU", + "RFC 6056 Port randomization algorithm"); +SYSCTL_VNET_PROC(_net_inet_ip_portrange_randomalg, OID_AUTO, + alg5_tradeoff, CTLTYPE_UINT|CTLFLAG_RW, + &VNET_NAME(ipport_randomalg_alg5_tradeoff), 0, + &sysctl_net_randomalg_alg5_tradeoff_check, "IU", + "RFC 6056 Algorithm 5 computational trade-off"); /* * in_pcb.c: manage the Protocol Control Blocks. @@ -468,21 +542,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 RFC 6056 there are 5 (five) possible algorithms + * for random port allocation. Usage of a particular algorithm + * is specified with the 'net.inet.ip.portrange.randomalg.version' + * sysctl variable. Default value is 1, which represents the + * legacy random port allocation algorithm in FreeBSD. + */ + if (dorandom) { + switch (V_ipport_randomalg_ver) { + case INP_RFC6056_ALG_5: /* Random-Increments Port Selection */ + do { + if (count-- < 0) /* completely used? */ + return (EADDRNOTAVAIL); + + *lastport = first + ((arc4random() % 65536) + + (arc4random() % V_ipport_randomalg_alg5_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 0d67f9c982f7 src/sys/netinet/in_pcb.h --- a/src/sys/netinet/in_pcb.h Mon Jan 31 11:35:24 2011 +0200 +++ b/src/sys/netinet/in_pcb.h Mon Jan 31 14:29:52 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_randomalg_ver); +VNET_DECLARE(u_int, ipport_randomalg_alg5_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_randomalg_ver VNET(ipport_randomalg_ver) +#define V_ipport_randomalg_alg5_tradeoff VNET(ipport_randomalg_alg5_tradeoff) extern struct callout ipport_tick_callout; diff -r 0d67f9c982f7 src/sys/netinet6/in6_src.c --- a/src/sys/netinet6/in6_src.c Mon Jan 31 11:35:24 2011 +0200 +++ b/src/sys/netinet6/in6_src.c Mon Jan 31 14:29:52 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 RFC 6056 there are 5 (five) possible algorithms + * for random port allocation. Usage of a particular algorithm + * is specified with the 'net.inet.ip.portrange.randomalg.version' + * sysctl variable. Default value is 1, which represents the + * legacy random port allocation algorithm in FreeBSD. + */ + if (dorandom) { + switch (V_ipport_randomalg_ver) { + 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_randomalg_alg5_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"