[libvirt PATCH v2 0/3] network: support NAT with IPv6

The virtual network has never supported NAT with IPv6 since this feature didn't exist at the time. NAT has been available since RHEL-7 vintage though, and it is desirable to be able to use it. This series enables it with <forward mode=3D"nat"> <nat ipv6=3D"yes"/> </forward> Note that I do NOT actually change the default.xml to enable use of IPv6 because that will cause failure if the user has force disabled IPv6 on their host kernel. Of course our current default.xml is already broken if someone has done the reverse and force disabled IPv4. We've also long had a problem with guests bringing up the default network with the same subnet as the host. We'll have this same issue with IPv6 too. On my prompting Laine proposed a way to deal with the clash by tearing down a network, if we see a real host NIC get assigned the same subnet. Meanwhile we also have complaints about the fact that libvirt does anything todo with networking in the %post of the RPM. I'm thinking that we can do something entirely different by introducing a concept of "automatic subnet selection" into the virtual network. Consider if we made default.xml be able to contain only: <network> <name>default</name> <forward/> <ip family=3D"ipv4" autoaddr=3D"yes"> <dhcp/> </ip> <ip family=3D"ipv6" autoaddr=3D"yes"/> </network> Conceptually this means - Try to gimme a subnet with IPv4 and DHCP - Try to gimme a subnet with IPv6 and RAs Now when we start the virtual network - If IPv4 is not enabled on host, don't assign addr - Else - Iterate N=3D1..254 to find a free range for IPv4 - Use 192.168.N.0/24 for subnet - Use 192.168.N.1 for host IP - Use 192.168.N.2 -> 192.168.N.254 for guest DHCP - If IPv6 is not enabled on host, don't assign addr - Else - Generate NNNN:NNNN as 4 random bytes - Use fd00:add:f00d:NNNN:NNNN::0/64 for IPv6 subnet - Use fd00:add:f00d:NNNN:NNNN::1 for host IP - Use route advertizement for IPv6 zero-conf With NNNN:NNNN, even with 1000 guests running, we have just a 0.02% chance of clashing with a guest for IPv6. The "live" XML would always reflect the currently assigned addresses Proactively monitor the address allocations of the host. If we see a conflicting address appear, take down the dnsmasq intance, generate a new subnet, bring dnsmasq back online. Ideally we would have to bring the guest network links offline and then online again to force DHCP re-assignment immediately. In v2: - Fix short circuit XML closing tag - Fix error checking in enum conversion - Fix docs typos - Improve RNG schema Daniel P. Berrang=C3=A9 (3): util: add support for IPv6 masquerade rules conf: add an attribute to turn on NAT for IPv6 virtual networks network: wire up support for IPv6 NAT rules docs/formatnetwork.html.in | 14 ++ docs/schemas/network.rng | 5 + src/conf/network_conf.c | 30 ++- src/conf/network_conf.h | 2 + src/network/bridge_driver_linux.c | 23 +- src/util/viriptables.c | 33 +-- .../nat-ipv6-masquerade-linux.args | 228 ++++++++++++++++++ .../nat-ipv6-masquerade.xml | 17 ++ tests/networkxml2firewalltest.c | 1 + .../nat-network-forward-nat-ipv6.xml | 10 + .../nat-network-forward-nat-ipv6.xml | 10 + tests/networkxml2xmltest.c | 1 + 12 files changed, 342 insertions(+), 32 deletions(-) create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.a= rgs create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade.xml create mode 100644 tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml create mode 100644 tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml --=20 2.26.2

IPv6 does support masquerade since Linux 3.9.0 / ip6tables 1.4.18, which is Fedora 18 / RHEL-7 vintage, which covers all our supported Linux versions. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/util/viriptables.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/util/viriptables.c b/src/util/viriptables.c index e6a1ded8d5..8ccce835b2 100644 --- a/src/util/viriptables.c +++ b/src/util/viriptables.c @@ -854,29 +854,24 @@ iptablesForwardMasquerade(virFirewallPtr fw, g_autofree char *portRangeStr = NULL; g_autofree char *natRangeStr = NULL; virFirewallRulePtr rule; + int af = VIR_SOCKET_ADDR_FAMILY(netaddr); + virFirewallLayer layer = af == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; - if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) { - /* Higher level code *should* guaranteee it's impossible to get here. */ - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Attempted to NAT '%s'. NAT is only supported for IPv4."), - networkstr); - return -1; - } - - if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, af)) { if (!(addrStartStr = virSocketAddrFormat(&addr->start))) return -1; - if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, af)) { if (!(addrEndStr = virSocketAddrFormat(&addr->end))) return -1; } } if (protocol && protocol[0]) { - rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + rule = virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -885,7 +880,7 @@ iptablesForwardMasquerade(virFirewallPtr fw, "!", "--destination", networkstr, NULL); } else { - rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + rule = virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -1004,20 +999,14 @@ iptablesForwardDontMasquerade(virFirewallPtr fw, int action) { g_autofree char *networkstr = NULL; + virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; - if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) { - /* Higher level code *should* guaranteee it's impossible to get here. */ - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Attempted to NAT '%s'. NAT is only supported for IPv4."), - networkstr); - return -1; - } - if (physdev && physdev[0]) - virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -1027,7 +1016,7 @@ iptablesForwardDontMasquerade(virFirewallPtr fw, "--jump", "RETURN", NULL); else - virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", -- 2.26.2

(or: "Remove hardcoding to IPv4 in function that creates masquerade rules" :-) On 6/9/20 12:17 PM, Daniel P. Berrangé wrote:
IPv6 does support masquerade since Linux 3.9.0 / ip6tables 1.4.18, which is Fedora 18 / RHEL-7 vintage, which covers all our supported Linux versions.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/util/viriptables.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-)
diff --git a/src/util/viriptables.c b/src/util/viriptables.c index e6a1ded8d5..8ccce835b2 100644 --- a/src/util/viriptables.c +++ b/src/util/viriptables.c @@ -854,29 +854,24 @@ iptablesForwardMasquerade(virFirewallPtr fw, g_autofree char *portRangeStr = NULL; g_autofree char *natRangeStr = NULL; virFirewallRulePtr rule; + int af = VIR_SOCKET_ADDR_FAMILY(netaddr); + virFirewallLayer layer = af == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1;
- if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) { - /* Higher level code *should* guaranteee it's impossible to get here. */ - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Attempted to NAT '%s'. NAT is only supported for IPv4."), - networkstr); - return -1; - } - - if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, af)) { if (!(addrStartStr = virSocketAddrFormat(&addr->start))) return -1; - if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, af)) { if (!(addrEndStr = virSocketAddrFormat(&addr->end))) return -1; } }
if (protocol && protocol[0]) { - rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + rule = virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -885,7 +880,7 @@ iptablesForwardMasquerade(virFirewallPtr fw, "!", "--destination", networkstr, NULL); } else { - rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + rule = virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -1004,20 +999,14 @@ iptablesForwardDontMasquerade(virFirewallPtr fw, int action) { g_autofree char *networkstr = NULL; + virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1;
- if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) { - /* Higher level code *should* guaranteee it's impossible to get here. */ - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Attempted to NAT '%s'. NAT is only supported for IPv4."), - networkstr); - return -1; - } - if (physdev && physdev[0]) - virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING", @@ -1027,7 +1016,7 @@ iptablesForwardDontMasquerade(virFirewallPtr fw, "--jump", "RETURN", NULL); else - virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + virFirewallAddRule(fw, layer, "--table", "nat", action == ADD ? "--insert" : "--delete", pvt ? "LIBVIRT_PRT" : "POSTROUTING",
It's nice that adding capability is done by *removing* code rather than adding it! Reviewed-by: Laine Stump <laine@redhat.com>

Historically IPv6 did not support NAT, so when IPv6 was added to libvirt's virtual networks, when requesting <forward mode="nat"/> libvirt will NOT apply NAT to IPv6 traffic, only IPv4 traffic. This is an annoying historical design decision as it means we cannot enable IPv6 automatically. We thus need to introduce a new attribute <forward mode="nat"> <nat ipv6="yes"/> </forward> The new attribute is a tri-state, so it leaves open the possibility of us intentionally changing the default behaviour in future to honour NAT for IPv6. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/formatnetwork.html.in | 14 +++++++++ docs/schemas/network.rng | 5 ++++ src/conf/network_conf.c | 30 +++++++++++++++++-- src/conf/network_conf.h | 2 ++ .../nat-network-forward-nat-ipv6.xml | 10 +++++++ .../nat-network-forward-nat-ipv6.xml | 10 +++++++ tests/networkxml2xmltest.c | 1 + 7 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml create mode 100644 tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 0383e2d891..fb740111b1 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -276,6 +276,20 @@ </nat> </forward> ...</pre> + + <p> + <span class="since">Since 6.5.0</span> it is possible to + enable NAT with IPv6 networking. As noted above, IPv6 + has historically done plain forwarding and thus to avoid + breaking historical compatibility, IPv6 NAT must be + explicitly requested. + </p> + <pre> +... + <forward mode='nat'> + <nat ipv6='yes'/> + </forward> +...</pre> </dd> <dt><code>route</code></dt> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 88b6f4dfdd..3a5eb3ced4 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -181,6 +181,11 @@ </optional> <optional> <element name='nat'> + <optional> + <attribute name="ipv6"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <optional> <element name='address'> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index f1d22b25b1..1b89e2985d 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1358,6 +1358,7 @@ virNetworkForwardNatDefParseXML(const char *networkName, int nNatAddrs, nNatPorts; char *addrStart = NULL; char *addrEnd = NULL; + char *ipv6 = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = node; @@ -1369,6 +1370,20 @@ virNetworkForwardNatDefParseXML(const char *networkName, goto cleanup; } + ipv6 = virXMLPropString(node, "ipv6"); + if (ipv6) { + int natIPv6; + if ((natIPv6 = virTristateBoolTypeFromString(ipv6)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid ipv6 setting '%s' " + "in network '%s' NAT"), + ipv6, networkName); + goto cleanup; + } + def->natIPv6 = natIPv6; + VIR_FREE(ipv6); + } + /* addresses for SNAT */ nNatAddrs = virXPathNodeSet("./address", ctxt, &natAddrNodes); if (nNatAddrs < 0) { @@ -2516,10 +2531,18 @@ virNetworkForwardNatDefFormat(virBufferPtr buf, goto cleanup; } - if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end) + if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end && !fwd->natIPv6) return 0; - virBufferAddLit(buf, "<nat>\n"); + virBufferAddLit(buf, "<nat"); + if (fwd->natIPv6) + virBufferAsprintf(buf, " ipv6='%s'", virTristateBoolTypeToString(fwd->natIPv6)); + + if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); if (addrStart) { @@ -2627,7 +2650,8 @@ virNetworkDefFormatBuf(virBufferPtr buf, || def->forward.port.start || def->forward.port.end || (def->forward.driverName - != VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT)); + != VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT) + || def->forward.natIPv6); virBufferAsprintf(buf, "%s>\n", shortforward ? "/" : ""); virBufferAdjustIndent(buf, 2); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index f2dc388ef0..e3a61c62ea 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -244,6 +244,8 @@ struct _virNetworkForwardDef { /* ranges for NAT */ virSocketAddrRange addr; virPortRange port; + + virTristateBool natIPv6; }; typedef struct _virPortGroupDef virPortGroupDef; diff --git a/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml b/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml new file mode 100644 index 0000000000..c360941e1e --- /dev/null +++ b/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml @@ -0,0 +1,10 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <bridge name="virbr0"/> + <forward mode="nat"> + <nat ipv6="yes"/> + </forward> + <ip family="ipv6" address="2001:db8:ac10:fe01::1" prefix="64"> + </ip> +</network> diff --git a/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml b/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml new file mode 100644 index 0000000000..cfec391ee2 --- /dev/null +++ b/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml @@ -0,0 +1,10 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward mode='nat'> + <nat ipv6='yes'/> + </forward> + <bridge name='virbr0' stp='on' delay='0'/> + <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'> + </ip> +</network> diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c index 700744785a..17817418b7 100644 --- a/tests/networkxml2xmltest.c +++ b/tests/networkxml2xmltest.c @@ -140,6 +140,7 @@ mymain(void) DO_TEST("nat-network-dns-forward-plain"); DO_TEST("nat-network-dns-forwarders"); DO_TEST("nat-network-dns-forwarder-no-resolv"); + DO_TEST("nat-network-forward-nat-ipv6"); DO_TEST("nat-network-forward-nat-address"); DO_TEST("nat-network-forward-nat-no-address"); DO_TEST("nat-network-mtu"); -- 2.26.2

On 6/9/20 12:17 PM, Daniel P. Berrangé wrote:
Historically IPv6 did not support NAT, so when IPv6 was added to libvirt's virtual networks, when requesting <forward mode="nat"/> libvirt will NOT apply NAT to IPv6 traffic, only IPv4 traffic.
This is an annoying historical design decision as it means we cannot enable IPv6 automatically. We thus need to introduce a new attribute
<forward mode="nat"> <nat ipv6="yes"/> </forward>
The new attribute is a tri-state, so it leaves open the possibility of us intentionally changing the default behaviour in future to honour NAT for IPv6.
Any network defined with an older libvirt, and even newly defined networks that don't have ipv6 set explicitly in the input XML, won't have an explicit value saved in the config. So if we change the default, those existing networks will have different behavior. I don't see how we could change the default without those existing networks getting automatically changed to ipv6='yes'.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/formatnetwork.html.in | 14 +++++++++ docs/schemas/network.rng | 5 ++++ src/conf/network_conf.c | 30 +++++++++++++++++-- src/conf/network_conf.h | 2 ++ .../nat-network-forward-nat-ipv6.xml | 10 +++++++ .../nat-network-forward-nat-ipv6.xml | 10 +++++++ tests/networkxml2xmltest.c | 1 + 7 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml create mode 100644 tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 0383e2d891..fb740111b1 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -276,6 +276,20 @@ </nat> </forward> ...</pre> + + <p> + <span class="since">Since 6.5.0</span> it is possible to + enable NAT with IPv6 networking. As noted above, IPv6 + has historically done plain forwarding and thus to avoid + breaking historical compatibility, IPv6 NAT must be + explicitly requested. + </p> + <pre> +... + <forward mode='nat'> + <nat ipv6='yes'/> + </forward> +...</pre> </dd>
<dt><code>route</code></dt> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 88b6f4dfdd..3a5eb3ced4 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -181,6 +181,11 @@ </optional> <optional> <element name='nat'> + <optional> + <attribute name="ipv6"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <optional> <element name='address'> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index f1d22b25b1..1b89e2985d 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1358,6 +1358,7 @@ virNetworkForwardNatDefParseXML(const char *networkName, int nNatAddrs, nNatPorts; char *addrStart = NULL; char *addrEnd = NULL; + char *ipv6 = NULL;
Eww. I just noticed this file isn't doing autofree memory allocation on many of its pointers...
VIR_XPATH_NODE_AUTORESTORE(ctxt);
ctxt->node = node; @@ -1369,6 +1370,20 @@ virNetworkForwardNatDefParseXML(const char *networkName, goto cleanup; }
+ ipv6 = virXMLPropString(node, "ipv6"); + if (ipv6) { + int natIPv6; + if ((natIPv6 = virTristateBoolTypeFromString(ipv6)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid ipv6 setting '%s' " + "in network '%s' NAT"), + ipv6, networkName); + goto cleanup;
...which leads to a memory leak here, since the code at the cleanup label doesn't free ipv6. For some reason I felt like spending an hour doing something mindless just now, so I made a patch to convert all the char* and xmlNodePtr* in network_conf.c to g_autofree (along with a few other g_autoptr things, and the standard cleanup/error label removals). I'll post that as a patch based on your patches; you can either just assume that my patch will go in after yours and fix the memory leak, or you can just change ipv6 to a g_autofree yourself and I'll adjust my patch accordingly.
+ } + def->natIPv6 = natIPv6; + VIR_FREE(ipv6); + } + /* addresses for SNAT */ nNatAddrs = virXPathNodeSet("./address", ctxt, &natAddrNodes); if (nNatAddrs < 0) { @@ -2516,10 +2531,18 @@ virNetworkForwardNatDefFormat(virBufferPtr buf, goto cleanup; }
- if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end) + if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end && !fwd->natIPv6) return 0;
- virBufferAddLit(buf, "<nat>\n"); + virBufferAddLit(buf, "<nat"); + if (fwd->natIPv6) + virBufferAsprintf(buf, " ipv6='%s'", virTristateBoolTypeToString(fwd->natIPv6)); + + if (!addrEnd && !addrStart && !fwd->port.start && !fwd->port.end) { + virBufferAddLit(buf, "/>\n"); + return 0; + } + virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2);
if (addrStart) { @@ -2627,7 +2650,8 @@ virNetworkDefFormatBuf(virBufferPtr buf, || def->forward.port.start || def->forward.port.end || (def->forward.driverName - != VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT)); + != VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT) + || def->forward.natIPv6); virBufferAsprintf(buf, "%s>\n", shortforward ? "/" : ""); virBufferAdjustIndent(buf, 2);
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index f2dc388ef0..e3a61c62ea 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -244,6 +244,8 @@ struct _virNetworkForwardDef { /* ranges for NAT */ virSocketAddrRange addr; virPortRange port; + + virTristateBool natIPv6; };
typedef struct _virPortGroupDef virPortGroupDef; diff --git a/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml b/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml new file mode 100644 index 0000000000..c360941e1e --- /dev/null +++ b/tests/networkxml2xmlin/nat-network-forward-nat-ipv6.xml @@ -0,0 +1,10 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <bridge name="virbr0"/> + <forward mode="nat"> + <nat ipv6="yes"/> + </forward> + <ip family="ipv6" address="2001:db8:ac10:fe01::1" prefix="64"> + </ip> +</network> diff --git a/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml b/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml new file mode 100644 index 0000000000..cfec391ee2 --- /dev/null +++ b/tests/networkxml2xmlout/nat-network-forward-nat-ipv6.xml @@ -0,0 +1,10 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward mode='nat'> + <nat ipv6='yes'/> + </forward> + <bridge name='virbr0' stp='on' delay='0'/> + <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'> + </ip> +</network> diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c index 700744785a..17817418b7 100644 --- a/tests/networkxml2xmltest.c +++ b/tests/networkxml2xmltest.c @@ -140,6 +140,7 @@ mymain(void) DO_TEST("nat-network-dns-forward-plain"); DO_TEST("nat-network-dns-forwarders"); DO_TEST("nat-network-dns-forwarder-no-resolv"); + DO_TEST("nat-network-forward-nat-ipv6"); DO_TEST("nat-network-forward-nat-address"); DO_TEST("nat-network-forward-nat-no-address"); DO_TEST("nat-network-mtu");
I don't know that I agree with the comment about ease of changing the default in the future (maybe I misunderstood), and if you want the patch to stand on its own you should fix the one memory leak (my patch will be posted soon, if not already), but it all looks good and works properly now (for defining the network at least - I haven't actually tried getting an IPv6 address on a guest yet, but I'll let you know if there are any problems :-) Reviewed-by: Laine Stump <laine@redhat.com>

On Wed, Jun 10, 2020 at 12:14:51AM -0400, Laine Stump wrote:
On 6/9/20 12:17 PM, Daniel P. Berrangé wrote:
Historically IPv6 did not support NAT, so when IPv6 was added to libvirt's virtual networks, when requesting <forward mode="nat"/> libvirt will NOT apply NAT to IPv6 traffic, only IPv4 traffic.
This is an annoying historical design decision as it means we cannot enable IPv6 automatically. We thus need to introduce a new attribute
<forward mode="nat"> <nat ipv6="yes"/> </forward>
The new attribute is a tri-state, so it leaves open the possibility of us intentionally changing the default behaviour in future to honour NAT for IPv6.
Any network defined with an older libvirt, and even newly defined networks that don't have ipv6 set explicitly in the input XML, won't have an explicit value saved in the config. So if we change the default, those existing networks will have different behavior. I don't see how we could change the default without those existing networks getting automatically changed to ipv6='yes'.
Yeah, with my comment about auto-addressing, we wouldn't need to change this default anyway. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Now that we have support for IPv6 in the iptables helpers, and a new option in the XML schema, we can wire up support for it in the network driver. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver_linux.c | 23 +- .../nat-ipv6-masquerade-linux.args | 228 ++++++++++++++++++ .../nat-ipv6-masquerade.xml | 17 ++ tests/networkxml2firewalltest.c | 1 + 4 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade.xml diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index b0bd207250..fcb3803965 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -307,7 +307,8 @@ int networkCheckRouteCollision(virNetworkDefPtr def) return ret; } -static const char networkLocalMulticast[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv4[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv6[] = "ffx2::/16"; static const char networkLocalBroadcast[] = "255.255.255.255/32"; static int @@ -317,6 +318,7 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, { int prefix = virNetworkIPDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(def, 0); + bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET); if (prefix < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -406,7 +408,8 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, return -1; /* exempt local network broadcast address as destination */ - if (iptablesAddDontMasquerade(fw, + if (isIPv4 && + iptablesAddDontMasquerade(fw, &ipdef->address, prefix, forwardIf, @@ -418,7 +421,8 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, &ipdef->address, prefix, forwardIf, - networkLocalMulticast) < 0) + isIPv4 ? networkLocalMulticastIPv4 : + networkLocalMulticastIPv6) < 0) return -1; return 0; @@ -431,6 +435,7 @@ networkRemoveMasqueradingFirewallRules(virFirewallPtr fw, { int prefix = virNetworkIPDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(def, 0); + bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET); if (prefix < 0) return 0; @@ -439,10 +444,12 @@ networkRemoveMasqueradingFirewallRules(virFirewallPtr fw, &ipdef->address, prefix, forwardIf, - networkLocalMulticast) < 0) + isIPv4 ? networkLocalMulticastIPv4 : + networkLocalMulticastIPv6) < 0) return -1; - if (iptablesRemoveDontMasquerade(fw, + if (isIPv4 && + iptablesRemoveDontMasquerade(fw, &ipdef->address, prefix, forwardIf, @@ -769,7 +776,8 @@ networkAddIPSpecificFirewallRules(virFirewallPtr fw, */ if (def->forward.type == VIR_NETWORK_FORWARD_NAT) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) || + def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) return networkAddMasqueradingFirewallRules(fw, def, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) return networkAddRoutingFirewallRules(fw, def, ipdef); @@ -786,7 +794,8 @@ networkRemoveIPSpecificFirewallRules(virFirewallPtr fw, virNetworkIPDefPtr ipdef) { if (def->forward.type == VIR_NETWORK_FORWARD_NAT) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) || + def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) return networkRemoveMasqueradingFirewallRules(fw, def, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) return networkRemoveRoutingFirewallRules(fw, def, ipdef); diff --git a/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args b/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args new file mode 100644 index 0000000000..4ba4c3da30 --- /dev/null +++ b/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args @@ -0,0 +1,228 @@ +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 67 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 67 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 68 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 68 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWO \ +--in-interface virbr0 \ +--jump REJECT +iptables \ +--table filter \ +--insert LIBVIRT_FWI \ +--out-interface virbr0 \ +--jump REJECT +iptables \ +--table filter \ +--insert LIBVIRT_FWX \ +--in-interface virbr0 \ +--out-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWO \ +--in-interface virbr0 \ +--jump REJECT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWI \ +--out-interface virbr0 \ +--jump REJECT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWX \ +--in-interface virbr0 \ +--out-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 547 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 546 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWO \ +--source 192.168.122.0/24 \ +--in-interface virbr0 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWI \ +--destination 192.168.122.0/24 \ +--out-interface virbr0 \ +--match conntrack \ +--ctstate ESTABLISHED,RELATED \ +--jump ACCEPT +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +-p udp '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +-p tcp '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +--destination 255.255.255.255/32 \ +--jump RETURN +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +--destination 224.0.0.0/24 \ +--jump RETURN +ip6tables \ +--table filter \ +--insert LIBVIRT_FWO \ +--source 2001:db8:ca2:2::/64 \ +--in-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWI \ +--destination 2001:db8:ca2:2::/64 \ +--out-interface virbr0 \ +--match conntrack \ +--ctstate ESTABLISHED,RELATED \ +--jump ACCEPT +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +-p udp '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +-p tcp '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +--destination ffx2::/16 \ +--jump RETURN +iptables \ +--table mangle \ +--insert LIBVIRT_PRT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 68 \ +--jump CHECKSUM \ +--checksum-fill diff --git a/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml b/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml new file mode 100644 index 0000000000..03bcc8c67d --- /dev/null +++ b/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml @@ -0,0 +1,17 @@ +<network> + <name>default</name> + <bridge name="virbr0"/> + <forward> + <nat ipv6="yes"/> + </forward> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254"/> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2firewalltest.c b/tests/networkxml2firewalltest.c index 0ad5e2303b..697bfd7e03 100644 --- a/tests/networkxml2firewalltest.c +++ b/tests/networkxml2firewalltest.c @@ -173,6 +173,7 @@ mymain(void) DO_TEST("nat-many-ips"); DO_TEST("nat-no-dhcp"); DO_TEST("nat-ipv6"); + DO_TEST("nat-ipv6-masquerade"); DO_TEST("route-default"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 2.26.2

On 6/9/20 12:17 PM, Daniel P. Berrangé wrote:
Now that we have support for IPv6 in the iptables helpers, and a new option in the XML schema, we can wire up support for it in the network driver.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver_linux.c | 23 +- .../nat-ipv6-masquerade-linux.args | 228 ++++++++++++++++++ .../nat-ipv6-masquerade.xml | 17 ++ tests/networkxml2firewalltest.c | 1 + 4 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade.xml
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index b0bd207250..fcb3803965 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -307,7 +307,8 @@ int networkCheckRouteCollision(virNetworkDefPtr def) return ret; }
-static const char networkLocalMulticast[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv4[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv6[] = "ffx2::/16";
Once I got everything built and tried starting a network with ipv6 nat, I got this error message: virsh net-start ipv6 error: Failed to start network ipv6 error: COMMAND_FAILED: '/usr/sbin/ip6tables -w10 -w --table nat --insert LIBVIRT_PRT --source 2001:4978:2ac:5::/80 --destination ffx2::/16 --jump RETURN' failed: ip6tables v1.8.3 (legacy): host/network `ffx2::' not found Try `ip6tables -h' or 'ip6tables --help' for more information. Do we need to do something different for multicast traffic in the case of IPv6? Other than that it all looks good, so Reviewed-by: Laine Stump <laine@redhat.com> once the problem with multicast ffx2::/16 as the destination of a rule is resolved.
static const char networkLocalBroadcast[] = "255.255.255.255/32";
static int @@ -317,6 +318,7 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, { int prefix = virNetworkIPDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(def, 0); + bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -406,7 +408,8 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, return -1;
/* exempt local network broadcast address as destination */ - if (iptablesAddDontMasquerade(fw, + if (isIPv4 && + iptablesAddDontMasquerade(fw, &ipdef->address, prefix, forwardIf, @@ -418,7 +421,8 @@ networkAddMasqueradingFirewallRules(virFirewallPtr fw, &ipdef->address, prefix, forwardIf, - networkLocalMulticast) < 0) + isIPv4 ? networkLocalMulticastIPv4 : + networkLocalMulticastIPv6) < 0) return -1;
return 0; @@ -431,6 +435,7 @@ networkRemoveMasqueradingFirewallRules(virFirewallPtr fw, { int prefix = virNetworkIPDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(def, 0); + bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0) return 0; @@ -439,10 +444,12 @@ networkRemoveMasqueradingFirewallRules(virFirewallPtr fw, &ipdef->address, prefix, forwardIf, - networkLocalMulticast) < 0) + isIPv4 ? networkLocalMulticastIPv4 : + networkLocalMulticastIPv6) < 0) return -1;
- if (iptablesRemoveDontMasquerade(fw, + if (isIPv4 && + iptablesRemoveDontMasquerade(fw, &ipdef->address, prefix, forwardIf, @@ -769,7 +776,8 @@ networkAddIPSpecificFirewallRules(virFirewallPtr fw, */
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) || + def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) return networkAddMasqueradingFirewallRules(fw, def, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) return networkAddRoutingFirewallRules(fw, def, ipdef); @@ -786,7 +794,8 @@ networkRemoveIPSpecificFirewallRules(virFirewallPtr fw, virNetworkIPDefPtr ipdef) { if (def->forward.type == VIR_NETWORK_FORWARD_NAT) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) || + def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES) return networkRemoveMasqueradingFirewallRules(fw, def, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) return networkRemoveRoutingFirewallRules(fw, def, ipdef); diff --git a/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args b/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args new file mode 100644 index 0000000000..4ba4c3da30 --- /dev/null +++ b/tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args @@ -0,0 +1,228 @@ +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 67 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 67 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 68 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 68 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWO \ +--in-interface virbr0 \ +--jump REJECT +iptables \ +--table filter \ +--insert LIBVIRT_FWI \ +--out-interface virbr0 \ +--jump REJECT +iptables \ +--table filter \ +--insert LIBVIRT_FWX \ +--in-interface virbr0 \ +--out-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWO \ +--in-interface virbr0 \ +--jump REJECT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWI \ +--out-interface virbr0 \ +--jump REJECT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWX \ +--in-interface virbr0 \ +--out-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol tcp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 53 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_INP \ +--in-interface virbr0 \ +--protocol udp \ +--destination-port 547 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_OUT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 546 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWO \ +--source 192.168.122.0/24 \ +--in-interface virbr0 \ +--jump ACCEPT +iptables \ +--table filter \ +--insert LIBVIRT_FWI \ +--destination 192.168.122.0/24 \ +--out-interface virbr0 \ +--match conntrack \ +--ctstate ESTABLISHED,RELATED \ +--jump ACCEPT +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +-p udp '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +-p tcp '!' \ +--destination 192.168.122.0/24 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +--destination 255.255.255.255/32 \ +--jump RETURN +iptables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 192.168.122.0/24 \ +--destination 224.0.0.0/24 \ +--jump RETURN +ip6tables \ +--table filter \ +--insert LIBVIRT_FWO \ +--source 2001:db8:ca2:2::/64 \ +--in-interface virbr0 \ +--jump ACCEPT +ip6tables \ +--table filter \ +--insert LIBVIRT_FWI \ +--destination 2001:db8:ca2:2::/64 \ +--out-interface virbr0 \ +--match conntrack \ +--ctstate ESTABLISHED,RELATED \ +--jump ACCEPT +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +-p udp '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +-p tcp '!' \ +--destination 2001:db8:ca2:2::/64 \ +--jump MASQUERADE \ +--to-ports 1024-65535 +ip6tables \ +--table nat \ +--insert LIBVIRT_PRT \ +--source 2001:db8:ca2:2::/64 \ +--destination ffx2::/16 \ +--jump RETURN +iptables \ +--table mangle \ +--insert LIBVIRT_PRT \ +--out-interface virbr0 \ +--protocol udp \ +--destination-port 68 \ +--jump CHECKSUM \ +--checksum-fill diff --git a/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml b/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml new file mode 100644 index 0000000000..03bcc8c67d --- /dev/null +++ b/tests/networkxml2firewalldata/nat-ipv6-masquerade.xml @@ -0,0 +1,17 @@ +<network> + <name>default</name> + <bridge name="virbr0"/> + <forward> + <nat ipv6="yes"/> + </forward> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254"/> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2firewalltest.c b/tests/networkxml2firewalltest.c index 0ad5e2303b..697bfd7e03 100644 --- a/tests/networkxml2firewalltest.c +++ b/tests/networkxml2firewalltest.c @@ -173,6 +173,7 @@ mymain(void) DO_TEST("nat-many-ips"); DO_TEST("nat-no-dhcp"); DO_TEST("nat-ipv6"); + DO_TEST("nat-ipv6-masquerade"); DO_TEST("route-default");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;

On 6/10/20 12:14 AM, Laine Stump wrote:
On 6/9/20 12:17 PM, Daniel P. Berrangé wrote:
Now that we have support for IPv6 in the iptables helpers, and a new option in the XML schema, we can wire up support for it in the network driver.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver_linux.c | 23 +- .../nat-ipv6-masquerade-linux.args | 228 ++++++++++++++++++ .../nat-ipv6-masquerade.xml | 17 ++ tests/networkxml2firewalltest.c | 1 + 4 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade-linux.args create mode 100644 tests/networkxml2firewalldata/nat-ipv6-masquerade.xml
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index b0bd207250..fcb3803965 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -307,7 +307,8 @@ int networkCheckRouteCollision(virNetworkDefPtr def) return ret; } -static const char networkLocalMulticast[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv4[] = "224.0.0.0/24"; +static const char networkLocalMulticastIPv6[] = "ffx2::/16";
Once I got everything built and tried starting a network with ipv6 nat, I got this error message:
virsh net-start ipv6 error: Failed to start network ipv6 error: COMMAND_FAILED: '/usr/sbin/ip6tables -w10 -w --table nat --insert LIBVIRT_PRT --source 2001:4978:2ac:5::/80 --destination ffx2::/16 --jump RETURN' failed: ip6tables v1.8.3 (legacy): host/network `ffx2::' not found Try `ip6tables -h' or 'ip6tables --help' for more information.
Do we need to do something different for multicast traffic in the case of IPv6?
Other than that it all looks good, so
Reviewed-by: Laine Stump <laine@redhat.com>
once the problem with multicast ffx2::/16 as the destination of a rule is resolved.
Based on discussion on IRC, apparently the "x" "ffx2" in the standards docs is intended to mean "any value for this digit", but so far only "ff02" is assigned/used, so we're in agreement that we should just change ffx2 (both here and in the test results file) to ff02.
participants (3)
-
Daniel P. Berrangé
-
Laine Stump
-
Laine Stump