Blame SOURCES/conntrack-tools-1.4.4-conntrack.patch

12f005
diff --git a/src/conntrack.c b/src/conntrack.c
12f005
index bd337f4..6e96b58 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
12f005
index 40e2c19..afe4342 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
12f005
index f94e8ff..014feb8 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
12f005
index 0000000..8cecd8e
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
-- 
12f005
2.7.4
12f005