All of lore.kernel.org
 help / color / mirror / Atom feed
* [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT
@ 2022-03-31 10:12 Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 1/9] man: DNAT: Describe shifted port range feature Phil Sutter
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Second try, without fancy service name parsing in ranges this time.
Instead, mention support for names outside of ranges in extensions man
page.

Other changes since v1:
- Fixed for garbage in output when listing multiple DNAT rules (due to
  missing reinit of a static buffer.
- Drop of last patch revealed a crash bug in service name parser.
- Do not allow service names in ranges' upper boundary.
- More test cases.

Phil Sutter (9):
  man: DNAT: Describe shifted port range feature
  Revert "libipt_[SD]NAT: avoid false error about multiple destinations
    specified"
  extensions: ipt_DNAT: Merge v1 and v2 parsers
  extensions: ipt_DNAT: Merge v1/v2 print/save code
  extensions: ipt_DNAT: Combine xlate functions also
  extensions: DNAT: Rename from libipt to libxt
  extensions: Merge IPv4 and IPv6 DNAT targets
  extensions: Merge REDIRECT into DNAT
  extensions: man: Document service name support in DNAT and REDIRECT

 extensions/GNUmakefile.in          |   4 +-
 extensions/libip6t_DNAT.c          | 409 ------------------
 extensions/libip6t_DNAT.t          |   4 +
 extensions/libip6t_DNAT.txlate     |  11 -
 extensions/libip6t_REDIRECT.c      | 170 --------
 extensions/libip6t_REDIRECT.t      |   6 -
 extensions/libip6t_REDIRECT.txlate |   5 -
 extensions/libip6t_SNAT.c          |   9 +-
 extensions/libipt_DNAT.c           | 507 ----------------------
 extensions/libipt_DNAT.t           |   4 +
 extensions/libipt_DNAT.txlate      |  14 -
 extensions/libipt_REDIRECT.c       | 174 --------
 extensions/libipt_REDIRECT.t       |   6 -
 extensions/libipt_REDIRECT.txlate  |   5 -
 extensions/libipt_SNAT.c           |   3 -
 extensions/libxt_DNAT.c            | 650 +++++++++++++++++++++++++++++
 extensions/libxt_DNAT.man          |   7 +-
 extensions/libxt_DNAT.txlate       |  35 ++
 extensions/libxt_REDIRECT.man      |   1 +
 extensions/libxt_REDIRECT.t        |  16 +
 extensions/libxt_REDIRECT.txlate   |  26 ++
 21 files changed, 746 insertions(+), 1320 deletions(-)
 delete mode 100644 extensions/libip6t_DNAT.c
 delete mode 100644 extensions/libip6t_DNAT.txlate
 delete mode 100644 extensions/libip6t_REDIRECT.c
 delete mode 100644 extensions/libip6t_REDIRECT.t
 delete mode 100644 extensions/libip6t_REDIRECT.txlate
 delete mode 100644 extensions/libipt_DNAT.c
 delete mode 100644 extensions/libipt_DNAT.txlate
 delete mode 100644 extensions/libipt_REDIRECT.c
 delete mode 100644 extensions/libipt_REDIRECT.t
 delete mode 100644 extensions/libipt_REDIRECT.txlate
 create mode 100644 extensions/libxt_DNAT.c
 create mode 100644 extensions/libxt_DNAT.txlate
 create mode 100644 extensions/libxt_REDIRECT.t
 create mode 100644 extensions/libxt_REDIRECT.txlate

-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 1/9] man: DNAT: Describe shifted port range feature
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 2/9] Revert "libipt_[SD]NAT: avoid false error about multiple destinations specified" Phil Sutter
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

This wasn't mentioned anywhere.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libxt_DNAT.man | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/extensions/libxt_DNAT.man b/extensions/libxt_DNAT.man
index c3daea9a40394..e044c8216fc09 100644
--- a/extensions/libxt_DNAT.man
+++ b/extensions/libxt_DNAT.man
@@ -10,7 +10,7 @@ should be modified (and all future packets in this connection will
 also be mangled), and rules should cease being examined.  It takes the
 following options:
 .TP
-\fB\-\-to\-destination\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]]
+\fB\-\-to\-destination\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP[\fB/\fIbaseport\fP]]]
 which can specify a single new destination IP address, an inclusive
 range of IP addresses. Optionally a port range,
 if the rule also specifies one of the following protocols:
@@ -18,6 +18,9 @@ if the rule also specifies one of the following protocols:
 If no port range is specified, then the destination port will never be
 modified. If no IP address is specified then only the destination port
 will be modified.
+If \fBbaseport\fP is given, the difference of the original destination port and
+its value is used as offset into the mapping port range. This allows to create
+shifted portmap ranges and is available since kernel version 4.18.
 .TP
 \fB\-\-random\fP
 If option
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 2/9] Revert "libipt_[SD]NAT: avoid false error about multiple destinations specified"
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 1/9] man: DNAT: Describe shifted port range feature Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 3/9] extensions: ipt_DNAT: Merge v1 and v2 parsers Phil Sutter
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

This reverts commit f25b2355e889290879c8cecad3dd24ec0c384fb8.

The workaround is not needed anymore since commit 30b178b9bf11e
("extensions: *NAT: Kill multiple IPv4 range support").

While being at it, drop the same hidden flag logic from
libip6t_[SD]NAT extensions as well and just don't set XTOPT_MULTI so
guided option parser will reject multiple parameters automatically.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libip6t_DNAT.c | 9 +--------
 extensions/libip6t_SNAT.c | 9 +--------
 extensions/libipt_DNAT.c  | 8 ++------
 extensions/libipt_SNAT.c  | 3 ---
 4 files changed, 4 insertions(+), 25 deletions(-)

diff --git a/extensions/libip6t_DNAT.c b/extensions/libip6t_DNAT.c
index f1ad81436316b..d51994c09e7f2 100644
--- a/extensions/libip6t_DNAT.c
+++ b/extensions/libip6t_DNAT.c
@@ -19,10 +19,8 @@ enum {
 	O_TO_DEST = 0,
 	O_RANDOM,
 	O_PERSISTENT,
-	O_X_TO_DEST,
 	F_TO_DEST   = 1 << O_TO_DEST,
 	F_RANDOM   = 1 << O_RANDOM,
-	F_X_TO_DEST = 1 << O_X_TO_DEST,
 };
 
 static void DNAT_help(void)
@@ -45,7 +43,7 @@ static void DNAT_help_v2(void)
 
 static const struct xt_option_entry DNAT_opts[] = {
 	{.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
-	 .flags = XTOPT_MAND | XTOPT_MULTI},
+	 .flags = XTOPT_MAND},
 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
 	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
 	XTOPT_TABLEEND,
@@ -183,12 +181,7 @@ static void _DNAT_parse(struct xt_option_call *cb,
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_TO_DEST:
-		if (cb->xflags & F_X_TO_DEST) {
-			xtables_error(PARAMETER_PROBLEM,
-				      "DNAT: Multiple --to-destination not supported");
-		}
 		parse_to(cb->arg, portok, range, rev);
-		cb->xflags |= F_X_TO_DEST;
 		break;
 	case O_PERSISTENT:
 		range->flags |= NF_NAT_RANGE_PERSISTENT;
diff --git a/extensions/libip6t_SNAT.c b/extensions/libip6t_SNAT.c
index 6d19614c7c708..4fe272b262a3d 100644
--- a/extensions/libip6t_SNAT.c
+++ b/extensions/libip6t_SNAT.c
@@ -20,11 +20,9 @@ enum {
 	O_RANDOM,
 	O_RANDOM_FULLY,
 	O_PERSISTENT,
-	O_X_TO_SRC,
 	F_TO_SRC       = 1 << O_TO_SRC,
 	F_RANDOM       = 1 << O_RANDOM,
 	F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
-	F_X_TO_SRC     = 1 << O_X_TO_SRC,
 };
 
 static void SNAT_help(void)
@@ -38,7 +36,7 @@ static void SNAT_help(void)
 
 static const struct xt_option_entry SNAT_opts[] = {
 	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
-	 .flags = XTOPT_MAND | XTOPT_MULTI},
+	 .flags = XTOPT_MAND},
 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
 	{.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
 	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
@@ -163,12 +161,7 @@ static void SNAT_parse(struct xt_option_call *cb)
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_TO_SRC:
-		if (cb->xflags & F_X_TO_SRC) {
-			xtables_error(PARAMETER_PROBLEM,
-				      "SNAT: Multiple --to-source not supported");
-		}
 		parse_to(cb->arg, portok, range);
-		cb->xflags |= F_X_TO_SRC;
 		break;
 	case O_PERSISTENT:
 		range->flags |= NF_NAT_RANGE_PERSISTENT;
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index eefa95eb73630..e93ab6958969b 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -12,10 +12,8 @@ enum {
 	O_TO_DEST = 0,
 	O_RANDOM,
 	O_PERSISTENT,
-	O_X_TO_DEST, /* hidden flag */
-	F_TO_DEST   = 1 << O_TO_DEST,
-	F_RANDOM    = 1 << O_RANDOM,
-	F_X_TO_DEST = 1 << O_X_TO_DEST,
+	F_TO_DEST = 1 << O_TO_DEST,
+	F_RANDOM  = 1 << O_RANDOM,
 };
 
 static void DNAT_help(void)
@@ -145,7 +143,6 @@ static void DNAT_parse(struct xt_option_call *cb)
 	switch (cb->entry->id) {
 	case O_TO_DEST:
 		parse_to(cb->arg, portok, mr->range);
-		cb->xflags |= F_X_TO_DEST;
 		break;
 	case O_PERSISTENT:
 		mr->range->flags |= NF_NAT_RANGE_PERSISTENT;
@@ -367,7 +364,6 @@ static void DNAT_parse_v2(struct xt_option_call *cb)
 	switch (cb->entry->id) {
 	case O_TO_DEST:
 		parse_to_v2(cb->arg, portok, range);
-		cb->xflags |= F_X_TO_DEST;
 		break;
 	case O_PERSISTENT:
 		range->flags |= NF_NAT_RANGE_PERSISTENT;
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
index bd36830ae91ce..211a20bc45bfe 100644
--- a/extensions/libipt_SNAT.c
+++ b/extensions/libipt_SNAT.c
@@ -13,11 +13,9 @@ enum {
 	O_RANDOM,
 	O_RANDOM_FULLY,
 	O_PERSISTENT,
-	O_X_TO_SRC,
 	F_TO_SRC       = 1 << O_TO_SRC,
 	F_RANDOM       = 1 << O_RANDOM,
 	F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
-	F_X_TO_SRC     = 1 << O_X_TO_SRC,
 };
 
 static void SNAT_help(void)
@@ -139,7 +137,6 @@ static void SNAT_parse(struct xt_option_call *cb)
 	switch (cb->entry->id) {
 	case O_TO_SRC:
 		parse_to(cb->arg, portok, mr->range);
-		cb->xflags |= F_X_TO_SRC;
 		break;
 	case O_PERSISTENT:
 		mr->range->flags |= NF_NAT_RANGE_PERSISTENT;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 3/9] extensions: ipt_DNAT: Merge v1 and v2 parsers
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 1/9] man: DNAT: Describe shifted port range feature Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 2/9] Revert "libipt_[SD]NAT: avoid false error about multiple destinations specified" Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 4/9] extensions: ipt_DNAT: Merge v1/v2 print/save code Phil Sutter
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Use v2 parser for both and copy field values into v1 data structure if
needed.

While being at it:

* Introduce parse_ports() function similar to the one in
  libipt_REDIRECT.c.
* Use xtables_strtoui() in the above instead of atoi() for integrated
  range checking.
* Parse IP addresses using inet_pton(), writing directly into
  struct nf_nat_range2 fields.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libipt_DNAT.c | 290 +++++++++++++++------------------------
 1 file changed, 111 insertions(+), 179 deletions(-)

diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index e93ab6958969b..2a7b1bc4ec0a6 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -5,6 +5,7 @@
 #include <xtables.h>
 #include <iptables.h> /* get_kernel_version */
 #include <limits.h> /* INT_MAX in ip_tables.h */
+#include <arpa/inet.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter/nf_nat.h>
 
@@ -42,54 +43,83 @@ static const struct xt_option_entry DNAT_opts[] = {
 	XTOPT_TABLEEND,
 };
 
+/* Parses ports */
+static void
+parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
+{
+	unsigned int port, maxport, baseport;
+	char *end = NULL;
+
+	if (!portok)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Need TCP, UDP, SCTP or DCCP with port specification");
+
+	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+	if (!xtables_strtoui(arg, &end, &port, 1, UINT16_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			      "Port `%s' not valid", arg);
+
+	switch (*end) {
+	case '\0':
+		range->min_proto.tcp.port
+			= range->max_proto.tcp.port
+			= htons(port);
+		return;
+	case '-':
+		arg = end + 1;
+		break;
+	case ':':
+		xtables_error(PARAMETER_PROBLEM,
+			      "Invalid port:port syntax - use dash");
+	default:
+		xtables_error(PARAMETER_PROBLEM,
+			      "Garbage after port value: `%s'", end);
+	}
+
+	if (!xtables_strtoui(arg, &end, &maxport, 1, UINT16_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			      "Port `%s' not valid", arg);
+
+	if (maxport < port)
+		/* People are stupid. */
+		xtables_error(PARAMETER_PROBLEM,
+			   "Port range `%s' funky", arg);
+
+	range->min_proto.tcp.port = htons(port);
+	range->max_proto.tcp.port = htons(maxport);
+
+	switch (*end) {
+	case '\0':
+		return;
+	case '/':
+		arg = end + 1;
+		break;
+	default:
+		xtables_error(PARAMETER_PROBLEM,
+			      "Garbage after port range: `%s'", end);
+	}
+
+	if (!xtables_strtoui(arg, &end, &baseport, 1, UINT16_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			      "Port `%s' not valid", arg);
+
+	range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
+	range->base_proto.tcp.port = htons(baseport);
+}
+
 /* Ranges expected in network order. */
 static void
-parse_to(const char *orig_arg, int portok, struct nf_nat_ipv4_range *range)
+parse_to(const char *orig_arg, bool portok, struct nf_nat_range2 *range)
 {
-	char *arg, *colon, *dash, *error;
-	const struct in_addr *ip;
+	char *arg, *colon, *dash;
 
 	arg = xtables_strdup(orig_arg);
 	colon = strchr(arg, ':');
 
 	if (colon) {
-		int port;
-
-		if (!portok)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Need TCP, UDP, SCTP or DCCP with port specification");
-
-		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+		parse_ports(colon + 1, portok, range);
 
-		port = atoi(colon+1);
-		if (port <= 0 || port > 65535)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Port `%s' not valid\n", colon+1);
-
-		error = strchr(colon+1, ':');
-		if (error)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Invalid port:port syntax - use dash\n");
-
-		dash = strchr(colon, '-');
-		if (!dash) {
-			range->min.tcp.port
-				= range->max.tcp.port
-				= htons(port);
-		} else {
-			int maxport;
-
-			maxport = atoi(dash + 1);
-			if (maxport <= 0 || maxport > 65535)
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port `%s' not valid\n", dash+1);
-			if (maxport < port)
-				/* People are stupid. */
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port range `%s' funky\n", colon+1);
-			range->min.tcp.port = htons(port);
-			range->max.tcp.port = htons(maxport);
-		}
 		/* Starts with a colon? No IP info...*/
 		if (colon == arg) {
 			free(arg);
@@ -106,46 +136,57 @@ parse_to(const char *orig_arg, int portok, struct nf_nat_ipv4_range *range)
 	if (dash)
 		*dash = '\0';
 
-	ip = xtables_numeric_to_ipaddr(arg);
-	if (!ip)
-		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-			   arg);
-	range->min_ip = ip->s_addr;
+	if (!inet_pton(AF_INET, arg, &range->min_addr))
+		xtables_error(PARAMETER_PROBLEM,
+			      "Bad IP address \"%s\"\n", arg);
 	if (dash) {
-		ip = xtables_numeric_to_ipaddr(dash+1);
-		if (!ip)
-			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-				   dash+1);
-		range->max_ip = ip->s_addr;
-	} else
-		range->max_ip = range->min_ip;
-
+		if (!inet_pton(AF_INET, dash + 1, &range->max_addr))
+			xtables_error(PARAMETER_PROBLEM,
+				      "Bad IP address \"%s\"\n", dash + 1);
+	} else {
+		range->max_addr = range->min_addr;
+	}
 	free(arg);
 	return;
 }
 
+static void __DNAT_parse(struct xt_option_call *cb, __u16 proto,
+			 struct nf_nat_range2 *range)
+{
+	bool portok = proto == IPPROTO_TCP ||
+		      proto == IPPROTO_UDP ||
+		      proto == IPPROTO_SCTP ||
+		      proto == IPPROTO_DCCP ||
+		      proto == IPPROTO_ICMP;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_DEST:
+		parse_to(cb->arg, portok, range);
+		break;
+	case O_PERSISTENT:
+		range->flags |= NF_NAT_RANGE_PERSISTENT;
+		break;
+	}
+}
+
 static void DNAT_parse(struct xt_option_call *cb)
 {
 	struct nf_nat_ipv4_multi_range_compat *mr = (void *)cb->data;
 	const struct ipt_entry *entry = cb->xt_entry;
-	int portok;
+	struct nf_nat_range2 range = {};
 
-	if (entry->ip.proto == IPPROTO_TCP
-	    || entry->ip.proto == IPPROTO_UDP
-	    || entry->ip.proto == IPPROTO_SCTP
-	    || entry->ip.proto == IPPROTO_DCCP
-	    || entry->ip.proto == IPPROTO_ICMP)
-		portok = 1;
-	else
-		portok = 0;
+	__DNAT_parse(cb, entry->ip.proto, &range);
 
-	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_TO_DEST:
-		parse_to(cb->arg, portok, mr->range);
-		break;
+		mr->range->min_ip = range.min_addr.ip;
+		mr->range->max_ip = range.max_addr.ip;
+		mr->range->min = range.min_proto;
+		mr->range->max = range.max_proto;
+		/* fall through */
 	case O_PERSISTENT:
-		mr->range->flags |= NF_NAT_RANGE_PERSISTENT;
+		mr->range->flags |= range.flags;
 		break;
 	}
 }
@@ -159,6 +200,10 @@ static void DNAT_fcheck(struct xt_fcheck_call *cb)
 		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
 
 	mr->rangesize = 1;
+
+	if (mr->range[0].flags & NF_NAT_RANGE_PROTO_OFFSET)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Shifted portmap ranges not supported with this kernel");
 }
 
 static void print_range(const struct nf_nat_ipv4_range *r)
@@ -251,124 +296,11 @@ static int DNAT_xlate(struct xt_xlate *xl,
 	return 1;
 }
 
-static void
-parse_to_v2(const char *orig_arg, int portok, struct nf_nat_range2 *range)
-{
-	char *arg, *colon, *dash, *error;
-	const struct in_addr *ip;
-
-	arg = xtables_strdup(orig_arg);
-
-	colon = strchr(arg, ':');
-	if (colon) {
-		int port;
-
-		if (!portok)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Need TCP, UDP, SCTP or DCCP with port specification");
-
-		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
-
-		port = atoi(colon+1);
-		if (port <= 0 || port > 65535)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Port `%s' not valid\n", colon+1);
-
-		error = strchr(colon+1, ':');
-		if (error)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Invalid port:port syntax - use dash\n");
-
-		dash = strchr(colon, '-');
-		if (!dash) {
-			range->min_proto.tcp.port
-				= range->max_proto.tcp.port
-				= htons(port);
-		} else {
-			int maxport;
-			char *slash;
-
-			maxport = atoi(dash + 1);
-			if (maxport <= 0 || maxport > 65535)
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port `%s' not valid\n", dash+1);
-			if (maxport < port)
-				/* People are stupid. */
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port range `%s' funky\n", colon+1);
-			range->min_proto.tcp.port = htons(port);
-			range->max_proto.tcp.port = htons(maxport);
-
-			slash = strchr(dash, '/');
-			if (slash) {
-				int baseport;
-
-				baseport = atoi(slash + 1);
-				if (baseport <= 0 || baseport > 65535)
-					xtables_error(PARAMETER_PROBLEM,
-							 "Port `%s' not valid\n", slash+1);
-				range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
-				range->base_proto.tcp.port = htons(baseport);
-			}
-		}
-		/* Starts with a colon? No IP info...*/
-		if (colon == arg) {
-			free(arg);
-			return;
-		}
-		*colon = '\0';
-	}
-
-	range->flags |= NF_NAT_RANGE_MAP_IPS;
-	dash = strchr(arg, '-');
-	if (colon && dash && dash > colon)
-		dash = NULL;
-
-	if (dash)
-		*dash = '\0';
-
-	ip = xtables_numeric_to_ipaddr(arg);
-	if (!ip)
-		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-			   arg);
-	range->min_addr.in = *ip;
-	if (dash) {
-		ip = xtables_numeric_to_ipaddr(dash+1);
-		if (!ip)
-			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-				   dash+1);
-		range->max_addr.in = *ip;
-	} else
-		range->max_addr = range->min_addr;
-
-	free(arg);
-	return;
-}
-
 static void DNAT_parse_v2(struct xt_option_call *cb)
 {
 	const struct ipt_entry *entry = cb->xt_entry;
-	struct nf_nat_range2 *range = cb->data;
-	int portok;
-
-	if (entry->ip.proto == IPPROTO_TCP
-	    || entry->ip.proto == IPPROTO_UDP
-	    || entry->ip.proto == IPPROTO_SCTP
-	    || entry->ip.proto == IPPROTO_DCCP
-	    || entry->ip.proto == IPPROTO_ICMP)
-		portok = 1;
-	else
-		portok = 0;
 
-	xtables_option_parse(cb);
-	switch (cb->entry->id) {
-	case O_TO_DEST:
-		parse_to_v2(cb->arg, portok, range);
-		break;
-	case O_PERSISTENT:
-		range->flags |= NF_NAT_RANGE_PERSISTENT;
-		break;
-	}
+	__DNAT_parse(cb, entry->ip.proto, cb->data);
 }
 
 static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 4/9] extensions: ipt_DNAT: Merge v1/v2 print/save code
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (2 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 3/9] extensions: ipt_DNAT: Merge v1 and v2 parsers Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 5/9] extensions: ipt_DNAT: Combine xlate functions also Phil Sutter
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Turn print_range() function into sprint_range() so it becomes more
versatile. Make it accept the new nf_nat_range2 data structure and
make v1 callers convert their nf_nat_ipv4_multi_range_compat structs
to that.
This allows to introduce an inner __DNAT_print() which acts for v1 and
v2 and prints either 'print' or 'save' syntax.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libipt_DNAT.c | 111 ++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 65 deletions(-)

diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index 2a7b1bc4ec0a6..b72437d5e92f2 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -9,6 +9,15 @@
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter/nf_nat.h>
 
+#define TO_IPV4_MRC(ptr) ((const struct nf_nat_ipv4_multi_range_compat *)(ptr))
+#define RANGE2_INIT_FROM_IPV4_MRC(ptr) {			\
+	.flags		= TO_IPV4_MRC(ptr)->range[0].flags,	\
+	.min_addr.ip	= TO_IPV4_MRC(ptr)->range[0].min_ip,	\
+	.max_addr.ip	= TO_IPV4_MRC(ptr)->range[0].max_ip,	\
+	.min_proto	= TO_IPV4_MRC(ptr)->range[0].min,	\
+	.max_proto	= TO_IPV4_MRC(ptr)->range[0].max,	\
+};
+
 enum {
 	O_TO_DEST = 0,
 	O_RANDOM,
@@ -206,51 +215,55 @@ static void DNAT_fcheck(struct xt_fcheck_call *cb)
 			      "Shifted portmap ranges not supported with this kernel");
 }
 
-static void print_range(const struct nf_nat_ipv4_range *r)
+static char *sprint_range(const struct nf_nat_range2 *r)
 {
-	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
-		struct in_addr a;
+	static char buf[INET_ADDRSTRLEN * 2 + 1 + 6 * 3];
 
-		a.s_addr = r->min_ip;
-		printf("%s", xtables_ipaddr_to_numeric(&a));
-		if (r->max_ip != r->min_ip) {
-			a.s_addr = r->max_ip;
-			printf("-%s", xtables_ipaddr_to_numeric(&a));
-		}
+	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
+		sprintf(buf, "%s", xtables_ipaddr_to_numeric(&r->min_addr.in));
+		if (memcmp(&r->min_addr, &r->max_addr, sizeof(r->min_addr)))
+			sprintf(buf + strlen(buf), "-%s",
+				xtables_ipaddr_to_numeric(&r->max_addr.in));
+	} else {
+		buf[0] = '\0';
 	}
 	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(":");
-		printf("%hu", ntohs(r->min.tcp.port));
-		if (r->max.tcp.port != r->min.tcp.port)
-			printf("-%hu", ntohs(r->max.tcp.port));
+		sprintf(buf + strlen(buf), ":%hu",
+			ntohs(r->min_proto.tcp.port));
+		if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+			sprintf(buf + strlen(buf), "-%hu",
+				ntohs(r->max_proto.tcp.port));
+		if (r->flags & NF_NAT_RANGE_PROTO_OFFSET)
+			sprintf(buf + strlen(buf), "/%hu",
+				ntohs(r->base_proto.tcp.port));
 	}
+	return buf;
+}
+
+static void __DNAT_print(const struct nf_nat_range2 *r, bool save)
+{
+	const char *dashdash = save ? "--" : "";
+
+	printf(" %s%s", save ? "--to-destination " : "to:", sprint_range(r));
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" %srandom", dashdash);
+	if (r->flags & NF_NAT_RANGE_PERSISTENT)
+		printf(" %spersistent", dashdash);
 }
 
 static void DNAT_print(const void *ip, const struct xt_entry_target *target,
                        int numeric)
 {
-	const struct nf_nat_ipv4_multi_range_compat *mr =
-				(const void *)target->data;
-
-	printf(" to:");
-	print_range(mr->range);
-	if (mr->range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" random");
-	if (mr->range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" persistent");
+	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
+
+	__DNAT_print(&range, false);
 }
 
 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
 {
-	const struct nf_nat_ipv4_multi_range_compat *mr =
-				(const void *)target->data;
-
-	printf(" --to-destination ");
-	print_range(mr->range);
-	if (mr->range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" --random");
-	if (mr->range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" --persistent");
+	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
+
+	__DNAT_print(&range, true);
 }
 
 static void print_range_xlate(const struct nf_nat_ipv4_range *r,
@@ -312,47 +325,15 @@ static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
 		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
 }
 
-static void print_range_v2(const struct nf_nat_range2 *range)
-{
-	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
-		printf("%s", xtables_ipaddr_to_numeric(&range->min_addr.in));
-		if (memcmp(&range->min_addr, &range->max_addr,
-			   sizeof(range->min_addr)))
-			printf("-%s", xtables_ipaddr_to_numeric(&range->max_addr.in));
-	}
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(":");
-		printf("%hu", ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			printf("-%hu", ntohs(range->max_proto.tcp.port));
-		if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
-			printf("/%hu", ntohs(range->base_proto.tcp.port));
-	}
-}
-
 static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target,
                        int numeric)
 {
-	const struct nf_nat_range2 *range = (const void *)target->data;
-
-	printf(" to:");
-	print_range_v2(range);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" random");
-	if (range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" persistent");
+	__DNAT_print((const void *)target->data, false);
 }
 
 static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
 {
-	const struct nf_nat_range2 *range = (const void *)target->data;
-
-	printf(" --to-destination ");
-	print_range_v2(range);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" --random");
-	if (range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" --persistent");
+	__DNAT_print((const void *)target->data, true);
 }
 
 static void print_range_xlate_v2(const struct nf_nat_range2 *range,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 5/9] extensions: ipt_DNAT: Combine xlate functions also
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (3 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 4/9] extensions: ipt_DNAT: Merge v1/v2 print/save code Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 6/9] extensions: DNAT: Rename from libipt to libxt Phil Sutter
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Make use of the new sprint_range() to introduce a common inner function
for both v1 and v2 xlate functions.

Also abort translation with shifted port ranges to not hide the missing
feature in nftables.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libipt_DNAT.c | 88 ++++++++++------------------------------
 1 file changed, 21 insertions(+), 67 deletions(-)

diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index b72437d5e92f2..9a179919f522d 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -266,47 +266,36 @@ static void DNAT_save(const void *ip, const struct xt_entry_target *target)
 	__DNAT_print(&range, true);
 }
 
-static void print_range_xlate(const struct nf_nat_ipv4_range *r,
-			struct xt_xlate *xl)
+static int __DNAT_xlate(struct xt_xlate *xl, const struct nf_nat_range2 *r)
 {
-	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
-		struct in_addr a;
+	char *range_str = sprint_range(r);
+	const char *sep = " ";
 
-		a.s_addr = r->min_ip;
-		xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a));
-		if (r->max_ip != r->min_ip) {
-			a.s_addr = r->max_ip;
-			xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a));
-		}
+	/* shifted portmap ranges are not supported by nftables */
+	if (r->flags & NF_NAT_RANGE_PROTO_OFFSET)
+		return 0;
+
+	xt_xlate_add(xl, "dnat");
+	if (strlen(range_str))
+		xt_xlate_add(xl, " to %s", range_str);
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM) {
+		xt_xlate_add(xl, "%srandom", sep);
+		sep = ",";
 	}
-	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		xt_xlate_add(xl, ":%hu", ntohs(r->min.tcp.port));
-		if (r->max.tcp.port != r->min.tcp.port)
-			xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
+	if (r->flags & NF_NAT_RANGE_PERSISTENT) {
+		xt_xlate_add(xl, "%spersistent", sep);
+		sep = ",";
 	}
+	return 1;
 }
 
 static int DNAT_xlate(struct xt_xlate *xl,
 		      const struct xt_xlate_tg_params *params)
 {
-	const struct nf_nat_ipv4_multi_range_compat *mr =
-			(const void *)params->target->data;
-	bool sep_need = false;
-	const char *sep = " ";
-
-	xt_xlate_add(xl, "dnat to ");
-	print_range_xlate(mr->range, xl);
-	if (mr->range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
-		xt_xlate_add(xl, " random");
-		sep_need = true;
-	}
-	if (mr->range->flags & NF_NAT_RANGE_PERSISTENT) {
-		if (sep_need)
-			sep = ",";
-		xt_xlate_add(xl, "%spersistent", sep);
-	}
+	struct nf_nat_range2 range =
+		RANGE2_INIT_FROM_IPV4_MRC(params->target->data);
 
-	return 1;
+	return __DNAT_xlate(xl, &range);
 }
 
 static void DNAT_parse_v2(struct xt_option_call *cb)
@@ -336,45 +325,10 @@ static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
 	__DNAT_print((const void *)target->data, true);
 }
 
-static void print_range_xlate_v2(const struct nf_nat_range2 *range,
-			      struct xt_xlate *xl)
-{
-	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
-		xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&range->min_addr.in));
-		if (memcmp(&range->min_addr, &range->max_addr,
-			   sizeof(range->min_addr))) {
-			xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&range->max_addr.in));
-		}
-	}
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			xt_xlate_add(xl, "-%hu", ntohs(range->max_proto.tcp.port));
-		if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
-			xt_xlate_add(xl, ";%hu", ntohs(range->base_proto.tcp.port));
-	}
-}
-
 static int DNAT_xlate_v2(struct xt_xlate *xl,
 		      const struct xt_xlate_tg_params *params)
 {
-	const struct nf_nat_range2 *range = (const void *)params->target->data;
-	bool sep_need = false;
-	const char *sep = " ";
-
-	xt_xlate_add(xl, "dnat to ");
-	print_range_xlate_v2(range, xl);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
-		xt_xlate_add(xl, " random");
-		sep_need = true;
-	}
-	if (range->flags & NF_NAT_RANGE_PERSISTENT) {
-		if (sep_need)
-			sep = ",";
-		xt_xlate_add(xl, "%spersistent", sep);
-	}
-
-	return 1;
+	return __DNAT_xlate(xl, (const void *)params->target->data);
 }
 
 static struct xtables_target dnat_tg_reg[] = {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 6/9] extensions: DNAT: Rename from libipt to libxt
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (4 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 5/9] extensions: ipt_DNAT: Combine xlate functions also Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 7/9] extensions: Merge IPv4 and IPv6 DNAT targets Phil Sutter
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Prepare for merge of libipt and libip6t DNAT extensions, allow for
better code review.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/{libipt_DNAT.c => libxt_DNAT.c} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename extensions/{libipt_DNAT.c => libxt_DNAT.c} (100%)

diff --git a/extensions/libipt_DNAT.c b/extensions/libxt_DNAT.c
similarity index 100%
rename from extensions/libipt_DNAT.c
rename to extensions/libxt_DNAT.c
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 7/9] extensions: Merge IPv4 and IPv6 DNAT targets
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (5 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 6/9] extensions: DNAT: Rename from libipt to libxt Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 8/9] extensions: Merge REDIRECT into DNAT Phil Sutter
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Make parse_to() family-aware so it serves for both IPv4 and IPv6. Have a
core _DNAT_parse() function which parses into the most modern
(nf_nat_range2) data structure and a bunch of wrappers to copy into
legacy data structures if needed. Treat other callbacks analogous.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v1:
- Fix for garbage in output when listing multiple rules.
---
 extensions/libip6t_DNAT.c      | 402 ---------------------------------
 extensions/libip6t_DNAT.txlate |  11 -
 extensions/libipt_DNAT.txlate  |  14 --
 extensions/libxt_DNAT.c        | 224 ++++++++++++++----
 extensions/libxt_DNAT.txlate   |  35 +++
 5 files changed, 218 insertions(+), 468 deletions(-)
 delete mode 100644 extensions/libip6t_DNAT.c
 delete mode 100644 extensions/libip6t_DNAT.txlate
 delete mode 100644 extensions/libipt_DNAT.txlate
 create mode 100644 extensions/libxt_DNAT.txlate

diff --git a/extensions/libip6t_DNAT.c b/extensions/libip6t_DNAT.c
deleted file mode 100644
index d51994c09e7f2..0000000000000
--- a/extensions/libip6t_DNAT.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
- *
- * Based on Rusty Russell's IPv4 DNAT target. Development of IPv6 NAT
- * funded by Astaro.
- */
-
-#include <stdio.h>
-#include <netdb.h>
-#include <string.h>
-#include <stdlib.h>
-#include <xtables.h>
-#include <iptables.h>
-#include <limits.h> /* INT_MAX in ip_tables.h */
-#include <linux/netfilter_ipv6/ip6_tables.h>
-#include <linux/netfilter/nf_nat.h>
-
-enum {
-	O_TO_DEST = 0,
-	O_RANDOM,
-	O_PERSISTENT,
-	F_TO_DEST   = 1 << O_TO_DEST,
-	F_RANDOM   = 1 << O_RANDOM,
-};
-
-static void DNAT_help(void)
-{
-	printf(
-"DNAT target options:\n"
-" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
-"				Address to map destination to.\n"
-"[--random] [--persistent]\n");
-}
-
-static void DNAT_help_v2(void)
-{
-	printf(
-"DNAT target options:\n"
-" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
-"				Address to map destination to.\n"
-"[--random] [--persistent]\n");
-}
-
-static const struct xt_option_entry DNAT_opts[] = {
-	{.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
-	 .flags = XTOPT_MAND},
-	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
-	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
-	XTOPT_TABLEEND,
-};
-
-/* Ranges expected in network order. */
-static void
-parse_to(const char *orig_arg, int portok, struct nf_nat_range2 *range, int rev)
-{
-	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
-	const struct in6_addr *ip;
-
-	arg = xtables_strdup(orig_arg);
-
-	start = strchr(arg, '[');
-	if (start == NULL) {
-		start = arg;
-		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
-		colon = strchr(arg, ':');
-		if (colon && strchr(colon+1, ':'))
-			colon = NULL;
-	}
-	else {
-		start++;
-		end = strchr(start, ']');
-		if (end == NULL)
-			xtables_error(PARAMETER_PROBLEM,
-				      "Invalid address format");
-
-		*end = '\0';
-		colon = strchr(end + 1, ':');
-	}
-
-	if (colon) {
-		int port;
-
-		if (!portok)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Need TCP, UDP, SCTP or DCCP with port specification");
-
-		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
-
-		port = atoi(colon+1);
-		if (port <= 0 || port > 65535)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Port `%s' not valid\n", colon+1);
-
-		error = strchr(colon+1, ':');
-		if (error)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Invalid port:port syntax - use dash\n");
-
-		dash = strchr(colon, '-');
-		if (!dash) {
-			range->min_proto.tcp.port
-				= range->max_proto.tcp.port
-				= htons(port);
-		} else {
-			int maxport;
-
-			maxport = atoi(dash + 1);
-			if (maxport <= 0 || maxport > 65535)
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port `%s' not valid\n", dash+1);
-			if (maxport < port)
-				/* People are stupid. */
-				xtables_error(PARAMETER_PROBLEM,
-					   "Port range `%s' funky\n", colon+1);
-			range->min_proto.tcp.port = htons(port);
-			range->max_proto.tcp.port = htons(maxport);
-
-			if (rev >= 2) {
-				char *slash = strchr(dash, '/');
-				if (slash) {
-					int baseport;
-
-					baseport = atoi(slash + 1);
-					if (baseport <= 0 || baseport > 65535)
-						xtables_error(PARAMETER_PROBLEM,
-								 "Port `%s' not valid\n", slash+1);
-					range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
-					range->base_proto.tcp.port = htons(baseport);
-				}
-			}
-		}
-		/* Starts with colon or [] colon? No IP info...*/
-		if (colon == arg || colon == arg+2) {
-			free(arg);
-			return;
-		}
-		*colon = '\0';
-	}
-
-	range->flags |= NF_NAT_RANGE_MAP_IPS;
-	dash = strchr(start, '-');
-	if (colon && dash && dash > colon)
-		dash = NULL;
-
-	if (dash)
-		*dash = '\0';
-
-	ip = xtables_numeric_to_ip6addr(start);
-	if (!ip)
-		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-			      start);
-	range->min_addr.in6 = *ip;
-	if (dash) {
-		ip = xtables_numeric_to_ip6addr(dash + 1);
-		if (!ip)
-			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
-				      dash+1);
-		range->max_addr.in6 = *ip;
-	} else
-		range->max_addr = range->min_addr;
-
-	free(arg);
-	return;
-}
-
-static void _DNAT_parse(struct xt_option_call *cb,
-		struct nf_nat_range2 *range, int rev)
-{
-	const struct ip6t_entry *entry = cb->xt_entry;
-	int portok;
-
-	if (entry->ipv6.proto == IPPROTO_TCP ||
-	    entry->ipv6.proto == IPPROTO_UDP ||
-	    entry->ipv6.proto == IPPROTO_SCTP ||
-	    entry->ipv6.proto == IPPROTO_DCCP ||
-	    entry->ipv6.proto == IPPROTO_ICMP)
-		portok = 1;
-	else
-		portok = 0;
-
-	xtables_option_parse(cb);
-	switch (cb->entry->id) {
-	case O_TO_DEST:
-		parse_to(cb->arg, portok, range, rev);
-		break;
-	case O_PERSISTENT:
-		range->flags |= NF_NAT_RANGE_PERSISTENT;
-		break;
-	}
-}
-
-static void DNAT_parse(struct xt_option_call *cb)
-{
-	struct nf_nat_range *range_v1 = (void *)cb->data;
-	struct nf_nat_range2 range = {};
-
-	memcpy(&range, range_v1, sizeof(*range_v1));
-	_DNAT_parse(cb, &range, 1);
-	memcpy(range_v1, &range, sizeof(*range_v1));
-}
-
-static void DNAT_parse_v2(struct xt_option_call *cb)
-{
-	_DNAT_parse(cb, (struct nf_nat_range2 *)cb->data, 2);
-}
-
-static void _DNAT_fcheck(struct xt_fcheck_call *cb, unsigned int *flags)
-{
-	static const unsigned int f = F_TO_DEST | F_RANDOM;
-
-	if ((cb->xflags & f) == f)
-		*flags |= NF_NAT_RANGE_PROTO_RANDOM;
-}
-
-static void DNAT_fcheck(struct xt_fcheck_call *cb)
-{
-	_DNAT_fcheck(cb, &((struct nf_nat_range *)cb->data)->flags);
-}
-
-static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
-{
-	_DNAT_fcheck(cb, &((struct nf_nat_range2 *)cb->data)->flags);
-}
-
-static void print_range(const struct nf_nat_range2 *range, int rev)
-{
-	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
-		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
-			printf("[");
-		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
-		if (memcmp(&range->min_addr, &range->max_addr,
-			   sizeof(range->min_addr)))
-			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
-		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
-			printf("]");
-	}
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(":");
-		printf("%hu", ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			printf("-%hu", ntohs(range->max_proto.tcp.port));
-		if (rev >= 2 && (range->flags & NF_NAT_RANGE_PROTO_OFFSET))
-			printf("/%hu", ntohs(range->base_proto.tcp.port));
-	}
-}
-
-static void _DNAT_print(const struct nf_nat_range2 *range, int rev)
-{
-	printf(" to:");
-	print_range(range, rev);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" random");
-	if (range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" persistent");
-}
-
-static void DNAT_print(const void *ip, const struct xt_entry_target *target,
-                       int numeric)
-{
-	const struct nf_nat_range *range_v1 = (const void *)target->data;
-	struct nf_nat_range2 range = {};
-
-	memcpy(&range, range_v1, sizeof(*range_v1));
-	_DNAT_print(&range, 1);
-}
-
-static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target,
-                          int numeric)
-{
-	_DNAT_print((const struct nf_nat_range2 *)target->data, 2);
-}
-
-static void _DNAT_save(const struct nf_nat_range2 *range, int rev)
-{
-	printf(" --to-destination ");
-	print_range(range, rev);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-		printf(" --random");
-	if (range->flags & NF_NAT_RANGE_PERSISTENT)
-		printf(" --persistent");
-}
-
-static void DNAT_save(const void *ip, const struct xt_entry_target *target)
-{
-	const struct nf_nat_range *range_v1 = (const void *)target->data;
-	struct nf_nat_range2 range = {};
-
-	memcpy(&range, range_v1, sizeof(*range_v1));
-	_DNAT_save(&range, 1);
-}
-
-static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
-{
-	_DNAT_save((const struct nf_nat_range2 *)target->data, 2);
-}
-
-static void print_range_xlate(const struct nf_nat_range2 *range,
-			      struct xt_xlate *xl, int rev)
-{
-	bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
-
-	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
-		xt_xlate_add(xl, "%s%s%s",
-			     proto_specified ? "[" : "",
-			     xtables_ip6addr_to_numeric(&range->min_addr.in6),
-			     proto_specified ? "]" : "");
-
-		if (memcmp(&range->min_addr, &range->max_addr,
-			   sizeof(range->min_addr))) {
-			xt_xlate_add(xl, "-%s%s%s",
-				     proto_specified ? "[" : "",
-				     xtables_ip6addr_to_numeric(&range->max_addr.in6),
-				     proto_specified ? "]" : "");
-		}
-	}
-	if (proto_specified) {
-		xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
-
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			xt_xlate_add(xl, "-%hu",
-				   ntohs(range->max_proto.tcp.port));
-	}
-}
-
-static int _DNAT_xlate(struct xt_xlate *xl,
-		      const struct nf_nat_range2 *range, int rev)
-{
-	bool sep_need = false;
-	const char *sep = " ";
-
-	xt_xlate_add(xl, "dnat to ");
-	print_range_xlate(range, xl, rev);
-	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
-		xt_xlate_add(xl, " random");
-		sep_need = true;
-	}
-	if (range->flags & NF_NAT_RANGE_PERSISTENT) {
-		if (sep_need)
-			sep = ",";
-		xt_xlate_add(xl, "%spersistent", sep);
-	}
-
-	return 1;
-}
-
-static int DNAT_xlate(struct xt_xlate *xl,
-		      const struct xt_xlate_tg_params *params)
-{
-	const struct nf_nat_range *range_v1 = (const void *)params->target->data;
-	struct nf_nat_range2 range = {};
-
-	memcpy(&range, range_v1, sizeof(*range_v1));
-	_DNAT_xlate(xl, &range, 1);
-
-	return 1;
-}
-
-static int DNAT_xlate_v2(struct xt_xlate *xl,
-		      const struct xt_xlate_tg_params *params)
-{
-	_DNAT_xlate(xl, (const struct nf_nat_range2 *)params->target->data, 2);
-
-	return 1;
-}
-
-static struct xtables_target dnat_tg_reg[] = {
-	{
-		.name		= "DNAT",
-		.version	= XTABLES_VERSION,
-		.family		= NFPROTO_IPV6,
-		.revision	= 1,
-		.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
-		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
-		.help		= DNAT_help,
-		.print		= DNAT_print,
-		.save		= DNAT_save,
-		.x6_parse	= DNAT_parse,
-		.x6_fcheck	= DNAT_fcheck,
-		.x6_options	= DNAT_opts,
-		.xlate		= DNAT_xlate,
-	},
-	{
-		.name		= "DNAT",
-		.version	= XTABLES_VERSION,
-		.family		= NFPROTO_IPV6,
-		.revision	= 2,
-		.size		= XT_ALIGN(sizeof(struct nf_nat_range2)),
-		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range2)),
-		.help		= DNAT_help_v2,
-		.print		= DNAT_print_v2,
-		.save		= DNAT_save_v2,
-		.x6_parse	= DNAT_parse_v2,
-		.x6_fcheck	= DNAT_fcheck_v2,
-		.x6_options	= DNAT_opts,
-		.xlate		= DNAT_xlate_v2,
-	},
-};
-
-void _init(void)
-{
-	xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg));
-}
diff --git a/extensions/libip6t_DNAT.txlate b/extensions/libip6t_DNAT.txlate
deleted file mode 100644
index 03c4caf7e87c4..0000000000000
--- a/extensions/libip6t_DNAT.txlate
+++ /dev/null
@@ -1,11 +0,0 @@
-ip6tables-translate -t nat -A prerouting -i eth1 -p tcp --dport 8080 -j DNAT --to-destination [fec0::1234]:80
-nft add rule ip6 nat prerouting iifname "eth1" tcp dport 8080 counter dnat to [fec0::1234]:80
-
-ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:1-20
-nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:1-20
-
-ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:80 --persistent
-nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:80 persistent
-
-ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:80 --random --persistent
-nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:80 random,persistent
diff --git a/extensions/libipt_DNAT.txlate b/extensions/libipt_DNAT.txlate
deleted file mode 100644
index e88314d9dba59..0000000000000
--- a/extensions/libipt_DNAT.txlate
+++ /dev/null
@@ -1,14 +0,0 @@
-iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4
-nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4
-
-iptables-translate -t nat -A prerouting -p tcp -d 15.45.23.67 --dport 80 -j DNAT --to-destination 192.168.1.1-192.168.1.10
-nft add rule ip nat prerouting ip daddr 15.45.23.67 tcp dport 80 counter dnat to 192.168.1.1-192.168.1.10
-
-iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4:1-1023
-nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4:1-1023
-
-iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4 --random
-nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4 random
-
-iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4 --random --persistent
-nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4 random,persistent
diff --git a/extensions/libxt_DNAT.c b/extensions/libxt_DNAT.c
index 9a179919f522d..7f7c322cb2f9b 100644
--- a/extensions/libxt_DNAT.c
+++ b/extensions/libxt_DNAT.c
@@ -1,3 +1,10 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Rusty Russell's IPv4 DNAT target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
 #include <stdio.h>
 #include <netdb.h>
 #include <string.h>
@@ -7,6 +14,7 @@
 #include <limits.h> /* INT_MAX in ip_tables.h */
 #include <arpa/inet.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
 #include <linux/netfilter/nf_nat.h>
 
 #define TO_IPV4_MRC(ptr) ((const struct nf_nat_ipv4_multi_range_compat *)(ptr))
@@ -119,18 +127,36 @@ parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
 
 /* Ranges expected in network order. */
 static void
-parse_to(const char *orig_arg, bool portok, struct nf_nat_range2 *range)
+parse_to(const char *orig_arg, bool portok,
+	 struct nf_nat_range2 *range, int family)
 {
-	char *arg, *colon, *dash;
+	char *arg, *start, *end, *colon, *dash;
 
 	arg = xtables_strdup(orig_arg);
-	colon = strchr(arg, ':');
+	start = strchr(arg, '[');
+	if (!start) {
+		start = arg;
+		/* Lets assume one colon is port information.
+		 * Otherwise its an IPv6 address */
+		colon = strchr(arg, ':');
+		if (colon && strchr(colon + 1, ':'))
+			colon = NULL;
+	} else {
+		start++;
+		end = strchr(start, ']');
+		if (end == NULL || family == AF_INET)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Invalid address format");
+
+		*end = '\0';
+		colon = strchr(end + 1, ':');
+	}
 
 	if (colon) {
 		parse_ports(colon + 1, portok, range);
 
-		/* Starts with a colon? No IP info...*/
-		if (colon == arg) {
+		/* Starts with colon or [] colon? No IP info...*/
+		if (colon == arg || colon == arg + 2) {
 			free(arg);
 			return;
 		}
@@ -138,20 +164,20 @@ parse_to(const char *orig_arg, bool portok, struct nf_nat_range2 *range)
 	}
 
 	range->flags |= NF_NAT_RANGE_MAP_IPS;
-	dash = strchr(arg, '-');
+	dash = strchr(start, '-');
 	if (colon && dash && dash > colon)
 		dash = NULL;
 
 	if (dash)
 		*dash = '\0';
 
-	if (!inet_pton(AF_INET, arg, &range->min_addr))
+	if (!inet_pton(family, start, &range->min_addr))
 		xtables_error(PARAMETER_PROBLEM,
-			      "Bad IP address \"%s\"\n", arg);
+			      "Bad IP address \"%s\"", arg);
 	if (dash) {
-		if (!inet_pton(AF_INET, dash + 1, &range->max_addr))
+		if (!inet_pton(family, dash + 1, &range->max_addr))
 			xtables_error(PARAMETER_PROBLEM,
-				      "Bad IP address \"%s\"\n", dash + 1);
+				      "Bad IP address \"%s\"", dash + 1);
 	} else {
 		range->max_addr = range->min_addr;
 	}
@@ -160,7 +186,7 @@ parse_to(const char *orig_arg, bool portok, struct nf_nat_range2 *range)
 }
 
 static void __DNAT_parse(struct xt_option_call *cb, __u16 proto,
-			 struct nf_nat_range2 *range)
+			 struct nf_nat_range2 *range, int family)
 {
 	bool portok = proto == IPPROTO_TCP ||
 		      proto == IPPROTO_UDP ||
@@ -171,7 +197,7 @@ static void __DNAT_parse(struct xt_option_call *cb, __u16 proto,
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_TO_DEST:
-		parse_to(cb->arg, portok, range);
+		parse_to(cb->arg, portok, range, family);
 		break;
 	case O_PERSISTENT:
 		range->flags |= NF_NAT_RANGE_PERSISTENT;
@@ -185,7 +211,7 @@ static void DNAT_parse(struct xt_option_call *cb)
 	const struct ipt_entry *entry = cb->xt_entry;
 	struct nf_nat_range2 range = {};
 
-	__DNAT_parse(cb, entry->ip.proto, &range);
+	__DNAT_parse(cb, entry->ip.proto, &range, AF_INET);
 
 	switch (cb->entry->id) {
 	case O_TO_DEST:
@@ -200,32 +226,47 @@ static void DNAT_parse(struct xt_option_call *cb)
 	}
 }
 
-static void DNAT_fcheck(struct xt_fcheck_call *cb)
+static void __DNAT_fcheck(struct xt_fcheck_call *cb, unsigned int *flags)
 {
 	static const unsigned int f = F_TO_DEST | F_RANDOM;
-	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
 
 	if ((cb->xflags & f) == f)
-		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+		*flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
+static void DNAT_fcheck(struct xt_fcheck_call *cb)
+{
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
 
 	mr->rangesize = 1;
 
 	if (mr->range[0].flags & NF_NAT_RANGE_PROTO_OFFSET)
 		xtables_error(PARAMETER_PROBLEM,
 			      "Shifted portmap ranges not supported with this kernel");
+
+	__DNAT_fcheck(cb, &mr->range[0].flags);
 }
 
-static char *sprint_range(const struct nf_nat_range2 *r)
+static char *sprint_range(const struct nf_nat_range2 *r, int family)
 {
-	static char buf[INET_ADDRSTRLEN * 2 + 1 + 6 * 3];
+	bool brackets = family == AF_INET6 &&
+			r->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
+	static char buf[INET6_ADDRSTRLEN * 2 + 3 + 6 * 3];
+
+	buf[0] = '\0';
 
 	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
-		sprintf(buf, "%s", xtables_ipaddr_to_numeric(&r->min_addr.in));
-		if (memcmp(&r->min_addr, &r->max_addr, sizeof(r->min_addr)))
-			sprintf(buf + strlen(buf), "-%s",
-				xtables_ipaddr_to_numeric(&r->max_addr.in));
-	} else {
-		buf[0] = '\0';
+		if (brackets)
+			strcat(buf, "[");
+		inet_ntop(family, &r->min_addr,
+			  buf + strlen(buf), INET6_ADDRSTRLEN);
+		if (memcmp(&r->min_addr, &r->max_addr, sizeof(r->min_addr))) {
+			strcat(buf, "-");
+			inet_ntop(family, &r->max_addr,
+				  buf + strlen(buf), INET6_ADDRSTRLEN);
+		}
+		if (brackets)
+			strcat(buf, "]");
 	}
 	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		sprintf(buf + strlen(buf), ":%hu",
@@ -240,11 +281,12 @@ static char *sprint_range(const struct nf_nat_range2 *r)
 	return buf;
 }
 
-static void __DNAT_print(const struct nf_nat_range2 *r, bool save)
+static void __DNAT_print(const struct nf_nat_range2 *r, bool save, int family)
 {
 	const char *dashdash = save ? "--" : "";
 
-	printf(" %s%s", save ? "--to-destination " : "to:", sprint_range(r));
+	printf(" %s%s", save ? "--to-destination " : "to:",
+	       sprint_range(r, family));
 	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
 		printf(" %srandom", dashdash);
 	if (r->flags & NF_NAT_RANGE_PERSISTENT)
@@ -256,19 +298,20 @@ static void DNAT_print(const void *ip, const struct xt_entry_target *target,
 {
 	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
 
-	__DNAT_print(&range, false);
+	__DNAT_print(&range, false, AF_INET);
 }
 
 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
 {
 	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
 
-	__DNAT_print(&range, true);
+	__DNAT_print(&range, true, AF_INET);
 }
 
-static int __DNAT_xlate(struct xt_xlate *xl, const struct nf_nat_range2 *r)
+static int
+__DNAT_xlate(struct xt_xlate *xl, const struct nf_nat_range2 *r, int family)
 {
-	char *range_str = sprint_range(r);
+	char *range_str = sprint_range(r, family);
 	const char *sep = " ";
 
 	/* shifted portmap ranges are not supported by nftables */
@@ -295,40 +338,109 @@ static int DNAT_xlate(struct xt_xlate *xl,
 	struct nf_nat_range2 range =
 		RANGE2_INIT_FROM_IPV4_MRC(params->target->data);
 
-	return __DNAT_xlate(xl, &range);
+	return __DNAT_xlate(xl, &range, AF_INET);
 }
 
 static void DNAT_parse_v2(struct xt_option_call *cb)
 {
 	const struct ipt_entry *entry = cb->xt_entry;
 
-	__DNAT_parse(cb, entry->ip.proto, cb->data);
+	__DNAT_parse(cb, entry->ip.proto, cb->data, AF_INET);
 }
 
 static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
 {
-	static const unsigned int f = F_TO_DEST | F_RANDOM;
-	struct nf_nat_range2 *range = cb->data;
-
-	if ((cb->xflags & f) == f)
-		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+	__DNAT_fcheck(cb, &((struct nf_nat_range2 *)cb->data)->flags);
 }
 
 static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target,
                        int numeric)
 {
-	__DNAT_print((const void *)target->data, false);
+	__DNAT_print((const void *)target->data, false, AF_INET);
 }
 
 static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
 {
-	__DNAT_print((const void *)target->data, true);
+	__DNAT_print((const void *)target->data, true, AF_INET);
 }
 
 static int DNAT_xlate_v2(struct xt_xlate *xl,
-		      const struct xt_xlate_tg_params *params)
+			  const struct xt_xlate_tg_params *params)
+{
+	return __DNAT_xlate(xl, (const void *)params->target->data, AF_INET);
+}
+
+static void DNAT_parse6(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	struct nf_nat_range *range_v1 = (void *)cb->data;
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, range_v1, sizeof(*range_v1));
+	__DNAT_parse(cb, entry->ipv6.proto, &range, AF_INET6);
+	memcpy(range_v1, &range, sizeof(*range_v1));
+}
+
+static void DNAT_fcheck6(struct xt_fcheck_call *cb)
+{
+	struct nf_nat_range *range = (void *)cb->data;
+
+	if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Shifted portmap ranges not supported with this kernel");
+
+	__DNAT_fcheck(cb, &range->flags);
+}
+
+static void DNAT_print6(const void *ip, const struct xt_entry_target *target,
+			int numeric)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)target->data, sizeof(struct nf_nat_range));
+	__DNAT_print(&range, true, AF_INET6);
+}
+
+static void DNAT_save6(const void *ip, const struct xt_entry_target *target)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)target->data, sizeof(struct nf_nat_range));
+	__DNAT_print(&range, true, AF_INET6);
+}
+
+static int DNAT_xlate6(struct xt_xlate *xl,
+		       const struct xt_xlate_tg_params *params)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)params->target->data,
+	       sizeof(struct nf_nat_range));
+	return __DNAT_xlate(xl, &range, AF_INET6);
+}
+
+static void DNAT_parse6_v2(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+
+	__DNAT_parse(cb, entry->ipv6.proto, cb->data, AF_INET6);
+}
+
+static void DNAT_print6_v2(const void *ip, const struct xt_entry_target *target,
+			   int numeric)
+{
+	__DNAT_print((const void *)target->data, true, AF_INET6);
+}
+
+static void DNAT_save6_v2(const void *ip, const struct xt_entry_target *target)
 {
-	return __DNAT_xlate(xl, (const void *)params->target->data);
+	__DNAT_print((const void *)target->data, true, AF_INET6);
+}
+
+static int DNAT_xlate6_v2(struct xt_xlate *xl,
+			  const struct xt_xlate_tg_params *params)
+{
+	return __DNAT_xlate(xl, (const void *)params->target->data, AF_INET6);
 }
 
 static struct xtables_target dnat_tg_reg[] = {
@@ -347,6 +459,21 @@ static struct xtables_target dnat_tg_reg[] = {
 		.x6_options	= DNAT_opts,
 		.xlate		= DNAT_xlate,
 	},
+	{
+		.name		= "DNAT",
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV6,
+		.revision	= 1,
+		.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+		.help		= DNAT_help,
+		.print		= DNAT_print6,
+		.save		= DNAT_save6,
+		.x6_parse	= DNAT_parse6,
+		.x6_fcheck	= DNAT_fcheck6,
+		.x6_options	= DNAT_opts,
+		.xlate		= DNAT_xlate6,
+	},
 	{
 		.name		= "DNAT",
 		.version	= XTABLES_VERSION,
@@ -362,6 +489,21 @@ static struct xtables_target dnat_tg_reg[] = {
 		.x6_options	= DNAT_opts,
 		.xlate		= DNAT_xlate_v2,
 	},
+	{
+		.name		= "DNAT",
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV6,
+		.revision	= 2,
+		.size		= XT_ALIGN(sizeof(struct nf_nat_range2)),
+		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range2)),
+		.help		= DNAT_help_v2,
+		.print		= DNAT_print6_v2,
+		.save		= DNAT_save6_v2,
+		.x6_parse	= DNAT_parse6_v2,
+		.x6_fcheck	= DNAT_fcheck_v2,
+		.x6_options	= DNAT_opts,
+		.xlate		= DNAT_xlate6_v2,
+	},
 };
 
 void _init(void)
diff --git a/extensions/libxt_DNAT.txlate b/extensions/libxt_DNAT.txlate
new file mode 100644
index 0000000000000..a65976562ef53
--- /dev/null
+++ b/extensions/libxt_DNAT.txlate
@@ -0,0 +1,35 @@
+iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4
+nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4
+
+iptables-translate -t nat -A prerouting -p tcp -d 15.45.23.67 --dport 80 -j DNAT --to-destination 192.168.1.1-192.168.1.10
+nft add rule ip nat prerouting ip daddr 15.45.23.67 tcp dport 80 counter dnat to 192.168.1.1-192.168.1.10
+
+iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4:1-1023
+nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4:1-1023
+
+iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4 --random
+nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4 random
+
+iptables-translate -t nat -A prerouting -p tcp -o eth0 -j DNAT --to-destination 1.2.3.4 --random --persistent
+nft add rule ip nat prerouting oifname "eth0" ip protocol tcp counter dnat to 1.2.3.4 random,persistent
+
+ip6tables-translate -t nat -A prerouting -p tcp --dport 8080 -j DNAT --to-destination fec0::1234
+nft add rule ip6 nat prerouting tcp dport 8080 counter dnat to fec0::1234
+
+ip6tables-translate -t nat -A prerouting -p tcp --dport 8080 -j DNAT --to-destination fec0::1234-fec0::2000
+nft add rule ip6 nat prerouting tcp dport 8080 counter dnat to fec0::1234-fec0::2000
+
+ip6tables-translate -t nat -A prerouting -i eth1 -p tcp --dport 8080 -j DNAT --to-destination [fec0::1234]:80
+nft add rule ip6 nat prerouting iifname "eth1" tcp dport 8080 counter dnat to [fec0::1234]:80
+
+ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:1-20
+nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:1-20
+
+ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234-fec0::2000]:1-20
+nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234-fec0::2000]:1-20
+
+ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:80 --persistent
+nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:80 persistent
+
+ip6tables-translate -t nat -A prerouting -p tcp -j DNAT --to-destination [fec0::1234]:80 --random --persistent
+nft add rule ip6 nat prerouting meta l4proto tcp counter dnat to [fec0::1234]:80 random,persistent
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 8/9] extensions: Merge REDIRECT into DNAT
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (6 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 7/9] extensions: Merge IPv4 and IPv6 DNAT targets Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-03-31 10:12 ` [iptables PATCH v2 9/9] extensions: man: Document service name support in DNAT and REDIRECT Phil Sutter
  2022-04-08 15:51 ` [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Pablo Neira Ayuso
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Code is very similar, join them to reuse parsing code at least.

As a side-effect, this enables parsing of service names for ports in
DNAT as well as using port number 0 as that's what REDIRECT allows.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v1:
- Fix for segfault when using service name as port due to dereference of
  uninitialized 'end' variable.
- Force numeric ports in ranges' upper boundary, this kills support for
  names in ranges.
- Add more test cases.
---
 extensions/GNUmakefile.in          |   4 +-
 extensions/libip6t_DNAT.t          |   4 +
 extensions/libip6t_REDIRECT.c      | 170 ----------------------------
 extensions/libip6t_REDIRECT.t      |   6 -
 extensions/libip6t_REDIRECT.txlate |   5 -
 extensions/libipt_DNAT.t           |   4 +
 extensions/libipt_REDIRECT.c       | 174 -----------------------------
 extensions/libipt_REDIRECT.t       |   6 -
 extensions/libipt_REDIRECT.txlate  |   5 -
 extensions/libxt_DNAT.c            | 164 ++++++++++++++++++++++++---
 extensions/libxt_REDIRECT.t        |  16 +++
 extensions/libxt_REDIRECT.txlate   |  26 +++++
 12 files changed, 204 insertions(+), 380 deletions(-)
 delete mode 100644 extensions/libip6t_REDIRECT.c
 delete mode 100644 extensions/libip6t_REDIRECT.t
 delete mode 100644 extensions/libip6t_REDIRECT.txlate
 delete mode 100644 extensions/libipt_REDIRECT.c
 delete mode 100644 extensions/libipt_REDIRECT.t
 delete mode 100644 extensions/libipt_REDIRECT.txlate
 create mode 100644 extensions/libxt_REDIRECT.t
 create mode 100644 extensions/libxt_REDIRECT.txlate

diff --git a/extensions/GNUmakefile.in b/extensions/GNUmakefile.in
index 956ccb38b2ab9..6dad4e02481bd 100644
--- a/extensions/GNUmakefile.in
+++ b/extensions/GNUmakefile.in
@@ -42,7 +42,7 @@ endif
 pfx_build_mod := $(patsubst ${srcdir}/libxt_%.c,%,$(sort $(wildcard ${srcdir}/libxt_*.c)))
 @ENABLE_NFTABLES_TRUE@ pfb_build_mod := $(patsubst ${srcdir}/libebt_%.c,%,$(sort $(wildcard ${srcdir}/libebt_*.c)))
 @ENABLE_NFTABLES_TRUE@ pfa_build_mod := $(patsubst ${srcdir}/libarpt_%.c,%,$(sort $(wildcard ${srcdir}/libarpt_*.c)))
-pfx_symlinks  := NOTRACK state
+pfx_symlinks  := NOTRACK state REDIRECT
 @ENABLE_IPV4_TRUE@ pf4_build_mod := $(patsubst ${srcdir}/libipt_%.c,%,$(sort $(wildcard ${srcdir}/libipt_*.c)))
 @ENABLE_IPV6_TRUE@ pf6_build_mod := $(patsubst ${srcdir}/libip6t_%.c,%,$(sort $(wildcard ${srcdir}/libip6t_*.c)))
 pfx_build_mod := $(filter-out @blacklist_modules@ @blacklist_x_modules@,${pfx_build_mod})
@@ -130,6 +130,8 @@ libxt_NOTRACK.so: libxt_CT.so
 	ln -fs $< $@
 libxt_state.so: libxt_conntrack.so
 	ln -fs $< $@
+libxt_REDIRECT.so: libxt_DNAT.so
+	ln -fs $< $@
 
 # Need the LIBADDs in iptables/Makefile.am too for libxtables_la_LIBADD
 xt_RATEEST_LIBADD   = -lm
diff --git a/extensions/libip6t_DNAT.t b/extensions/libip6t_DNAT.t
index ec7d61f418cfe..e53dfa16a8871 100644
--- a/extensions/libip6t_DNAT.t
+++ b/extensions/libip6t_DNAT.t
@@ -13,4 +13,8 @@
 -p tcp -j DNAT --to-destination [dead::beef-dead::fee7]:1000-2000/65535;=;OK
 -p tcp -j DNAT --to-destination [dead::beef-dead::fee7]:1000-2000/0;;FAIL
 -p tcp -j DNAT --to-destination [dead::beef-dead::fee7]:1000-2000/65536;;FAIL
+-p tcp -j DNAT --to-destination [dead::beef]:ssh;-p tcp -j DNAT --to-destination [dead::beef]:22;OK
+-p tcp -j DNAT --to-destination [dead::beef]:ftp-data;-p tcp -j DNAT --to-destination [dead::beef]:20;OK
+-p tcp -j DNAT --to-destination [dead::beef]:echo-ssh;;FAIL
+-p tcp -j DNAT --to-destination [dead::beef]:10-20/ftp;-p tcp -j DNAT --to-destination [dead::beef]:10-20/21;OK
 -j DNAT;;FAIL
diff --git a/extensions/libip6t_REDIRECT.c b/extensions/libip6t_REDIRECT.c
deleted file mode 100644
index 8e04d2cd33d50..0000000000000
--- a/extensions/libip6t_REDIRECT.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
- *
- * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 NAT
- * funded by Astaro.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <xtables.h>
-#include <limits.h> /* INT_MAX in ip_tables.h */
-#include <linux/netfilter_ipv6/ip6_tables.h>
-#include <linux/netfilter/nf_nat.h>
-
-enum {
-	O_TO_PORTS = 0,
-	O_RANDOM,
-	F_TO_PORTS = 1 << O_TO_PORTS,
-	F_RANDOM   = 1 << O_RANDOM,
-};
-
-static void REDIRECT_help(void)
-{
-	printf(
-"REDIRECT target options:\n"
-" --to-ports <port>[-<port>]\n"
-"				Port (range) to map to.\n"
-" [--random]\n");
-}
-
-static const struct xt_option_entry REDIRECT_opts[] = {
-	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
-	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
-	XTOPT_TABLEEND,
-};
-
-/* Parses ports */
-static void
-parse_ports(const char *arg, struct nf_nat_range *range)
-{
-	char *end = "";
-	unsigned int port, maxport;
-
-	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
-
-	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
-	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
-		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
-
-	switch (*end) {
-	case '\0':
-		range->min_proto.tcp.port
-			= range->max_proto.tcp.port
-			= htons(port);
-		return;
-	case '-':
-		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
-		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
-			break;
-
-		if (maxport < port)
-			break;
-
-		range->min_proto.tcp.port = htons(port);
-		range->max_proto.tcp.port = htons(maxport);
-		return;
-	default:
-		break;
-	}
-	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
-}
-
-static void REDIRECT_parse(struct xt_option_call *cb)
-{
-	const struct ip6t_entry *entry = cb->xt_entry;
-	struct nf_nat_range *range = (void *)(*cb->target)->data;
-	int portok;
-
-	if (entry->ipv6.proto == IPPROTO_TCP
-	    || entry->ipv6.proto == IPPROTO_UDP
-	    || entry->ipv6.proto == IPPROTO_SCTP
-	    || entry->ipv6.proto == IPPROTO_DCCP
-	    || entry->ipv6.proto == IPPROTO_ICMP)
-		portok = 1;
-	else
-		portok = 0;
-
-	xtables_option_parse(cb);
-	switch (cb->entry->id) {
-	case O_TO_PORTS:
-		if (!portok)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Need TCP, UDP, SCTP or DCCP with port specification");
-		parse_ports(cb->arg, range);
-		if (cb->xflags & F_RANDOM)
-			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
-		break;
-	case O_RANDOM:
-		if (cb->xflags & F_TO_PORTS)
-			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
-		break;
-	}
-}
-
-static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
-                           int numeric)
-{
-	const struct nf_nat_range *range = (const void *)target->data;
-
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(" redir ports ");
-		printf("%hu", ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			printf("-%hu", ntohs(range->max_proto.tcp.port));
-		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-			printf(" random");
-	}
-}
-
-static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
-{
-	const struct nf_nat_range *range = (const void *)target->data;
-
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(" --to-ports ");
-		printf("%hu", ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			printf("-%hu", ntohs(range->max_proto.tcp.port));
-		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-			printf(" --random");
-	}
-}
-
-static int REDIRECT_xlate(struct xt_xlate *xl,
-			  const struct xt_xlate_tg_params *params)
-{
-	const struct nf_nat_range *range = (const void *)params->target->data;
-
-	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		xt_xlate_add(xl, "redirect to :%hu",
-			   ntohs(range->min_proto.tcp.port));
-		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
-			xt_xlate_add(xl, "-%hu ",
-				   ntohs(range->max_proto.tcp.port));
-		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
-			xt_xlate_add(xl, " random ");
-	}
-
-	return 1;
-}
-
-static struct xtables_target redirect_tg_reg = {
-	.name		= "REDIRECT",
-	.version	= XTABLES_VERSION,
-	.family		= NFPROTO_IPV6,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
-	.help		= REDIRECT_help,
-	.x6_parse	= REDIRECT_parse,
-	.print		= REDIRECT_print,
-	.save		= REDIRECT_save,
-	.x6_options	= REDIRECT_opts,
-	.xlate		= REDIRECT_xlate,
-};
-
-void _init(void)
-{
-	xtables_register_target(&redirect_tg_reg);
-}
diff --git a/extensions/libip6t_REDIRECT.t b/extensions/libip6t_REDIRECT.t
deleted file mode 100644
index a0fb0ed19a5ea..0000000000000
--- a/extensions/libip6t_REDIRECT.t
+++ /dev/null
@@ -1,6 +0,0 @@
-:PREROUTING,OUTPUT
-*nat
--p tcp -j REDIRECT --to-ports 42;=;OK
--p udp -j REDIRECT --to-ports 42-1234;=;OK
--p tcp -j REDIRECT --to-ports 42-1234 --random;=;OK
--j REDIRECT --to-ports 42;;FAIL
diff --git a/extensions/libip6t_REDIRECT.txlate b/extensions/libip6t_REDIRECT.txlate
deleted file mode 100644
index 209f67a4235f9..0000000000000
--- a/extensions/libip6t_REDIRECT.txlate
+++ /dev/null
@@ -1,5 +0,0 @@
-ip6tables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080
-nft add rule ip6 nat prerouting tcp dport 80 counter redirect to :8080
-
-ip6tables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080 --random
-nft add rule ip6 nat prerouting tcp dport 80 counter redirect to :8080 random
diff --git a/extensions/libipt_DNAT.t b/extensions/libipt_DNAT.t
index 1c4413b9b3bb5..9007572ae32d3 100644
--- a/extensions/libipt_DNAT.t
+++ b/extensions/libipt_DNAT.t
@@ -13,4 +13,8 @@
 -p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65535;=;OK
 -p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/0;;FAIL
 -p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65536;;FAIL
+-p tcp -j DNAT --to-destination 1.1.1.1:ssh;-p tcp -j DNAT --to-destination 1.1.1.1:22;OK
+-p tcp -j DNAT --to-destination 1.1.1.1:ftp-data;-p tcp -j DNAT --to-destination 1.1.1.1:20;OK
+-p tcp -j DNAT --to-destination 1.1.1.1:echo-ssh;;FAIL
+-p tcp -j DNAT --to-destination 1.1.1.1:10-20/ftp;-p tcp -j DNAT --to-destination 1.1.1.1:10-20/21;OK
 -j DNAT;;FAIL
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
deleted file mode 100644
index 7850306f5fe25..0000000000000
--- a/extensions/libipt_REDIRECT.c
+++ /dev/null
@@ -1,174 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <xtables.h>
-#include <limits.h> /* INT_MAX in ip_tables.h */
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter/nf_nat.h>
-
-enum {
-	O_TO_PORTS = 0,
-	O_RANDOM,
-	F_TO_PORTS = 1 << O_TO_PORTS,
-	F_RANDOM   = 1 << O_RANDOM,
-};
-
-static void REDIRECT_help(void)
-{
-	printf(
-"REDIRECT target options:\n"
-" --to-ports <port>[-<port>]\n"
-"				Port (range) to map to.\n"
-" [--random]\n");
-}
-
-static const struct xt_option_entry REDIRECT_opts[] = {
-	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
-	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
-	XTOPT_TABLEEND,
-};
-
-static void REDIRECT_init(struct xt_entry_target *t)
-{
-	struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
-
-	/* Actually, it's 0, but it's ignored at the moment. */
-	mr->rangesize = 1;
-}
-
-/* Parses ports */
-static void
-parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
-{
-	char *end = "";
-	unsigned int port, maxport;
-
-	mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
-
-	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
-	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
-		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
-
-	switch (*end) {
-	case '\0':
-		mr->range[0].min.tcp.port
-			= mr->range[0].max.tcp.port
-			= htons(port);
-		return;
-	case '-':
-		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
-		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
-			break;
-
-		if (maxport < port)
-			break;
-
-		mr->range[0].min.tcp.port = htons(port);
-		mr->range[0].max.tcp.port = htons(maxport);
-		return;
-	default:
-		break;
-	}
-	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
-}
-
-static void REDIRECT_parse(struct xt_option_call *cb)
-{
-	const struct ipt_entry *entry = cb->xt_entry;
-	struct nf_nat_ipv4_multi_range_compat *mr = (void *)(*cb->target)->data;
-	int portok;
-
-	if (entry->ip.proto == IPPROTO_TCP
-	    || entry->ip.proto == IPPROTO_UDP
-	    || entry->ip.proto == IPPROTO_SCTP
-	    || entry->ip.proto == IPPROTO_DCCP
-	    || entry->ip.proto == IPPROTO_ICMP)
-		portok = 1;
-	else
-		portok = 0;
-
-	xtables_option_parse(cb);
-	switch (cb->entry->id) {
-	case O_TO_PORTS:
-		if (!portok)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Need TCP, UDP, SCTP or DCCP with port specification");
-		parse_ports(cb->arg, mr);
-		if (cb->xflags & F_RANDOM)
-			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
-		break;
-	case O_RANDOM:
-		if (cb->xflags & F_TO_PORTS)
-			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
-		break;
-	}
-}
-
-static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
-                           int numeric)
-{
-	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
-	const struct nf_nat_ipv4_range *r = &mr->range[0];
-
-	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(" redir ports ");
-		printf("%hu", ntohs(r->min.tcp.port));
-		if (r->max.tcp.port != r->min.tcp.port)
-			printf("-%hu", ntohs(r->max.tcp.port));
-		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
-			printf(" random");
-	}
-}
-
-static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
-{
-	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
-	const struct nf_nat_ipv4_range *r = &mr->range[0];
-
-	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		printf(" --to-ports ");
-		printf("%hu", ntohs(r->min.tcp.port));
-		if (r->max.tcp.port != r->min.tcp.port)
-			printf("-%hu", ntohs(r->max.tcp.port));
-		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
-			printf(" --random");
-	}
-}
-
-static int REDIRECT_xlate(struct xt_xlate *xl,
-			  const struct xt_xlate_tg_params *params)
-{
-	const struct nf_nat_ipv4_multi_range_compat *mr =
-		(const void *)params->target->data;
-	const struct nf_nat_ipv4_range *r = &mr->range[0];
-
-	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
-		xt_xlate_add(xl, "redirect to :%hu", ntohs(r->min.tcp.port));
-		if (r->max.tcp.port != r->min.tcp.port)
-			xt_xlate_add(xl, "-%hu ", ntohs(r->max.tcp.port));
-		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
-			xt_xlate_add(xl, " random ");
-	}
-
-	return 1;
-}
-
-static struct xtables_target redirect_tg_reg = {
-	.name		= "REDIRECT",
-	.version	= XTABLES_VERSION,
-	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
-	.help		= REDIRECT_help,
-	.init		= REDIRECT_init,
- 	.x6_parse	= REDIRECT_parse,
-	.print		= REDIRECT_print,
-	.save		= REDIRECT_save,
-	.x6_options	= REDIRECT_opts,
-	.xlate		= REDIRECT_xlate,
-};
-
-void _init(void)
-{
-	xtables_register_target(&redirect_tg_reg);
-}
diff --git a/extensions/libipt_REDIRECT.t b/extensions/libipt_REDIRECT.t
deleted file mode 100644
index a0fb0ed19a5ea..0000000000000
--- a/extensions/libipt_REDIRECT.t
+++ /dev/null
@@ -1,6 +0,0 @@
-:PREROUTING,OUTPUT
-*nat
--p tcp -j REDIRECT --to-ports 42;=;OK
--p udp -j REDIRECT --to-ports 42-1234;=;OK
--p tcp -j REDIRECT --to-ports 42-1234 --random;=;OK
--j REDIRECT --to-ports 42;;FAIL
diff --git a/extensions/libipt_REDIRECT.txlate b/extensions/libipt_REDIRECT.txlate
deleted file mode 100644
index 815bb7714138d..0000000000000
--- a/extensions/libipt_REDIRECT.txlate
+++ /dev/null
@@ -1,5 +0,0 @@
-iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080
-nft add rule ip nat prerouting tcp dport 80 counter redirect to :8080
-
-iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080 --random
-nft add rule ip nat prerouting tcp dport 80 counter redirect to :8080 random
diff --git a/extensions/libxt_DNAT.c b/extensions/libxt_DNAT.c
index 7f7c322cb2f9b..5ac8018c12423 100644
--- a/extensions/libxt_DNAT.c
+++ b/extensions/libxt_DNAT.c
@@ -28,10 +28,12 @@
 
 enum {
 	O_TO_DEST = 0,
+	O_TO_PORTS,
 	O_RANDOM,
 	O_PERSISTENT,
-	F_TO_DEST = 1 << O_TO_DEST,
-	F_RANDOM  = 1 << O_RANDOM,
+	F_TO_DEST  = 1 << O_TO_DEST,
+	F_TO_PORTS = 1 << O_TO_PORTS,
+	F_RANDOM   = 1 << O_RANDOM,
 };
 
 static void DNAT_help(void)
@@ -52,6 +54,15 @@ static void DNAT_help_v2(void)
 "[--random] [--persistent]\n");
 }
 
+static void REDIRECT_help(void)
+{
+	printf(
+"REDIRECT target options:\n"
+" --to-ports <port>[-<port>]\n"
+"				Port (range) to map to.\n"
+" [--random]\n");
+}
+
 static const struct xt_option_entry DNAT_opts[] = {
 	{.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
 	 .flags = XTOPT_MAND},
@@ -60,6 +71,12 @@ static const struct xt_option_entry DNAT_opts[] = {
 	XTOPT_TABLEEND,
 };
 
+static const struct xt_option_entry REDIRECT_opts[] = {
+	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
 /* Parses ports */
 static void
 parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
@@ -73,9 +90,13 @@ parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
 
 	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 
-	if (!xtables_strtoui(arg, &end, &port, 1, UINT16_MAX))
-		xtables_error(PARAMETER_PROBLEM,
-			      "Port `%s' not valid", arg);
+	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX)) {
+		port = xtables_service_to_port(arg, NULL);
+		if (port == (unsigned)-1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Port `%s' not valid", arg);
+		end = "";
+	}
 
 	switch (*end) {
 	case '\0':
@@ -94,9 +115,9 @@ parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
 			      "Garbage after port value: `%s'", end);
 	}
 
-	if (!xtables_strtoui(arg, &end, &maxport, 1, UINT16_MAX))
-		xtables_error(PARAMETER_PROBLEM,
-			      "Port `%s' not valid", arg);
+	/* it is a range, don't allow service names here */
+	if (!xtables_strtoui(arg, &end, &maxport, 0, UINT16_MAX))
+		xtables_error(PARAMETER_PROBLEM, "Port `%s' not valid", arg);
 
 	if (maxport < port)
 		/* People are stupid. */
@@ -117,9 +138,12 @@ parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
 			      "Garbage after port range: `%s'", end);
 	}
 
-	if (!xtables_strtoui(arg, &end, &baseport, 1, UINT16_MAX))
-		xtables_error(PARAMETER_PROBLEM,
-			      "Port `%s' not valid", arg);
+	if (!xtables_strtoui(arg, &end, &baseport, 1, UINT16_MAX)) {
+		baseport = xtables_service_to_port(arg, NULL);
+		if (baseport == (unsigned)-1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Port `%s' not valid", arg);
+	}
 
 	range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
 	range->base_proto.tcp.port = htons(baseport);
@@ -199,6 +223,9 @@ static void __DNAT_parse(struct xt_option_call *cb, __u16 proto,
 	case O_TO_DEST:
 		parse_to(cb->arg, portok, range, family);
 		break;
+	case O_TO_PORTS:
+		parse_ports(cb->arg, portok, range);
+		break;
 	case O_PERSISTENT:
 		range->flags |= NF_NAT_RANGE_PERSISTENT;
 		break;
@@ -217,6 +244,8 @@ static void DNAT_parse(struct xt_option_call *cb)
 	case O_TO_DEST:
 		mr->range->min_ip = range.min_addr.ip;
 		mr->range->max_ip = range.max_addr.ip;
+		/* fall through */
+	case O_TO_PORTS:
 		mr->range->min = range.min_proto;
 		mr->range->max = range.max_proto;
 		/* fall through */
@@ -228,9 +257,11 @@ static void DNAT_parse(struct xt_option_call *cb)
 
 static void __DNAT_fcheck(struct xt_fcheck_call *cb, unsigned int *flags)
 {
-	static const unsigned int f = F_TO_DEST | F_RANDOM;
+	static const unsigned int redir_f = F_TO_PORTS | F_RANDOM;
+	static const unsigned int dnat_f = F_TO_DEST | F_RANDOM;
 
-	if ((cb->xflags & f) == f)
+	if ((cb->xflags & redir_f) == redir_f ||
+	    (cb->xflags & dnat_f) == dnat_f)
 		*flags |= NF_NAT_RANGE_PROTO_RANDOM;
 }
 
@@ -443,6 +474,84 @@ static int DNAT_xlate6_v2(struct xt_xlate *xl,
 	return __DNAT_xlate(xl, (const void *)params->target->data, AF_INET6);
 }
 
+static void __REDIRECT_print(const struct nf_nat_range2 *range, bool save)
+{
+	char *range_str = sprint_range(range, AF_INET);
+	const char *dashdash = save ? "--" : "";
+
+	if (strlen(range_str))
+		/* range_str starts with colon, skip over them */
+		printf(" %s %s", save ? "--to-ports" : "redir ports",
+		       range_str + 1);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" %srandom", dashdash);
+}
+
+static int __REDIRECT_xlate(struct xt_xlate *xl,
+			    const struct nf_nat_range2 *range)
+{
+	char *range_str = sprint_range(range, AF_INET);
+
+	xt_xlate_add(xl, "redirect");
+	if (strlen(range_str))
+		xt_xlate_add(xl, " to %s", range_str);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		xt_xlate_add(xl, " random");
+
+	return 1;
+}
+
+static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
+                           int numeric)
+{
+	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
+
+	__REDIRECT_print(&range, false);
+}
+
+static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
+{
+	struct nf_nat_range2 range = RANGE2_INIT_FROM_IPV4_MRC(target->data);
+
+	__REDIRECT_print(&range, true);
+}
+
+static int REDIRECT_xlate(struct xt_xlate *xl,
+			   const struct xt_xlate_tg_params *params)
+{
+	struct nf_nat_range2 range =
+		RANGE2_INIT_FROM_IPV4_MRC(params->target->data);
+
+	return __REDIRECT_xlate(xl, &range);
+}
+
+static void REDIRECT_print6(const void *ip, const struct xt_entry_target *target,
+                            int numeric)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)target->data, sizeof(struct nf_nat_range));
+	__REDIRECT_print(&range, false);
+}
+
+static void REDIRECT_save6(const void *ip, const struct xt_entry_target *target)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)target->data, sizeof(struct nf_nat_range));
+	__REDIRECT_print(&range, true);
+}
+
+static int REDIRECT_xlate6(struct xt_xlate *xl,
+			   const struct xt_xlate_tg_params *params)
+{
+	struct nf_nat_range2 range = {};
+
+	memcpy(&range, (const void *)params->target->data,
+	       sizeof(struct nf_nat_range));
+	return __REDIRECT_xlate(xl, &range);
+}
+
 static struct xtables_target dnat_tg_reg[] = {
 	{
 		.name		= "DNAT",
@@ -459,6 +568,21 @@ static struct xtables_target dnat_tg_reg[] = {
 		.x6_options	= DNAT_opts,
 		.xlate		= DNAT_xlate,
 	},
+	{
+		.name		= "REDIRECT",
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV4,
+		.revision	= 0,
+		.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+		.help		= REDIRECT_help,
+		.print		= REDIRECT_print,
+		.save		= REDIRECT_save,
+		.x6_parse	= DNAT_parse,
+		.x6_fcheck	= DNAT_fcheck,
+		.x6_options	= REDIRECT_opts,
+		.xlate		= REDIRECT_xlate,
+	},
 	{
 		.name		= "DNAT",
 		.version	= XTABLES_VERSION,
@@ -474,6 +598,20 @@ static struct xtables_target dnat_tg_reg[] = {
 		.x6_options	= DNAT_opts,
 		.xlate		= DNAT_xlate6,
 	},
+	{
+		.name		= "REDIRECT",
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV6,
+		.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+		.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+		.help		= REDIRECT_help,
+		.print		= REDIRECT_print6,
+		.save		= REDIRECT_save6,
+		.x6_parse	= DNAT_parse6,
+		.x6_fcheck	= DNAT_fcheck6,
+		.x6_options	= REDIRECT_opts,
+		.xlate		= REDIRECT_xlate6,
+	},
 	{
 		.name		= "DNAT",
 		.version	= XTABLES_VERSION,
diff --git a/extensions/libxt_REDIRECT.t b/extensions/libxt_REDIRECT.t
new file mode 100644
index 0000000000000..f607dd0a12c51
--- /dev/null
+++ b/extensions/libxt_REDIRECT.t
@@ -0,0 +1,16 @@
+:PREROUTING,OUTPUT
+*nat
+-p tcp -j REDIRECT --to-ports 42;=;OK
+-p tcp -j REDIRECT --to-ports 0;=;OK
+-p tcp -j REDIRECT --to-ports 65535;=;OK
+-p tcp -j REDIRECT --to-ports 65536;;FAIL
+-p udp -j REDIRECT --to-ports 0-0;-p udp -j REDIRECT --to-ports 0;OK
+-p udp -j REDIRECT --to-ports 512-512;-p udp -j REDIRECT --to-ports 512;OK
+-p udp -j REDIRECT --to-ports 42-1234;=;OK
+-p tcp -j REDIRECT --to-ports 42-1234 --random;=;OK
+-p tcp -j REDIRECT --to-ports 42-1234/567;;FAIL
+-p tcp -j REDIRECT --to-ports ssh;-p tcp -j REDIRECT --to-ports 22;OK
+-p tcp -j REDIRECT --to-ports ftp-data;-p tcp -j REDIRECT --to-ports 20;OK
+-p tcp -j REDIRECT --to-ports ftp-ssh;;FAIL
+-p tcp -j REDIRECT --to-ports 10-ssh;;FAIL
+-j REDIRECT --to-ports 42;;FAIL
diff --git a/extensions/libxt_REDIRECT.txlate b/extensions/libxt_REDIRECT.txlate
new file mode 100644
index 0000000000000..2c536495b35a2
--- /dev/null
+++ b/extensions/libxt_REDIRECT.txlate
@@ -0,0 +1,26 @@
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT
+nft add rule ip nat prerouting tcp dport 80 counter redirect
+
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 0
+nft add rule ip nat prerouting tcp dport 80 counter redirect to :0
+
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080
+nft add rule ip nat prerouting tcp dport 80 counter redirect to :8080
+
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 0-65535
+nft add rule ip nat prerouting tcp dport 80 counter redirect to :0-65535
+
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 10-22
+nft add rule ip nat prerouting tcp dport 80 counter redirect to :10-22
+
+iptables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080 --random
+nft add rule ip nat prerouting tcp dport 80 counter redirect to :8080 random
+
+ip6tables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT
+nft add rule ip6 nat prerouting tcp dport 80 counter redirect
+
+ip6tables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080
+nft add rule ip6 nat prerouting tcp dport 80 counter redirect to :8080
+
+ip6tables-translate -t nat -A prerouting -p tcp --dport 80 -j REDIRECT --to-ports 8080 --random
+nft add rule ip6 nat prerouting tcp dport 80 counter redirect to :8080 random
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [iptables PATCH v2 9/9] extensions: man: Document service name support in DNAT and REDIRECT
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (7 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 8/9] extensions: Merge REDIRECT into DNAT Phil Sutter
@ 2022-03-31 10:12 ` Phil Sutter
  2022-04-08 15:51 ` [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Pablo Neira Ayuso
  9 siblings, 0 replies; 11+ messages in thread
From: Phil Sutter @ 2022-03-31 10:12 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Jan Engelhardt

Unless as part of a range, service names may be used. Point this out to
avoid confusion.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libxt_DNAT.man     | 2 ++
 extensions/libxt_REDIRECT.man | 1 +
 2 files changed, 3 insertions(+)

diff --git a/extensions/libxt_DNAT.man b/extensions/libxt_DNAT.man
index e044c8216fc09..12d334af5a479 100644
--- a/extensions/libxt_DNAT.man
+++ b/extensions/libxt_DNAT.man
@@ -21,6 +21,8 @@ will be modified.
 If \fBbaseport\fP is given, the difference of the original destination port and
 its value is used as offset into the mapping port range. This allows to create
 shifted portmap ranges and is available since kernel version 4.18.
+For a single port or \fIbaseport\fP, a service name as listed in
+\fB/etc/services\fP may be used.
 .TP
 \fB\-\-random\fP
 If option
diff --git a/extensions/libxt_REDIRECT.man b/extensions/libxt_REDIRECT.man
index 28d4d10b79046..10305597f87a3 100644
--- a/extensions/libxt_REDIRECT.man
+++ b/extensions/libxt_REDIRECT.man
@@ -16,6 +16,7 @@ This specifies a destination port or range of ports to use: without
 this, the destination port is never altered.  This is only valid
 if the rule also specifies one of the following protocols:
 \fBtcp\fP, \fBudp\fP, \fBdccp\fP or \fBsctp\fP.
+For a single port, a service name as listed in \fB/etc/services\fP may be used.
 .TP
 \fB\-\-random\fP
 If option
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT
  2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
                   ` (8 preceding siblings ...)
  2022-03-31 10:12 ` [iptables PATCH v2 9/9] extensions: man: Document service name support in DNAT and REDIRECT Phil Sutter
@ 2022-04-08 15:51 ` Pablo Neira Ayuso
  9 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2022-04-08 15:51 UTC (permalink / raw)
  To: Phil Sutter; +Cc: netfilter-devel, Jan Engelhardt

LGTM.

On Thu, Mar 31, 2022 at 12:12:02PM +0200, Phil Sutter wrote:
> Second try, without fancy service name parsing in ranges this time.
> Instead, mention support for names outside of ranges in extensions man
> page.
> 
> Other changes since v1:
> - Fixed for garbage in output when listing multiple DNAT rules (due to
>   missing reinit of a static buffer.
> - Drop of last patch revealed a crash bug in service name parser.
> - Do not allow service names in ranges' upper boundary.
> - More test cases.
> 
> Phil Sutter (9):
>   man: DNAT: Describe shifted port range feature
>   Revert "libipt_[SD]NAT: avoid false error about multiple destinations
>     specified"
>   extensions: ipt_DNAT: Merge v1 and v2 parsers
>   extensions: ipt_DNAT: Merge v1/v2 print/save code
>   extensions: ipt_DNAT: Combine xlate functions also
>   extensions: DNAT: Rename from libipt to libxt
>   extensions: Merge IPv4 and IPv6 DNAT targets
>   extensions: Merge REDIRECT into DNAT
>   extensions: man: Document service name support in DNAT and REDIRECT
> 
>  extensions/GNUmakefile.in          |   4 +-
>  extensions/libip6t_DNAT.c          | 409 ------------------
>  extensions/libip6t_DNAT.t          |   4 +
>  extensions/libip6t_DNAT.txlate     |  11 -
>  extensions/libip6t_REDIRECT.c      | 170 --------
>  extensions/libip6t_REDIRECT.t      |   6 -
>  extensions/libip6t_REDIRECT.txlate |   5 -
>  extensions/libip6t_SNAT.c          |   9 +-
>  extensions/libipt_DNAT.c           | 507 ----------------------
>  extensions/libipt_DNAT.t           |   4 +
>  extensions/libipt_DNAT.txlate      |  14 -
>  extensions/libipt_REDIRECT.c       | 174 --------
>  extensions/libipt_REDIRECT.t       |   6 -
>  extensions/libipt_REDIRECT.txlate  |   5 -
>  extensions/libipt_SNAT.c           |   3 -
>  extensions/libxt_DNAT.c            | 650 +++++++++++++++++++++++++++++
>  extensions/libxt_DNAT.man          |   7 +-
>  extensions/libxt_DNAT.txlate       |  35 ++
>  extensions/libxt_REDIRECT.man      |   1 +
>  extensions/libxt_REDIRECT.t        |  16 +
>  extensions/libxt_REDIRECT.txlate   |  26 ++
>  21 files changed, 746 insertions(+), 1320 deletions(-)
>  delete mode 100644 extensions/libip6t_DNAT.c
>  delete mode 100644 extensions/libip6t_DNAT.txlate
>  delete mode 100644 extensions/libip6t_REDIRECT.c
>  delete mode 100644 extensions/libip6t_REDIRECT.t
>  delete mode 100644 extensions/libip6t_REDIRECT.txlate
>  delete mode 100644 extensions/libipt_DNAT.c
>  delete mode 100644 extensions/libipt_DNAT.txlate
>  delete mode 100644 extensions/libipt_REDIRECT.c
>  delete mode 100644 extensions/libipt_REDIRECT.t
>  delete mode 100644 extensions/libipt_REDIRECT.txlate
>  create mode 100644 extensions/libxt_DNAT.c
>  create mode 100644 extensions/libxt_DNAT.txlate
>  create mode 100644 extensions/libxt_REDIRECT.t
>  create mode 100644 extensions/libxt_REDIRECT.txlate
> 
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-04-08 15:51 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-31 10:12 [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 1/9] man: DNAT: Describe shifted port range feature Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 2/9] Revert "libipt_[SD]NAT: avoid false error about multiple destinations specified" Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 3/9] extensions: ipt_DNAT: Merge v1 and v2 parsers Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 4/9] extensions: ipt_DNAT: Merge v1/v2 print/save code Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 5/9] extensions: ipt_DNAT: Combine xlate functions also Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 6/9] extensions: DNAT: Rename from libipt to libxt Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 7/9] extensions: Merge IPv4 and IPv6 DNAT targets Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 8/9] extensions: Merge REDIRECT into DNAT Phil Sutter
2022-03-31 10:12 ` [iptables PATCH v2 9/9] extensions: man: Document service name support in DNAT and REDIRECT Phil Sutter
2022-04-08 15:51 ` [iptables PATCH v2 0/9] extensions: Merge *_DNAT and *_REDIRECT Pablo Neira Ayuso

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.