Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Feb 2021 18:15:20 GMT
From:      Neel Chauhan <nc@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 46539a7bf0ec - stable/13 - Allow setting alias port ranges in libalias and ipfw.
Message-ID:  <202102171815.11HIFKub071245@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by nc (ports committer):

URL: https://cgit.FreeBSD.org/src/commit/?id=46539a7bf0ecb2f624245da69334a66598da6664

commit 46539a7bf0ecb2f624245da69334a66598da6664
Author:     Neel Chauhan <nc@FreeBSD.org>
AuthorDate: 2021-02-02 21:24:17 +0000
Commit:     Neel Chauhan <nc@FreeBSD.org>
CommitDate: 2021-02-17 18:13:54 +0000

    Allow setting alias port ranges in libalias and ipfw.
    
    This will allow a system to be a true RFC 6598 NAT444 setup, where each
    network segment (e.g. user, subnet) can have their own dedicated port
    aliasing ranges.
    
    Reviewed by:            donner, kp
    Approved by:            0mp (mentor), donner, kp
    Differential Revision:  https://reviews.freebsd.org/D23450
    
    (cherry picked from commit a08cdb6cfb1c84b80b5337d46c574b55d0e15c63)
---
 sbin/ipfw/ipfw.8                   |  3 ++
 sbin/ipfw/ipfw2.h                  |  1 +
 sbin/ipfw/main.c                   |  3 +-
 sbin/ipfw/nat.c                    | 41 ++++++++++++++++
 sys/netinet/ip_fw.h                |  2 +
 sys/netinet/libalias/alias.h       |  1 +
 sys/netinet/libalias/alias_db.c    | 30 ++++++++++--
 sys/netinet/libalias/alias_local.h |  4 ++
 sys/netpfil/ipfw/ip_fw_nat.c       |  7 +++
 tests/sys/netpfil/common/nat.sh    | 99 +++++++++++++++++++++++++++++++++++++-
 10 files changed, 186 insertions(+), 5 deletions(-)

diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index e77930355094..d2c4885bc119 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -3271,6 +3271,9 @@ Reverse the way libalias handles aliasing.
 Obey transparent proxy rules only, packet aliasing is not performed.
 .It Cm skip_global
 Skip instance in case of global state lookup (see below).
+.It Cm port_range Ar lower-upper
+Set the aliasing ports between the ranges given. Upper port has to be greater
+than lower.
 .El
 .Pp
 Some specials value can be supplied instead of
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 764e5176e8ef..42e4f4777792 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -285,6 +285,7 @@ enum tokens {
 	TOK_STATES_CHUNKS,
 	TOK_JMAXLEN,
 	TOK_PORT_RANGE,
+	TOK_PORT_ALIAS,
 	TOK_HOST_DEL_AGE,
 	TOK_PG_DEL_AGE,
 	TOK_TCP_SYN_AGE,
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index dc55d0bfa416..89f21062811c 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -45,7 +45,8 @@ help(void)
 "[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_range 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"
diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c
index bbf5be666ea0..1076e1038b8a 100644
--- a/sbin/ipfw/nat.c
+++ b/sbin/ipfw/nat.c
@@ -65,6 +65,7 @@ static struct _s_x nat_params[] = {
  	{ "reset",		TOK_RESET_ADDR },
  	{ "reverse",		TOK_ALIAS_REV },
  	{ "proxy_only",		TOK_PROXY_ONLY },
+	{ "port_range",		TOK_PORT_ALIAS },
 	{ "redirect_addr",	TOK_REDIR_ADDR },
 	{ "redirect_port",	TOK_REDIR_PORT },
 	{ "redirect_proto",	TOK_REDIR_PROTO },
@@ -753,12 +754,35 @@ nat_show_cfg(struct nat44_cfg_nat *n, void *arg __unused)
 	printf("\n");
 }
 
+static int
+nat_port_alias_parse(char *str, u_short *lpout, u_short *hpout) {
+	long lp, hp;
+	char *ptr;
+	/* Lower port parsing */
+	lp = (long) strtol(str, &ptr, 10);
+	if (lp < 1024 || lp > 65535)
+		return 0;
+	if (!ptr || *ptr != '-')
+		return 0;
+	/* Upper port parsing */
+	hp = (long) strtol(ptr, &ptr, 10);
+	if (hp < 1024 || hp > 65535)
+		return 0;
+	if (ptr)
+		return 0;
+
+	*lpout = (u_short) lp;
+	*hpout = (u_short) hp;
+	return 1;
+}
+
 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;
 
@@ -786,6 +810,7 @@ ipfw_config_nat(int ac, char **av)
 		switch (tok) {
 		case TOK_IP:
 		case TOK_IF:
+		case TOK_PORT_ALIAS:
 			ac1--;
 			av1++;
 			break;
@@ -925,8 +950,24 @@ ipfw_config_nat(int ac, char **av)
 			n->redir_cnt++;
 			off += i;
 			break;
+		case TOK_PORT_ALIAS:
+			if (ac == 0)
+				errx(EX_DATAERR, "missing option");
+			if (!nat_port_alias_parse(av[0], &lp, &hp))
+				errx(EX_DATAERR,
+				    "You need a range of port(s) from 1024 <= x < 65536");
+			if (lp >= hp)
+				errx(EX_DATAERR,
+				    "Upper port has to be greater than lower port");
+			n->alias_port_lo = lp;
+			n->alias_port_hi = hp;
+			ac--;
+			av++;
+			break;
 		}
 	}
+	if (n->mode & PKT_ALIAS_SAME_PORTS && n->alias_port_lo)
+		errx(EX_DATAERR, "same_ports and port_range cannot both be selected");
 
 	i = do_set3(IP_FW_NAT44_XCONFIG, &oh->opheader, len);
 	if (i != 0)
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index f0531a67132d..4d3099a781a0 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -549,6 +549,8 @@ struct nat44_cfg_nat {
 	struct in_addr	ip;		/* nat IPv4 address */
 	uint32_t	mode;		/* aliasing mode */
 	uint32_t	redir_cnt;	/* number of entry in spool chain */
+	u_short		alias_port_lo;	/* low range for port aliasing */
+	u_short		alias_port_hi;	/* high range for port aliasing */
 };
 
 /* Nat command. */
diff --git a/sys/netinet/libalias/alias.h b/sys/netinet/libalias/alias.h
index 671241212799..91351a9eb8b9 100644
--- a/sys/netinet/libalias/alias.h
+++ b/sys/netinet/libalias/alias.h
@@ -86,6 +86,7 @@ struct alias_link;
 /* 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
diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c
index 8da9a7fe683a..1f85a606b2d5 100644
--- a/sys/netinet/libalias/alias_db.c
+++ b/sys/netinet/libalias/alias_db.c
@@ -595,6 +595,11 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
 			 */
 			port_net = lnk->src_port;
 			port_sys = ntohs(port_net);
+		} else if (la->aliasPortLower) {
+			/* First trial is a random port in the aliasing range. */
+			port_sys = la->aliasPortLower +
+			    (arc4random() % la->aliasPortLength);
+			port_net = htons(port_sys);
 		} else {
 			/* First trial and all subsequent are random. */
 			port_sys = arc4random() & ALIAS_PORT_MASK;
@@ -647,9 +652,15 @@ GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
 			}
 #endif
 		}
-		port_sys = arc4random() & ALIAS_PORT_MASK;
-		port_sys += ALIAS_PORT_BASE;
-		port_net = htons(port_sys);
+		if (la->aliasPortLower) {
+			port_sys = la->aliasPortLower +
+			    (arc4random() % la->aliasPortLength);
+			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
@@ -2381,6 +2392,19 @@ LibAliasSetAddress(struct libalias *la, struct in_addr addr)
 	LIBALIAS_UNLOCK(la);
 }
 
+
+void
+LibAliasSetAliasPortRange(struct libalias *la, u_short port_low,
+    u_short port_high)
+{
+
+	LIBALIAS_LOCK(la);
+	la->aliasPortLower = port_low;
+	/* Add 1 to the aliasPortLength as modulo has range of 1 to n-1 */
+	la->aliasPortLength = port_high - port_low + 1;
+	LIBALIAS_UNLOCK(la);
+}
+
 void
 LibAliasSetTarget(struct libalias *la, struct in_addr target_addr)
 {
diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h
index 5919851a4019..ba128638c1fe 100644
--- a/sys/netinet/libalias/alias_local.h
+++ b/sys/netinet/libalias/alias_local.h
@@ -163,6 +163,10 @@ struct libalias {
 	struct in_addr	true_addr;	/* in network byte order. */
 	u_short		true_port;	/* in host byte order. */
 
+	/* Port ranges for aliasing. */
+	u_short		aliasPortLower;
+	u_short		aliasPortLength;
+
 	/*
 	 * sctp code support
 	 */
diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c
index 81229b2707e9..bcda3cff011c 100644
--- a/sys/netpfil/ipfw/ip_fw_nat.c
+++ b/sys/netpfil/ipfw/ip_fw_nat.c
@@ -93,6 +93,8 @@ struct cfg_nat {
 	/* chain of redir instances */
 	LIST_HEAD(redir_chain, cfg_redir) redir_chain;  
 	char			if_name[IF_NAMESIZE];	/* interface name */
+	u_short			alias_port_lo;	/* low range for port aliasing */
+	u_short			alias_port_hi;	/* high range for port aliasing */
 };
 
 static eventhandler_tag ifaddr_event_tag;
@@ -528,9 +530,12 @@ nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
 	ptr->ip = ucfg->ip;
 	ptr->redir_cnt = ucfg->redir_cnt;
 	ptr->mode = ucfg->mode;
+	ptr->alias_port_lo = ucfg->alias_port_lo;
+	ptr->alias_port_hi = ucfg->alias_port_hi;
 	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->alias_port_lo, ptr->alias_port_hi);
 
 	/*
 	 * Redir and LSNAT configuration.
@@ -658,6 +663,8 @@ export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
 	ucfg->ip = ptr->ip;
 	ucfg->redir_cnt = ptr->redir_cnt;
 	ucfg->mode = ptr->mode;
+	ucfg->alias_port_lo = ptr->alias_port_lo;
+	ucfg->alias_port_hi = ptr->alias_port_hi;
 	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
 }
 
diff --git a/tests/sys/netpfil/common/nat.sh b/tests/sys/netpfil/common/nat.sh
index f74467dce062..9aa06de51337 100644
--- a/tests/sys/netpfil/common/nat.sh
+++ b/tests/sys/netpfil/common/nat.sh
@@ -147,10 +147,107 @@ userspace_nat_cleanup()
 	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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102171815.11HIFKub071245>