Index: sbin/ipfw/ipfw.8 =================================================================== --- sbin/ipfw/ipfw.8 +++ sbin/ipfw/ipfw.8 @@ -3257,6 +3257,9 @@ If no entry was found in any of the instances, packet is passed unchanged, and no new entry will be created. See section +.It Cm port_alias lower upper +Set the aliasing ports between the ranges given. Upper port has to be greater +than lower. The interval is half-open, as the upper number is never used. .Sx MULTIPLE INSTANCES in .Xr natd 8 Index: sbin/ipfw/ipfw2.h =================================================================== --- sbin/ipfw/ipfw2.h +++ sbin/ipfw/ipfw2.h @@ -285,6 +285,7 @@ TOK_STATES_CHUNKS, TOK_JMAXLEN, TOK_PORT_RANGE, + TOK_PORT_ALIAS, TOK_HOST_DEL_AGE, TOK_PG_DEL_AGE, TOK_TCP_SYN_AGE, Index: sbin/ipfw/main.c =================================================================== --- sbin/ipfw/main.c +++ sbin/ipfw/main.c @@ -45,7 +45,8 @@ "[pipe|queue] {zero|delete|show} [N{,N}]\n" "nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" " reset|reverse|proxy_only|redirect_addr linkspec|\n" -" redirect_port linkspec|redirect_proto linkspec}\n" +" redirect_port linkspec|redirect_proto linkspec|\n" +" port_alias lower upper}\n" "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" "set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" Index: sbin/ipfw/nat.c =================================================================== --- sbin/ipfw/nat.c +++ sbin/ipfw/nat.c @@ -65,6 +65,7 @@ { "reset", TOK_RESET_ADDR }, { "reverse", TOK_ALIAS_REV }, { "proxy_only", TOK_PROXY_ONLY }, + { "port_alias", TOK_PORT_ALIAS }, { "redirect_addr", TOK_REDIR_ADDR }, { "redirect_port", TOK_REDIR_PORT }, { "redirect_proto", TOK_REDIR_PROTO }, @@ -752,12 +753,25 @@ printf("\n"); } +static u_short +nat_port_alias_parse(char *ptr) +{ + u_short port; + for (char *ptr2 = ptr; *ptr2 != '\0'; ptr2++) + if (!isdigit(*ptr2)) return 0; + port = (u_short) strtol(ptr, NULL, 10); + if (port < 1024 || port > 65535) + return 0; + return port; +} + void ipfw_config_nat(int ac, char **av) { ipfw_obj_header *oh; struct nat44_cfg_nat *n; /* Nat instance configuration. */ int i, off, tok, ac1; + u_short lp, hp; char *id, *buf, **av1, *end; size_t len; @@ -837,6 +851,13 @@ ac1--; } break; + case TOK_PORT_ALIAS: + if (ac1 < 2) + errx(EX_DATAERR, "port_alias: " + "not enough arguments"); + av1 += 2; + ac1 -= 2; + break; default: errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]); } @@ -919,6 +940,19 @@ } n->redir_cnt++; off += i; + break; + case TOK_PORT_ALIAS: + if (ac == 0) + errx(EX_DATAERR, "missing option"); + if (!(lp = nat_port_alias_parse(av[0])) || + !(hp = nat_port_alias_parse(av[1]))) + errx(EX_DATAERR, "port has to be an integer from 1024 <= x < 65536"); + if (lp >= hp) + errx(EX_DATAERR, "upper port has to be greater than lower port"); + n->alias_port_range.lower = lp; + n->alias_port_range.upper = hp; + ac -= 2; + av += 2; break; } } Index: sys/netinet/ip_fw.h =================================================================== --- sys/netinet/ip_fw.h +++ sys/netinet/ip_fw.h @@ -526,6 +526,12 @@ #define NAT44_REDIR_PORT 0x02 #define NAT44_REDIR_PROTO 0x04 +/* Port ranges for port aliasing. */ +struct nat_port_range { + u_short lower; + u_short upper; +}; + /* Nat redirect configuration. */ struct nat44_cfg_redir { struct in_addr laddr; /* local ip address */ @@ -550,6 +556,8 @@ struct in_addr ip; /* nat IPv4 address */ uint32_t mode; /* aliasing mode */ uint32_t redir_cnt; /* number of entry in spool chain */ + /* port ranges for port aliasing */ + struct nat_port_range alias_port_range; }; /* Nat command. */ Index: sys/netinet/libalias/alias.h =================================================================== --- sys/netinet/libalias/alias.h +++ sys/netinet/libalias/alias.h @@ -86,6 +86,7 @@ /* Initialization and control functions. */ struct libalias *LibAliasInit(struct libalias *); void LibAliasSetAddress(struct libalias *, struct in_addr _addr); +void LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, u_short port_hi); void LibAliasSetFWBase(struct libalias *, unsigned int _base, unsigned int _num); void LibAliasSetSkinnyPort(struct libalias *, unsigned int _port); unsigned int Index: sys/netinet/libalias/alias_db.c =================================================================== --- sys/netinet/libalias/alias_db.c +++ sys/netinet/libalias/alias_db.c @@ -605,6 +605,11 @@ */ port_net = lnk->src_port; port_sys = ntohs(port_net); + } else if (la->portRange.lower && la->portRange.upper) { + /* First trial is a random port in the aliasing range. */ + port_sys = la->portRange.lower + (arc4random() % + (la->portRange.upper - la->portRange.lower)); + port_net = htons(port_sys); } else { /* First trial and all subsequent are random. */ port_sys = arc4random() & ALIAS_PORT_MASK; @@ -658,9 +663,15 @@ } #endif } - port_sys = arc4random() & ALIAS_PORT_MASK; - port_sys += ALIAS_PORT_BASE; - port_net = htons(port_sys); + if (la->portRange.lower && la->portRange.upper) { + port_sys = la->portRange.lower + (arc4random() % + (la->portRange.upper - la->portRange.lower)); + port_net = htons(port_sys); + } else { + port_sys = arc4random() & ALIAS_PORT_MASK; + port_sys += ALIAS_PORT_BASE; + port_net = htons(port_sys); + } } #ifdef LIBALIAS_DEBUG @@ -2443,6 +2454,18 @@ CleanupAliasData(la); la->aliasAddress = addr; + LIBALIAS_UNLOCK(la); +} + + +void +LibAliasSetAliasPortRange(struct libalias *la, u_short port_low, + u_short port_high) +{ + + LIBALIAS_LOCK(la); + la->portRange.lower = port_low; + la->portRange.upper = port_high; LIBALIAS_UNLOCK(la); } Index: sys/netinet/libalias/alias_local.h =================================================================== --- sys/netinet/libalias/alias_local.h +++ sys/netinet/libalias/alias_local.h @@ -65,6 +65,9 @@ #include "alias_sctp.h" #endif +#include +#include + /* Sizes of input and output link tables */ #define LINK_TABLE_OUT_SIZE 4001 #define LINK_TABLE_IN_SIZE 4001 @@ -162,6 +165,9 @@ struct in_addr true_addr; /* in network byte order. */ u_short true_port; /* in host byte order. */ + + /* Port ranges for aliasing. */ + struct nat_port_range portRange; /* * sctp code support Index: sys/netpfil/ipfw/ip_fw_nat.c =================================================================== --- sys/netpfil/ipfw/ip_fw_nat.c +++ sys/netpfil/ipfw/ip_fw_nat.c @@ -93,6 +93,7 @@ /* chain of redir instances */ LIST_HEAD(redir_chain, cfg_redir) redir_chain; char if_name[IF_NAMESIZE]; /* interface name */ + struct nat_port_range port_range; /* port ranges for aliasing */ }; static eventhandler_tag ifaddr_event_tag; @@ -529,9 +530,11 @@ ptr->ip = ucfg->ip; ptr->redir_cnt = ucfg->redir_cnt; ptr->mode = ucfg->mode; + ptr->port_range = ucfg->alias_port_range; strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); LibAliasSetMode(ptr->lib, ptr->mode, ~0); LibAliasSetAddress(ptr->lib, ptr->ip); + LibAliasSetAliasPortRange(ptr->lib, ptr->port_range.lower, ptr->port_range.upper); /* * Redir and LSNAT configuration. @@ -659,6 +662,7 @@ ucfg->ip = ptr->ip; ucfg->redir_cnt = ptr->redir_cnt; ucfg->mode = ptr->mode; + ucfg->alias_port_range = ptr->port_range; strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); } Index: tests/sys/netpfil/common/nat.sh =================================================================== --- tests/sys/netpfil/common/nat.sh +++ tests/sys/netpfil/common/nat.sh @@ -147,10 +147,107 @@ firewall_cleanup $firewall } +common_cgn() { + firewall=$1 + portalias=$2 + firewall_init $firewall + nat_init $firewall + + epair_host_nat=$(vnet_mkepair) + epair_client1_nat=$(vnet_mkepair) + epair_client2_nat=$(vnet_mkepair) + + vnet_mkjail nat ${epair_host_nat}b ${epair_client1_nat}a ${epair_client2_nat}a + vnet_mkjail client1 ${epair_client1_nat}b + vnet_mkjail client2 ${epair_client2_nat}b + + ifconfig ${epair_host_nat}a 198.51.100.2/24 up + jexec nat ifconfig ${epair_host_nat}b 198.51.100.1/24 up + + jexec nat ifconfig ${epair_client1_nat}a 100.64.0.1/24 up + jexec client1 ifconfig ${epair_client1_nat}b 100.64.0.2/24 up + + jexec nat ifconfig ${epair_client2_nat}a 100.64.1.1/24 up + jexec client2 ifconfig ${epair_client2_nat}b 100.64.1.2/24 up + + jexec nat sysctl net.inet.ip.forwarding=1 + + jexec client1 route add -net 198.51.100.0/24 100.64.0.1 + jexec client2 route add -net 198.51.100.0/24 100.64.1.1 + + # ping fails without NAT configuration + atf_check -s exit:2 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2 + atf_check -s exit:2 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2 + + if [[ $portalias ]]; then + firewall_config nat $firewall \ + "ipfw" \ + "ipfw -q nat 123 config if ${epair_host_nat}b unreg_cgn port_alias 2000 2999" \ + "ipfw -q nat 456 config if ${epair_host_nat}b unreg_cgn port_alias 3000 3999" \ + "ipfw -q add 1000 nat 123 all from any to 198.51.100.2 2000-2999 in via ${epair_host_nat}b" \ + "ipfw -q add 2000 nat 456 all from any to 198.51.100.2 3000-3999 in via ${epair_host_nat}b" \ + "ipfw -q add 3000 nat 123 all from 100.64.0.2 to any out via ${epair_host_nat}b" \ + "ipfw -q add 4000 nat 456 all from 100.64.1.2 to any out via ${epair_host_nat}b" + else + firewall_config nat $firewall \ + "ipfw" \ + "ipfw -q nat 123 config if ${epair_host_nat}b unreg_cgn" \ + "ipfw -q add 1000 nat 123 all from any to any" + fi + + # ping is successful now + atf_check -s exit:0 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2 + + # if portalias, test a tcp server/client with nc + if [[ $portalias ]]; then + for inst in 1 2; do + daemon nc -p 198.51.100.2 7 + atf_check -s exit:0 -o ignore jexec client$inst sh -c "echo | nc -N 198.51.100.2 7" + done + fi +} + +cgn_head() +{ + atf_set descr 'IPv4 CGN (RFC 6598) test' + atf_set require.user root +} + +cgn_body() +{ + common_cgn $1 false +} + +cgn_cleanup() +{ + firewall_cleanup ipfw +} + +portalias_head() +{ + atf_set descr 'IPv4 CGN (RFC 6598) port aliasing test' + atf_set require.user root +} + +portalias_body() +{ + common_cgn $1 true +} + +portalias_cleanup() +{ + firewall_cleanup ipfw +} + setup_tests \ basic \ pf \ ipfw \ ipfnat \ userspace_nat \ - ipfw \ No newline at end of file + ipfw \ + cgn \ + ipfw \ + portalias \ + ipfw