diff mbox

[OpenWrt-Devel,package] firewall: Adds support for IPv6 DNAT, SNAT, and REDIRECT

Message ID ED9CFA6F-B8C3-4264-A22E-DEAF62DB3320@timothyace.com
State Changes Requested
Headers show

Commit Message

Timothy Ace Aug. 13, 2015, 5:58 p.m. UTC
From: Timothy Ace <openwrt.org-1@timothyace.com>

Adds support for IPv6 DNAT, SNAT, and REDIRECT.

Signed-off-by: Timothy Ace <openwrt.org-1@timothyace.com>
---
This is a git patch for the firewall3 git repo at git://nbd.name/firewall3.git

Note1: Luci updates still need to be made in a separate patch to support IPv6 address entry on the port forwards page, but this works using manual entries in /etc/config/firewall for now.

Note2: I changed the section in compare_addr() from this:

-	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
-	        (b->address.v4.s_addr & a->mask.v4.s_addr));

to this:

+	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
+	        (b->address.v4.s_addr & b->mask.v4.s_addr));

... because using "a->mask" instead of "b->mask" on the second line looked wrong to me. Please let me know if that was intentional and I'll re-submit a patch that accounts for that change for the v4 and new v6 sections.


From 83cdcf604b8c81c75065a91f50459721e87740b0 Mon Sep 17 00:00:00 2001
From: Timothy Ace <openwrt.org-1@timothyace.com>
Date: Thu, 13 Aug 2015 00:28:02 -0400
Subject: [PATCH] Adds support for IPv6 DNAT, SNAT, and REDIRECT

---
defaults.c  |  8 ++++----
redirects.c | 42 ++++++++++++++++++++++++++++++++++--------
snats.c     |  1 +
ubus.c      | 24 ++++++++++++++++++++----
utils.c     | 10 ++++++++--
zones.c     | 13 ++++++++-----
6 files changed, 75 insertions(+), 23 deletions(-)

Comments

Timothy Ace Sept. 15, 2015, 5:47 a.m. UTC | #1
I've created a fork of the firewall3 git repo at git://nbd.name/firewall3.git on github repo, that contains this patch, for easier pulling:

Please see: https://github.com/ecammit/firewall3

> On Aug 13, 2015, at 1:58 PM, Timothy Ace <openwrt.org-1@timothyace.com> wrote:
> 
> From: Timothy Ace <openwrt.org-1@timothyace.com>
> 
> Adds support for IPv6 DNAT, SNAT, and REDIRECT.
> 
> Signed-off-by: Timothy Ace <openwrt.org-1@timothyace.com>
> ---
> This is a git patch for the firewall3 git repo at git://nbd.name/firewall3.git
> 
> Note1: Luci updates still need to be made in a separate patch to support IPv6 address entry on the port forwards page, but this works using manual entries in /etc/config/firewall for now.
> 
> Note2: I changed the section in compare_addr() from this:
> 
> -	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
> -	        (b->address.v4.s_addr & a->mask.v4.s_addr));
> 
> to this:
> 
> +	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
> +	        (b->address.v4.s_addr & b->mask.v4.s_addr));
> 
> ... because using "a->mask" instead of "b->mask" on the second line looked wrong to me. Please let me know if that was intentional and I'll re-submit a patch that accounts for that change for the v4 and new v6 sections.
> 
> 
> From 83cdcf604b8c81c75065a91f50459721e87740b0 Mon Sep 17 00:00:00 2001
> From: Timothy Ace <openwrt.org-1@timothyace.com>
> Date: Thu, 13 Aug 2015 00:28:02 -0400
> Subject: [PATCH] Adds support for IPv6 DNAT, SNAT, and REDIRECT
> 
> ---
> defaults.c  |  8 ++++----
> redirects.c | 42 ++++++++++++++++++++++++++++++++++--------
> snats.c     |  1 +
> ubus.c      | 24 ++++++++++++++++++++----
> utils.c     | 10 ++++++++--
> zones.c     | 13 ++++++++-----
> 6 files changed, 75 insertions(+), 23 deletions(-)
> 
> diff --git a/defaults.c b/defaults.c
> index 396cbf7..45d6de6 100644
> --- a/defaults.c
> +++ b/defaults.c
> @@ -32,10 +32,10 @@ static const struct fw3_chain_spec default_chains[] = {
> 	C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_rule"),
> 	C(ANY, FILTER, SYN_FLOOD,     "syn_flood"),
> 
> -	C(V4,  NAT,    UNSPEC,        "delegate_prerouting"),
> -	C(V4,  NAT,    UNSPEC,        "delegate_postrouting"),
> -	C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_rule"),
> -	C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_rule"),
> +	C(ANY, NAT,    UNSPEC,        "delegate_prerouting"),
> +	C(ANY, NAT,    UNSPEC,        "delegate_postrouting"),
> +	C(ANY, NAT,    CUSTOM_CHAINS, "prerouting_rule"),
> +	C(ANY, NAT,    CUSTOM_CHAINS, "postrouting_rule"),
> 
> 	C(ANY, MANGLE, UNSPEC,        "mssfix"),
> 	C(ANY, MANGLE, UNSPEC,        "fwmark"),
> diff --git a/redirects.c b/redirects.c
> index 5b8d7a9..0751e93 100644
> --- a/redirects.c
> +++ b/redirects.c
> @@ -116,11 +116,26 @@ check_families(struct uci_element *e, struct fw3_redirect *r)
> static bool
> compare_addr(struct fw3_address *a, struct fw3_address *b)
> {
> -	if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4)
> +	if (a->family != b->family)
> 		return false;
> 
> -	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
> -	        (b->address.v4.s_addr & a->mask.v4.s_addr));
> +	if (a->family == FW3_FAMILY_V4)
> +	{
> +		return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
> +		        (b->address.v4.s_addr & b->mask.v4.s_addr));
> +	}
> +	else if (a->family == FW3_FAMILY_V6)
> +	{
> +		int i;
> +		for (i = 0; i < 16; i++)
> +		{
> +			if ((a->address.v6.s6_addr[i] & a->mask.v6.s6_addr[i]) !=
> +			    (b->address.v6.s6_addr[i] & b->mask.v6.s6_addr[i]))
> +				return false;
> +		}
> +		return true;
> +	}
> +	return false;
> }
> 
> static bool
> @@ -278,6 +293,7 @@ fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
> 			else
> 			{
> 				set(redir->_src->flags, FW3_FAMILY_V4, redir->target);
> +				set(redir->_src->flags, FW3_FAMILY_V6, redir->target);
> 				redir->_src->conntrack = true;
> 				valid = true;
> 
> @@ -293,6 +309,9 @@ fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
> 					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_ACCEPT);
> 					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_DNAT);
> 					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
> +					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_ACCEPT);
> +					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_DNAT);
> +					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_SNAT);
> 				}
> 			}
> 		}
> @@ -309,6 +328,7 @@ fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
> 			else
> 			{
> 				set(redir->_dest->flags, FW3_FAMILY_V4, redir->target);
> +				set(redir->_dest->flags, FW3_FAMILY_V6, redir->target);
> 				redir->_dest->conntrack = true;
> 				valid = true;
> 			}
> @@ -364,13 +384,22 @@ static void
> set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target,
>             struct fw3_address *addr, struct fw3_port *port)
> {
> -	char buf[sizeof("255.255.255.255:65535-65535\0")];
> +	char buf[INET6_ADDRSTRLEN + sizeof("[]:65535-65535\0")];
> 
> 	buf[0] = '\0';
> 
> 	if (addr && addr->set)
> 	{
> -		inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
> +		if (addr->family == FW3_FAMILY_V4)
> +		{
> +			inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
> +		}
> +		else if (addr->family == FW3_FAMILY_V6)
> +		{
> +			buf[0] = '[';
> +			inet_ntop(AF_INET6, &addr->address.v6, buf + 1, sizeof(buf) - 2);
> +			sprintf(buf + strlen(buf), "]");
> +		}
> 	}
> 
> 	if (port && port->set)
> @@ -607,9 +636,6 @@ fw3_print_redirects(struct fw3_ipt_handle *handle, struct fw3_state *state)
> 	int num = 0;
> 	struct fw3_redirect *redir;
> 
> -	if (handle->family == FW3_FAMILY_V6)
> -		return;
> -
> 	if (handle->table != FW3_TABLE_FILTER && handle->table != FW3_TABLE_NAT)
> 		return;
> 
> diff --git a/snats.c b/snats.c
> index 0f7d851..1d22e33 100644
> --- a/snats.c
> +++ b/snats.c
> @@ -254,6 +254,7 @@ fw3_load_snats(struct fw3_state *state, struct uci_package *p, struct blob_attr
> 		if (snat->_src)
> 		{
> 			set(snat->_src->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
> +			set(snat->_src->flags, FW3_FAMILY_V6, FW3_FLAG_SNAT);
> 			snat->_src->conntrack = true;
> 		}
> 	}
> diff --git a/ubus.c b/ubus.c
> index 9e034c8..4ae5c8b 100644
> --- a/ubus.c
> +++ b/ubus.c
> @@ -93,11 +93,27 @@ parse_subnet(enum fw3_family family, struct blob_attr *dict, int rem)
> 	__blob_for_each_attr(cur, dict, rem)
> 	{
> 		if (!strcmp(blobmsg_name(cur), "address"))
> -			inet_pton(family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
> -			          blobmsg_get_string(cur), &addr->address.v6);
> -
> +		{
> +			if (family == FW3_FAMILY_V4)
> +			{
> +				inet_pton(AF_INET, blobmsg_get_string(cur), &addr->address.v4);
> +			}
> +			else
> +			{
> +				inet_pton(AF_INET6, blobmsg_get_string(cur), &addr->address.v6);
> +			}
> +		}
> 		else if (!strcmp(blobmsg_name(cur), "mask"))
> -			fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6);
> +		{
> +			if (family == FW3_FAMILY_V4)
> +			{
> +				fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v4);
> +			}
> +			else
> +			{
> +				fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6);
> +			}
> +		}
> 	}
> 
> 	return addr;
> diff --git a/utils.c b/utils.c
> index b2fbe02..4b8d44e 100644
> --- a/utils.c
> +++ b/utils.c
> @@ -882,8 +882,14 @@ fw3_flush_conntrack(void *state)
> 
> 			if (!found)
> 			{
> -				inet_ntop(addr->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
> -						  &addr->address.v4, buf, sizeof(buf));
> +				if (addr->family == FW3_FAMILY_V4)
> +				{
> +					inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
> +				}
> +				else
> +				{
> +					inet_ntop(AF_INET6, &addr->address.v6, buf, sizeof(buf));
> +				}
> 
> 				info(" * Flushing conntrack: %s", buf);
> 				fprintf(ct, "%s\n", buf);
> diff --git a/zones.c b/zones.c
> index 2ddd7b4..ff2403f 100644
> --- a/zones.c
> +++ b/zones.c
> @@ -36,8 +36,8 @@ static const struct fw3_chain_spec zone_chains[] = {
> 	C(ANY, FILTER, REJECT,        "zone_%s_dest_REJECT"),
> 	C(ANY, FILTER, DROP,          "zone_%s_dest_DROP"),
> 
> -	C(V4,  NAT,    SNAT,          "zone_%s_postrouting"),
> -	C(V4,  NAT,    DNAT,          "zone_%s_prerouting"),
> +	C(ANY, NAT,    SNAT,          "zone_%s_postrouting"),
> +	C(ANY, NAT,    DNAT,          "zone_%s_prerouting"),
> 
> 	C(ANY, RAW,    NOTRACK,       "zone_%s_notrack"),
> 
> @@ -45,8 +45,8 @@ static const struct fw3_chain_spec zone_chains[] = {
> 	C(ANY, FILTER, CUSTOM_CHAINS, "output_%s_rule"),
> 	C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_%s_rule"),
> 
> -	C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
> -	C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),
> +	C(ANY, NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
> +	C(ANY, NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),
> 
> 	{ }
> };
> @@ -217,13 +217,16 @@ fw3_load_zones(struct fw3_state *state, struct uci_package *p)
> 		if (zone->masq)
> 		{
> 			setbit(zone->flags[0], FW3_FLAG_SNAT);
> +			setbit(zone->flags[1], FW3_FLAG_SNAT);
> 			zone->conntrack = true;
> 		}
> 
> 		if (zone->custom_chains)
> 		{
> 			setbit(zone->flags[0], FW3_FLAG_SNAT);
> +			setbit(zone->flags[1], FW3_FLAG_SNAT);
> 			setbit(zone->flags[0], FW3_FLAG_DNAT);
> +			setbit(zone->flags[1], FW3_FLAG_DNAT);
> 		}
> 
> 		setbit(zone->flags[0], fw3_to_src_target(zone->policy_input));
> @@ -540,7 +543,7 @@ print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
> 		break;
> 
> 	case FW3_TABLE_NAT:
> -		if (zone->masq && handle->family == FW3_FAMILY_V4)
> +		if (zone->masq)
> 		{
> 			fw3_foreach(msrc, &zone->masq_src)
> 			fw3_foreach(mdest, &zone->masq_dest)
> -- 
> 2.3.2 (Apple Git-55)
diff mbox

Patch

diff --git a/defaults.c b/defaults.c
index 396cbf7..45d6de6 100644
--- a/defaults.c
+++ b/defaults.c
@@ -32,10 +32,10 @@  static const struct fw3_chain_spec default_chains[] = {
	C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_rule"),
	C(ANY, FILTER, SYN_FLOOD,     "syn_flood"),

-	C(V4,  NAT,    UNSPEC,        "delegate_prerouting"),
-	C(V4,  NAT,    UNSPEC,        "delegate_postrouting"),
-	C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_rule"),
-	C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_rule"),
+	C(ANY, NAT,    UNSPEC,        "delegate_prerouting"),
+	C(ANY, NAT,    UNSPEC,        "delegate_postrouting"),
+	C(ANY, NAT,    CUSTOM_CHAINS, "prerouting_rule"),
+	C(ANY, NAT,    CUSTOM_CHAINS, "postrouting_rule"),

	C(ANY, MANGLE, UNSPEC,        "mssfix"),
	C(ANY, MANGLE, UNSPEC,        "fwmark"),
diff --git a/redirects.c b/redirects.c
index 5b8d7a9..0751e93 100644
--- a/redirects.c
+++ b/redirects.c
@@ -116,11 +116,26 @@  check_families(struct uci_element *e, struct fw3_redirect *r)
static bool
compare_addr(struct fw3_address *a, struct fw3_address *b)
{
-	if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4)
+	if (a->family != b->family)
		return false;

-	return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
-	        (b->address.v4.s_addr & a->mask.v4.s_addr));
+	if (a->family == FW3_FAMILY_V4)
+	{
+		return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
+		        (b->address.v4.s_addr & b->mask.v4.s_addr));
+	}
+	else if (a->family == FW3_FAMILY_V6)
+	{
+		int i;
+		for (i = 0; i < 16; i++)
+		{
+			if ((a->address.v6.s6_addr[i] & a->mask.v6.s6_addr[i]) !=
+			    (b->address.v6.s6_addr[i] & b->mask.v6.s6_addr[i]))
+				return false;
+		}
+		return true;
+	}
+	return false;
}

static bool
@@ -278,6 +293,7 @@  fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
			else
			{
				set(redir->_src->flags, FW3_FAMILY_V4, redir->target);
+				set(redir->_src->flags, FW3_FAMILY_V6, redir->target);
				redir->_src->conntrack = true;
				valid = true;

@@ -293,6 +309,9 @@  fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_ACCEPT);
					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_DNAT);
					set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
+					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_ACCEPT);
+					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_DNAT);
+					set(redir->_dest->flags, FW3_FAMILY_V6, FW3_FLAG_SNAT);
				}
			}
		}
@@ -309,6 +328,7 @@  fw3_load_redirects(struct fw3_state *state, struct uci_package *p)
			else
			{
				set(redir->_dest->flags, FW3_FAMILY_V4, redir->target);
+				set(redir->_dest->flags, FW3_FAMILY_V6, redir->target);
				redir->_dest->conntrack = true;
				valid = true;
			}
@@ -364,13 +384,22 @@  static void
set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target,
             struct fw3_address *addr, struct fw3_port *port)
{
-	char buf[sizeof("255.255.255.255:65535-65535\0")];
+	char buf[INET6_ADDRSTRLEN + sizeof("[]:65535-65535\0")];

	buf[0] = '\0';

	if (addr && addr->set)
	{
-		inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
+		if (addr->family == FW3_FAMILY_V4)
+		{
+			inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
+		}
+		else if (addr->family == FW3_FAMILY_V6)
+		{
+			buf[0] = '[';
+			inet_ntop(AF_INET6, &addr->address.v6, buf + 1, sizeof(buf) - 2);
+			sprintf(buf + strlen(buf), "]");
+		}
	}

	if (port && port->set)
@@ -607,9 +636,6 @@  fw3_print_redirects(struct fw3_ipt_handle *handle, struct fw3_state *state)
	int num = 0;
	struct fw3_redirect *redir;

-	if (handle->family == FW3_FAMILY_V6)
-		return;
-
	if (handle->table != FW3_TABLE_FILTER && handle->table != FW3_TABLE_NAT)
		return;

diff --git a/snats.c b/snats.c
index 0f7d851..1d22e33 100644
--- a/snats.c
+++ b/snats.c
@@ -254,6 +254,7 @@  fw3_load_snats(struct fw3_state *state, struct uci_package *p, struct blob_attr
		if (snat->_src)
		{
			set(snat->_src->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
+			set(snat->_src->flags, FW3_FAMILY_V6, FW3_FLAG_SNAT);
			snat->_src->conntrack = true;
		}
	}
diff --git a/ubus.c b/ubus.c
index 9e034c8..4ae5c8b 100644
--- a/ubus.c
+++ b/ubus.c
@@ -93,11 +93,27 @@  parse_subnet(enum fw3_family family, struct blob_attr *dict, int rem)
	__blob_for_each_attr(cur, dict, rem)
	{
		if (!strcmp(blobmsg_name(cur), "address"))
-			inet_pton(family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-			          blobmsg_get_string(cur), &addr->address.v6);
-
+		{
+			if (family == FW3_FAMILY_V4)
+			{
+				inet_pton(AF_INET, blobmsg_get_string(cur), &addr->address.v4);
+			}
+			else
+			{
+				inet_pton(AF_INET6, blobmsg_get_string(cur), &addr->address.v6);
+			}
+		}
		else if (!strcmp(blobmsg_name(cur), "mask"))
-			fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6);
+		{
+			if (family == FW3_FAMILY_V4)
+			{
+				fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v4);
+			}
+			else
+			{
+				fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6);
+			}
+		}
	}

	return addr;
diff --git a/utils.c b/utils.c
index b2fbe02..4b8d44e 100644
--- a/utils.c
+++ b/utils.c
@@ -882,8 +882,14 @@  fw3_flush_conntrack(void *state)

			if (!found)
			{
-				inet_ntop(addr->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-						  &addr->address.v4, buf, sizeof(buf));
+				if (addr->family == FW3_FAMILY_V4)
+				{
+					inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
+				}
+				else
+				{
+					inet_ntop(AF_INET6, &addr->address.v6, buf, sizeof(buf));
+				}

				info(" * Flushing conntrack: %s", buf);
				fprintf(ct, "%s\n", buf);
diff --git a/zones.c b/zones.c
index 2ddd7b4..ff2403f 100644
--- a/zones.c
+++ b/zones.c
@@ -36,8 +36,8 @@  static const struct fw3_chain_spec zone_chains[] = {
	C(ANY, FILTER, REJECT,        "zone_%s_dest_REJECT"),
	C(ANY, FILTER, DROP,          "zone_%s_dest_DROP"),

-	C(V4,  NAT,    SNAT,          "zone_%s_postrouting"),
-	C(V4,  NAT,    DNAT,          "zone_%s_prerouting"),
+	C(ANY, NAT,    SNAT,          "zone_%s_postrouting"),
+	C(ANY, NAT,    DNAT,          "zone_%s_prerouting"),

	C(ANY, RAW,    NOTRACK,       "zone_%s_notrack"),

@@ -45,8 +45,8 @@  static const struct fw3_chain_spec zone_chains[] = {
	C(ANY, FILTER, CUSTOM_CHAINS, "output_%s_rule"),
	C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_%s_rule"),

-	C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
-	C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),
+	C(ANY, NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
+	C(ANY, NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),

	{ }
};
@@ -217,13 +217,16 @@  fw3_load_zones(struct fw3_state *state, struct uci_package *p)
		if (zone->masq)
		{
			setbit(zone->flags[0], FW3_FLAG_SNAT);
+			setbit(zone->flags[1], FW3_FLAG_SNAT);
			zone->conntrack = true;
		}

		if (zone->custom_chains)
		{
			setbit(zone->flags[0], FW3_FLAG_SNAT);
+			setbit(zone->flags[1], FW3_FLAG_SNAT);
			setbit(zone->flags[0], FW3_FLAG_DNAT);
+			setbit(zone->flags[1], FW3_FLAG_DNAT);
		}

		setbit(zone->flags[0], fw3_to_src_target(zone->policy_input));
@@ -540,7 +543,7 @@  print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
		break;

	case FW3_TABLE_NAT:
-		if (zone->masq && handle->family == FW3_FAMILY_V4)
+		if (zone->masq)
		{
			fw3_foreach(msrc, &zone->masq_src)
			fw3_foreach(mdest, &zone->masq_dest)