Blame SOURCES/0001-conntrack-Support-IPv6-NAT.patch

0c8692
From c1874130f845e526de76d116639c044cb30fcc9a Mon Sep 17 00:00:00 2001
0c8692
From: Neil Wilson <neil@aldur.co.uk>
0c8692
Date: Thu, 16 Mar 2017 11:49:03 +0000
0c8692
Subject: [PATCH] conntrack: Support IPv6 NAT
0c8692
0c8692
Refactor and improve nat support to allow conntrack to manage IPv6
0c8692
NAT entries.
0c8692
0c8692
Refactor and improve conntrack nat tests to include IPv6 NAT.
0c8692
0c8692
Signed-off-by: Neil Wilson <neil@aldur.co.uk>
0c8692
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
0c8692
(cherry picked from commit 29b390a2122143997a651e6b25d7496e62ead2a1)
0c8692
---
0c8692
 src/conntrack.c                    | 213 ++++++++++++++++++++---------
0c8692
 tests/conntrack/testsuite/00create |   6 +
0c8692
 tests/conntrack/testsuite/03nat    |   8 ++
0c8692
 tests/conntrack/testsuite/07nat6   |  56 ++++++++
0c8692
 4 files changed, 216 insertions(+), 67 deletions(-)
0c8692
 create mode 100644 tests/conntrack/testsuite/07nat6
0c8692
12f005
diff --git a/src/conntrack.c b/src/conntrack.c
0c8692
index ff030fe54e103..cbf03c7be8834 100644
12f005
--- a/src/conntrack.c
12f005
+++ b/src/conntrack.c
12f005
@@ -43,6 +43,8 @@
12f005
 #include <stdio.h>
12f005
 #include <getopt.h>
12f005
 #include <stdlib.h>
12f005
+#include <ctype.h>
12f005
+#include <limits.h>
12f005
 #include <stdarg.h>
12f005
 #include <errno.h>
12f005
 #include <unistd.h>
12f005
@@ -437,6 +439,9 @@ static const int opt2type[] = {
12f005
 static const int opt2maskopt[] = {
12f005
 	['s']	= '{',
12f005
 	['d']	= '}',
12f005
+	['g']   = 0,
12f005
+	['j']   = 0,
12f005
+	['n']   = 0,
12f005
 	['r']	= 0, /* no netmask */
12f005
 	['q']	= 0, /* support yet */
12f005
 	['{']	= 0,
12f005
@@ -448,6 +453,8 @@ static const int opt2maskopt[] = {
12f005
 static const int opt2family_attr[][2] = {
12f005
 	['s']	= { ATTR_ORIG_IPV4_SRC,	ATTR_ORIG_IPV6_SRC },
12f005
 	['d']	= { ATTR_ORIG_IPV4_DST,	ATTR_ORIG_IPV6_DST },
12f005
+	['g']   = { ATTR_DNAT_IPV4, ATTR_DNAT_IPV6 },
12f005
+	['n']   = { ATTR_SNAT_IPV4, ATTR_SNAT_IPV6 },
12f005
 	['r']	= { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV6_SRC },
12f005
 	['q']	= { ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_DST },
12f005
 	['{']	= { ATTR_ORIG_IPV4_SRC,	ATTR_ORIG_IPV6_SRC },
12f005
@@ -459,6 +466,8 @@ static const int opt2family_attr[][2] = {
12f005
 static const int opt2attr[] = {
12f005
 	['s']	= ATTR_ORIG_L3PROTO,
12f005
 	['d']	= ATTR_ORIG_L3PROTO,
12f005
+	['g']	= ATTR_ORIG_L3PROTO,
12f005
+	['n']	= ATTR_ORIG_L3PROTO,
12f005
 	['r']	= ATTR_REPL_L3PROTO,
12f005
 	['q']	= ATTR_REPL_L3PROTO,
12f005
 	['{']	= ATTR_ORIG_L3PROTO,
12f005
@@ -1094,58 +1103,85 @@ parse_addr(const char *cp, union ct_address *address, int *mask)
12f005
 	return family;
12f005
 }
12f005
 
12f005
-static void
12f005
-nat_parse(char *arg, struct nf_conntrack *obj, int type)
12f005
+static bool
12f005
+valid_port(char *cursor)
12f005
 {
12f005
-	char *colon, *error;
12f005
-	union ct_address parse;
12f005
+	const char *str = cursor;
12f005
+	/* Missing port number */
12f005
+	if (!*str)
12f005
+		return false;
12f005
 
12f005
-	colon = strchr(arg, ':');
12f005
+	/* Must be entirely digits - no spaces or +/- */
12f005
+	while (*cursor) {
12f005
+		if (!isdigit(*cursor))
12f005
+			return false;
12f005
+		else
12f005
+			++cursor;
12f005
+	}
12f005
 
12f005
-	if (colon) {
12f005
-		uint16_t port;
12f005
+	/* Must be in range */
12f005
+	errno = 0;
12f005
+	long port = strtol(str, NULL, 10);
12f005
 
12f005
-		*colon = '\0';
12f005
+	if ((errno == ERANGE && (port == LONG_MAX || port == LONG_MIN))
12f005
+		|| (errno != 0 && port == 0) || (port > USHRT_MAX))
12f005
+		return false;
12f005
 
12f005
-		port = (uint16_t)atoi(colon+1);
12f005
-		if (port == 0) {
12f005
-			if (strlen(colon+1) == 0) {
12f005
-				exit_error(PARAMETER_PROBLEM,
12f005
-					   "No port specified after `:'");
12f005
-			} else {
12f005
-				exit_error(PARAMETER_PROBLEM,
12f005
-					   "Port `%s' not valid", colon+1);
12f005
-			}
12f005
-		}
12f005
+	return true;
12f005
+}
12f005
+
12f005
+static void
12f005
+split_address_and_port(const char *arg, char **address, char **port_str)
12f005
+{
12f005
+	char *cursor = strchr(arg, '[');
12f005
+
12f005
+	if (cursor) {
12f005
+		/* IPv6 address with port*/
12f005
+		char *start = cursor + 1;
12f005
 
12f005
-		error = strchr(colon+1, ':');
12f005
-		if (error)
12f005
+		cursor = strchr(start, ']');
12f005
+		if (start == cursor) {
12f005
+			exit_error(PARAMETER_PROBLEM,
12f005
+				   "No IPv6 address specified");
12f005
+		} else if (!cursor) {
12f005
 			exit_error(PARAMETER_PROBLEM,
12f005
-				   "Invalid port:port syntax");
12f005
-
12f005
-		if (type == CT_OPT_SRC_NAT)
12f005
-			nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
12f005
-		else if (type == CT_OPT_DST_NAT)
12f005
-			nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
12f005
-		else if (type == CT_OPT_ANY_NAT) {
12f005
-			nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
12f005
-			nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
12f005
+				   "No closing ']' around IPv6 address");
12f005
 		}
12f005
-	}
12f005
+		size_t len = cursor - start;
12f005
 
12f005
-	if (parse_addr(arg, &parse, NULL) == AF_UNSPEC) {
12f005
-		if (strlen(arg) == 0) {
12f005
-			exit_error(PARAMETER_PROBLEM, "No IP specified");
12f005
+		cursor = strchr(cursor, ':');
12f005
+		if (cursor) {
12f005
+			/* Copy address only if there is a port */
12f005
+			*address = strndup(start, len);
12f005
+		}
12f005
+	} else {
12f005
+		cursor = strchr(arg, ':');
12f005
+		if (cursor && !strchr(cursor + 1, ':')) {
12f005
+			/* IPv4 address with port */
12f005
+			*address = strndup(arg, cursor - arg);
12f005
 		} else {
12f005
+			/* v6 address */
12f005
+			cursor = NULL;
12f005
+		}
12f005
+	}
12f005
+	if (cursor) {
12f005
+		/* Parse port entry */
12f005
+		cursor++;
12f005
+		if (strlen(cursor) == 0) {
12f005
 			exit_error(PARAMETER_PROBLEM,
12f005
-					"Invalid IP address `%s'", arg);
12f005
+				   "No port specified after `:'");
12f005
 		}
12f005
+		if (!valid_port(cursor)) {
12f005
+			exit_error(PARAMETER_PROBLEM,
12f005
+				   "Invalid port `%s'", cursor);
12f005
+		}
12f005
+		*port_str = strdup(cursor);
12f005
+	} else {
12f005
+		/* No port colon or more than one colon (ipv6)
12f005
+		 * assume arg is straight IP address and no port
12f005
+		 */
12f005
+		*address = strdup(arg);
12f005
 	}
12f005
-
12f005
-	if (type == CT_OPT_SRC_NAT || type == CT_OPT_ANY_NAT)
12f005
-		nfct_set_attr_u32(tmpl.ct, ATTR_SNAT_IPV4, parse.v4);
12f005
-	else if (type == CT_OPT_DST_NAT || type == CT_OPT_ANY_NAT)
12f005
-		nfct_set_attr_u32(tmpl.ct, ATTR_DNAT_IPV4, parse.v4);
12f005
 }
12f005
 
12f005
 static void
12f005
@@ -1289,7 +1325,7 @@ nfct_ip6_net_cmp(const union ct_address *addr, const struct ct_network *net)
12f005
 
12f005
 static int
12f005
 nfct_ip_net_cmp(int family, const union ct_address *addr,
12f005
-                const struct ct_network *net)
12f005
+		const struct ct_network *net)
12f005
 {
12f005
 	switch(family) {
12f005
 	case AF_INET:
12f005
@@ -2128,6 +2164,7 @@ static void merge_bitmasks(struct nfct_bitmask **current,
12f005
 	nfct_bitmask_destroy(src);
12f005
 }
12f005
 
12f005
+
12f005
 static void
12f005
 nfct_build_netmask(uint32_t *dst, int b, int n)
12f005
 {
12f005
@@ -2147,10 +2184,9 @@ nfct_build_netmask(uint32_t *dst, int b, int n)
12f005
 }
12f005
 
12f005
 static void
12f005
-nfct_set_addr_opt(int opt, struct nf_conntrack *ct, union ct_address *ad,
12f005
-		  int l3protonum)
12f005
+nfct_set_addr_only(const int opt, struct nf_conntrack *ct, union ct_address *ad,
12f005
+		   const int l3protonum)
12f005
 {
12f005
-	options |= opt2type[opt];
12f005
 	switch (l3protonum) {
12f005
 	case AF_INET:
12f005
 		nfct_set_attr_u32(ct,
12f005
@@ -2163,24 +2199,33 @@ nfct_set_addr_opt(int opt, struct nf_conntrack *ct, union ct_address *ad,
12f005
 		              &ad->v6);
12f005
 		break;
12f005
 	}
12f005
+}
12f005
+
12f005
+static void
12f005
+nfct_set_addr_opt(const int opt, struct nf_conntrack *ct, union ct_address *ad,
12f005
+		  const int l3protonum)
12f005
+{
12f005
+	options |= opt2type[opt];
12f005
+	nfct_set_addr_only(opt, ct, ad, l3protonum);
12f005
 	nfct_set_attr_u8(ct, opt2attr[opt], l3protonum);
12f005
 }
12f005
 
12f005
 static void
12f005
-nfct_parse_addr_from_opt(int opt, struct nf_conntrack *ct,
12f005
-                         struct nf_conntrack *ctmask,
12f005
-                         union ct_address *ad, int *family)
12f005
+nfct_parse_addr_from_opt(const int opt, const char *arg,
12f005
+			 struct nf_conntrack *ct,
12f005
+			 struct nf_conntrack *ctmask,
12f005
+			 union ct_address *ad, int *family)
12f005
 {
12f005
-	int l3protonum, mask, maskopt;
12f005
+	int mask, maskopt;
12f005
 
12f005
-	l3protonum = parse_addr(optarg, ad, &mask);
12f005
+	const int l3protonum = parse_addr(arg, ad, &mask);
12f005
 	if (l3protonum == AF_UNSPEC) {
12f005
 		exit_error(PARAMETER_PROBLEM,
12f005
-			   "Invalid IP address `%s'", optarg);
12f005
+			   "Invalid IP address `%s'", arg);
12f005
 	}
12f005
 	set_family(family, l3protonum);
12f005
 	maskopt = opt2maskopt[opt];
12f005
-	if (!maskopt && mask != -1) {
12f005
+	if (mask != -1 && !maskopt) {
12f005
 		exit_error(PARAMETER_PROBLEM,
12f005
 		           "CIDR notation unavailable"
12f005
 		           " for `--%s'", get_long_opt(opt));
12f005
@@ -2192,7 +2237,7 @@ nfct_parse_addr_from_opt(int opt, struct nf_conntrack *ct,
12f005
 	nfct_set_addr_opt(opt, ct, ad, l3protonum);
12f005
 
12f005
 	/* bail if we don't have a netmask to set*/
12f005
-	if (!maskopt || mask == -1 || ctmask == NULL)
12f005
+	if (mask == -1 || !maskopt || ctmask == NULL)
12f005
 		return;
12f005
 
12f005
 	switch(l3protonum) {
12f005
@@ -2211,6 +2256,24 @@ nfct_parse_addr_from_opt(int opt, struct nf_conntrack *ct,
12f005
 	nfct_set_addr_opt(maskopt, ctmask, ad, l3protonum);
12f005
 }
12f005
 
12f005
+static void
12f005
+nfct_set_nat_details(const int opt, struct nf_conntrack *ct,
12f005
+		     union ct_address *ad, const char *port_str,
12f005
+		     const int family)
12f005
+{
12f005
+	const int type = opt2type[opt];
12f005
+
12f005
+	nfct_set_addr_only(opt, ct, ad, family);
12f005
+	if (port_str && type == CT_OPT_SRC_NAT) {
12f005
+		nfct_set_attr_u16(ct, ATTR_SNAT_PORT,
12f005
+				  ntohs((uint16_t)atoi(port_str)));
12f005
+	} else if (port_str && type == CT_OPT_DST_NAT) {
12f005
+		nfct_set_attr_u16(ct, ATTR_DNAT_PORT,
12f005
+				  ntohs((uint16_t)atoi(port_str)));
12f005
+	}
12f005
+
12f005
+}
12f005
+
12f005
 int main(int argc, char *argv[])
12f005
 {
12f005
 	int c, cmd;
12f005
@@ -2289,17 +2352,18 @@ int main(int argc, char *argv[])
12f005
 		case 'd':
12f005
 		case 'r':
12f005
 		case 'q':
12f005
-			nfct_parse_addr_from_opt(c, tmpl.ct, tmpl.mask,
12f005
-			                         &ad, &family);
12f005
+			nfct_parse_addr_from_opt(c, optarg, tmpl.ct,
12f005
+						 tmpl.mask, &ad, &family);
12f005
 			break;
12f005
 		case '[':
12f005
 		case ']':
12f005
-			nfct_parse_addr_from_opt(c, tmpl.exptuple, tmpl.mask,
12f005
-			                         &ad, &family);
12f005
+			nfct_parse_addr_from_opt(c, optarg, tmpl.exptuple,
12f005
+						 tmpl.mask, &ad, &family);
12f005
 			break;
12f005
 		case '{':
12f005
 		case '}':
12f005
-			nfct_parse_addr_from_opt(c, tmpl.mask, NULL, &ad, &family);
12f005
+			nfct_parse_addr_from_opt(c, optarg, tmpl.mask,
12f005
+						 NULL, &ad, &family);
12f005
 			break;
12f005
 		case 'p':
12f005
 			options |= CT_OPT_PROTO;
12f005
@@ -2341,19 +2405,34 @@ int main(int argc, char *argv[])
12f005
 			break;
12f005
 		case 'n':
12f005
 		case 'g':
12f005
-		case 'j': {
12f005
-			char *tmp = NULL;
12f005
-
12f005
+		case 'j':
12f005
 			options |= opt2type[c];
12f005
-
12f005
-			tmp = get_optional_arg(argc, argv);
12f005
-			if (tmp == NULL)
12f005
-				continue;
12f005
-
12f005
-			set_family(&family, AF_INET);
12f005
-			nat_parse(tmp, tmpl.ct, opt2type[c]);
12f005
+			char *optional_arg = get_optional_arg(argc, argv);
12f005
+
12f005
+			if (optional_arg) {
12f005
+				char *port_str = NULL;
12f005
+				char *nat_address = NULL;
12f005
+
12f005
+				split_address_and_port(optional_arg,
12f005
+						       &nat_address,
12f005
+						       &port_str);
12f005
+				nfct_parse_addr_from_opt(c, nat_address,
12f005
+							 tmpl.ct, NULL,
12f005
+							 &ad, &family);
12f005
+				if (c == 'j') {
12f005
+					/* Set details on both src and dst
12f005
+					 * with any-nat
12f005
+					 */
12f005
+					nfct_set_nat_details('g', tmpl.ct, &ad,
12f005
+							     port_str, family);
12f005
+					nfct_set_nat_details('n', tmpl.ct, &ad,
12f005
+							     port_str, family);
12f005
+				} else {
12f005
+					nfct_set_nat_details(c, tmpl.ct, &ad,
12f005
+							     port_str, family);
12f005
+				}
12f005
+			}
12f005
 			break;
12f005
-		}
12f005
 		case 'w':
12f005
 		case '(':
12f005
 		case ')':
12f005
diff --git a/tests/conntrack/testsuite/00create b/tests/conntrack/testsuite/00create
0c8692
index 40e2c1952940c..afe4342e9b00d 100644
12f005
--- a/tests/conntrack/testsuite/00create
12f005
+++ b/tests/conntrack/testsuite/00create
12f005
@@ -18,3 +18,9 @@
12f005
 -I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
12f005
 # delete reverse
12f005
 -D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
12f005
+# create a v6 conntrack
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
12f005
+# delete v6 conntrack
12f005
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
12f005
+# mismatched address family
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
12f005
diff --git a/tests/conntrack/testsuite/03nat b/tests/conntrack/testsuite/03nat
0c8692
index f94e8ffeb2f9f..014feb8e6e3ab 100644
12f005
--- a/tests/conntrack/testsuite/03nat
12f005
+++ b/tests/conntrack/testsuite/03nat
12f005
@@ -36,5 +36,13 @@
12f005
 -L --dst-nat 3.3.3.3:81 ; OK
12f005
 # show
12f005
 -L --dst-nat 1.1.1.1:80 ; OK
12f005
+# badport
12f005
+-L --dst-nat 1.1.1.1: ; BAD
12f005
+# badport
12f005
+-L --dst-nat 1.1.1.1::; BAD
12f005
+# badport
12f005
+-L --dst-nat 1.1.1.1:80:80; BAD
12f005
+# badport
12f005
+-L --dst-nat 1.1.1.1:65536; BAD
12f005
 # delete
12f005
 -D -s 1.1.1.1 ; OK
12f005
diff --git a/tests/conntrack/testsuite/07nat6 b/tests/conntrack/testsuite/07nat6
12f005
new file mode 100644
0c8692
index 0000000000000..8cecd8e9bdd88
12f005
--- /dev/null
12f005
+++ b/tests/conntrack/testsuite/07nat6
12f005
@@ -0,0 +1,56 @@
12f005
+# create dummy
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 --dst-nat 2001:DB8::3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
12f005
+# show
12f005
+-L --dst-nat ; OK
12f005
+# show
12f005
+-L --dst-nat 2001:DB8::3.3.3.3 ; OK
12f005
+# show
12f005
+-L --src-nat ; OK
12f005
+# delete
12f005
+-D -s 2001:DB8::1.1.1.1 ; OK
12f005
+# create dummy again
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 --src-nat 2001:DB8::3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
12f005
+# show
12f005
+-L --src-nat ; OK
12f005
+# show
12f005
+-L --src-nat 2001:DB8::3.3.3.3 ; OK
12f005
+# show
12f005
+-L --dst-nat ; OK
12f005
+# show any-nat
12f005
+-L --any-nat ; OK
12f005
+# delete
12f005
+-D -s 2001:DB8::1.1.1.1 ; OK
12f005
+# bad combination
12f005
+-L --dst-nat --any-nat ; BAD
12f005
+# bad combination
12f005
+-L --src-nat --any-nat ; BAD
12f005
+# bad combination
12f005
+-L --src-nat --dst-nat --any-nat ; BAD
12f005
+# create
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 --dst-nat [2001:DB8::3.3.3.3]:80 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
12f005
+# show
12f005
+-L --dst-nat [2001:DB8::3.3.3.3]:80 ; OK
12f005
+# show
12f005
+-L --any-nat [2001:DB8::3.3.3.3]:80 ; OK
12f005
+# show
12f005
+-L --dst-nat [2001:DB8::3.3.3.3]:81 ; OK
12f005
+# show
12f005
+-L --dst-nat [2001:DB8::1.1.1.1]:80 ; OK
12f005
+# noport
12f005
+-L --dst-nat [2001:DB8::1.1.1.1]: ; BAD
12f005
+# badport
12f005
+-L --dst-nat [2001:DB8::1.1.1.1]:: ; BAD
12f005
+# badport
12f005
+-L --dst-nat [2001:DB8::1.1.1.1]:80:80 ; BAD
12f005
+# badport
12f005
+-L --dst-nat [2001:DB8::1.1.1.1]:65536 ; BAD
12f005
+# delete
12f005
+-D -s 2001:DB8::1.1.1.1 ; OK
12f005
+# mismatched address family
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 --dst-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
12f005
+# mismatched address family
12f005
+-I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 2001:DB8::3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
12f005
+# create - brackets only for ports in nat
12f005
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 --dst-nat [2001:DB8::3.3.3.3] -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
12f005
+# create - brackets rejected elsewhere
12f005
+-I -s [2001:DB8::1.1.1.1] -d 2001:DB8::2.2.2.2 --dst-nat 2001:DB8::3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
12f005
-- 
0c8692
2.21.0
12f005