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"