[libvirt] [PATCH 12/13] Turn on IPv6 support in the bridge_driver.c virtual network driver

Laine Stump laine at laine.org
Mon Dec 20 08:03:24 UTC 2010


At this point everything is already in place to make IPv6 happen, we just
need to add a few rules, remove some checks for IPv4-only, and document
the changes to the XML on the website.
---
 docs/formatnetwork.html.in  |   35 +++++++--
 src/network/bridge_driver.c |  186 ++++++++++++++++++++++++++++++++----------
 2 files changed, 169 insertions(+), 52 deletions(-)

diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 7a24518..b1b0485 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -85,8 +85,13 @@
       </dd>
       <dt><code>forward</code></dt>
       <dd>Inclusion of the <code>forward</code> element indicates that
-        the virtual network is to be connected to the physical LAN. If
-        no attributes are set, NAT forwarding will be used for connectivity.
+        the virtual network is to be connected to the physical
+        LAN. the <code>mode</code> attribute determines the method of
+        forwarding; possible selections are 'nat' and 'route'. If mode
+        is not specified, NAT forwarding will be used for
+        connectivity.  If a network has any IPv6 addresses defined,
+        even if <code>mode</code> is given as 'nat', the IPv6 traffic
+        will be forwarded using routing, since IPv6 has no concept of NAT.
         Firewall rules will allow forwarding to any other network device whether
         ethernet, wireless, dialup, or VPN. If the <code>dev</code> attribute
         is set, the firewall rules will restrict forwarding to the named
@@ -118,21 +123,37 @@
     <dl>
       <dt><code>ip</code></dt>
       <dd>The <code>address</code> attribute defines an IPv4 address in
-        dotted-decimal format, that will be configured on the bridge
+        dotted-decimal format, or an IPv6 address in standard
+        colon-separated hexadecimal format, that will be configured on
+        the bridge
         device associated with the virtual network. To the guests this
-        address will be their default route. The <code>netmask</code>
+        address will be their default route. For IPv4 addresses, the <code>netmask</code>
         attribute defines the significant bits of the network address,
-        again specified in dotted-decimal format.  <span class="since">Since 0.3.0</span>
+        again specified in dotted-decimal format.  For IPv6 addresses,
+        and as an alternate method for IPv4 addresses, you can specify
+        the significant bits of the network address with the <code>prefix</code>
+        attribute, which is an integer (for example, <code>netmask='255.255.255.0'</code>
+        could also be given as <code>prefix='24'</code>. The <code>family</code>
+        attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no
+        <code>family</code> is given, 'ipv4' is assumed. A network can have more than
+        one of each family of address defined, but only a single address can have a
+        <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0;
+        IPv6, multiple addresses on a single network, <code>family</code>, and
+        <code>prefix</code> since 0.8.7</span>
       </dd><dt><code>tftp</code></dt><dd>Immediately within
         the <code>ip</code> element there is an optional <code>tftp</code>
         element. The presence of this element and of its attribute
         <code>root</code> enables TFTP services.  The attribute specifies
-        the path to the root directory served via TFTP.
+        the path to the root directory served via TFTP. <code>tftp</code> is not
+        supported for IPv6 addresses, can only be specified on a single IPv4 address
+        per network.
         <span class="since">Since 0.7.1</span>
       </dd><dt><code>dhcp</code></dt><dd>Also within the <code>ip</code> element there is an
         optional <code>dhcp</code> element. The presence of this element
         enables DHCP services on the virtual network. It will further
-        contain one or more <code>range</code> elements.
+        contain one or more <code>range</code> elements. The
+        <code>dhcp</code> element is not supported for IPv6, and
+        is only supported on a single IP address per network for IPv4.
         <span class="since">Since 0.3.0</span>
       </dd>
       <dt><code>range</code></dt>
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index e405429..665a13f 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -825,6 +825,65 @@ networkRemoveRoutingIptablesRules(struct network_driver *driver,
     }
 }
 
+/* Add all once/network rules required for IPv6 (if any IPv6 addresses are defined) */
+static int
+networkAddGeneralIp6tablesRules(struct network_driver *driver,
+                               virNetworkObjPtr network)
+{
+
+    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
+        return 0;
+
+    /* Catch all rules to block forwarding to/from bridges */
+
+    if (iptablesAddForwardRejectOut(driver->iptables, AF_INET6,
+                                    network->def->bridge) < 0) {
+        networkReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("failed to add ip6tables rule to block outbound traffic from '%s'"),
+                           network->def->bridge);
+        goto err1;
+    }
+
+    if (iptablesAddForwardRejectIn(driver->iptables, AF_INET6,
+                                   network->def->bridge) < 0) {
+        networkReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("failed to add ip6tables rule to block inbound traffic to '%s'"),
+                           network->def->bridge);
+        goto err2;
+    }
+
+    /* Allow traffic between guests on the same bridge */
+    if (iptablesAddForwardAllowCross(driver->iptables, AF_INET6,
+                                     network->def->bridge) < 0) {
+        networkReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("failed to add ip6tables rule to allow cross bridge traffic on '%s'"),
+                           network->def->bridge);
+        goto err3;
+    }
+
+    return 0;
+
+    /* unwind in reverse order from the point of failure */
+err3:
+    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
+err2:
+    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
+err1:
+    return -1;
+}
+
+static void
+networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
+                                  virNetworkObjPtr network)
+{
+    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
+        return;
+
+    iptablesRemoveForwardAllowCross(driver->iptables, AF_INET6, network->def->bridge);
+    iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
+    iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
+}
+
 static int
 networkAddGeneralIptablesRules(struct network_driver *driver,
                                virNetworkObjPtr network)
@@ -927,6 +986,11 @@ networkAddGeneralIptablesRules(struct network_driver *driver,
         goto err8;
     }
 
+    /* add IPv6 general rules, if needed */
+    if (networkAddGeneralIp6tablesRules(driver, network) < 0) {
+        goto err8;
+    }
+
     return 0;
 
     /* unwind in reverse order from the point of failure */
@@ -957,6 +1021,8 @@ networkRemoveGeneralIptablesRules(struct network_driver *driver,
     int ii;
     virNetworkIpDefPtr ipv4def;
 
+    networkRemoveGeneralIp6tablesRules(driver, network);
+
     for (ii = 0;
          (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
          ii++) {
@@ -985,12 +1051,18 @@ networkAddIpSpecificIptablesRules(struct network_driver *driver,
                                   virNetworkObjPtr network,
                                   virNetworkIpDefPtr ipdef)
 {
-    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT &&
-        networkAddMasqueradingIptablesRules(driver, network, ipdef) < 0)
-        return -1;
-    else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE &&
-             networkAddRoutingIptablesRules(driver, network, ipdef) < 0)
-        return -1;
+    /* NB: in the case of IPv6, routing rules are added when the
+     * forward mode is NAT. This is because IPv6 has no NAT.
+     */
+
+    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
+        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
+            return networkAddMasqueradingIptablesRules(driver, network, ipdef);
+        else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+            return networkAddRoutingIptablesRules(driver, network, ipdef);
+    } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
+        return networkAddRoutingIptablesRules(driver, network, ipdef);
+    }
 
     return 0;
 }
@@ -1000,10 +1072,14 @@ networkRemoveIpSpecificIptablesRules(struct network_driver *driver,
                                      virNetworkObjPtr network,
                                      virNetworkIpDefPtr ipdef)
 {
-    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT)
-        networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
-    else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE)
+    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
+        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
+            networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
+        else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+            networkRemoveRoutingIptablesRules(driver, network, ipdef);
+    } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
         networkRemoveRoutingIptablesRules(driver, network, ipdef);
+    }
 }
 
 /* Add all rules for all ip addresses (and general rules) on a network */
@@ -1085,30 +1161,46 @@ networkEnableIpForwarding(void)
 
 #define SYSCTL_PATH "/proc/sys"
 
-static int networkDisableIPV6(virNetworkObjPtr network)
+static int
+networkSetIPv6Sysctls(virNetworkObjPtr network)
 {
     char *field = NULL;
     int ret = -1;
 
-    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/disable_ipv6", network->def->bridge) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
+    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
+        /* Only set disable_ipv6 if there are no ipv6 addresses defined for
+         * the network.
+         */
+        if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/disable_ipv6",
+                        network->def->bridge) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
 
-    if (access(field, W_OK) < 0 && errno == ENOENT) {
-        VIR_DEBUG("ipv6 appears to already be disabled on %s", network->def->bridge);
-        ret = 0;
-        goto cleanup;
-    }
+        if (access(field, W_OK) < 0 && errno == ENOENT) {
+            VIR_DEBUG("ipv6 appears to already be disabled on %s",
+                      network->def->bridge);
+            ret = 0;
+            goto cleanup;
+        }
 
-    if (virFileWriteStr(field, "1", 0) < 0) {
-        virReportSystemError(errno,
-                             _("cannot enable %s"), field);
-        goto cleanup;
+        if (virFileWriteStr(field, "1", 0) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot enable %s"), field);
+            goto cleanup;
+        }
+        VIR_FREE(field);
     }
-    VIR_FREE(field);
 
-    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra", network->def->bridge) < 0) {
+    /* The rest of the ipv6 sysctl tunables should always be set,
+     * whether or not we're using ipv6 on this bridge.
+     */
+
+    /* Prevent guests from hijacking the host network by sending out
+     * their own router advertisements.
+     */
+    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra",
+                    network->def->bridge) < 0) {
         virReportOOMError();
         goto cleanup;
     }
@@ -1120,7 +1212,11 @@ static int networkDisableIPV6(virNetworkObjPtr network)
     }
     VIR_FREE(field);
 
-    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/autoconf", network->def->bridge) < 0) {
+    /* All interfaces used as a gateway (which is what this is, by
+     * definition), must always have autoconf=0.
+     */
+    if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/autoconf",
+                    network->def->bridge) < 0) {
         virReportOOMError();
         goto cleanup;
     }
@@ -1262,7 +1358,7 @@ static int
 networkStartNetworkDaemon(struct network_driver *driver,
                           virNetworkObjPtr network)
 {
-    int ii, err, v4present = 0;
+    int ii, err, v4present = 0, v6present = 0;
     virErrorPtr save_err = NULL;
     virNetworkIpDefPtr ipdef;
 
@@ -1301,8 +1397,10 @@ networkStartNetworkDaemon(struct network_driver *driver,
         goto err1;
     }
 
-    /* Disable IPv6 on the bridge */
-    if (networkDisableIPV6(network) < 0)
+    /* Disable IPv6 on the bridge if there are no IPv6 addresses
+     * defined, and set other IPv6 sysctl tunables appropriately.
+     */
+    if (networkSetIPv6Sysctls(network) < 0)
         goto err1;
 
     /* Add "once per network" rules */
@@ -1314,6 +1412,8 @@ networkStartNetworkDaemon(struct network_driver *driver,
          ii++) {
         if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
             v4present = 1;
+        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+            v6present = 1;
 
         /* Add the IP address/netmask to the bridge */
         if (networkAddAddrToBridge(driver, network, ipdef) < 0) {
@@ -1708,9 +1808,7 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
         goto cleanup;
     }
 
-    /* we only support dhcp on one IPv4 address per defined network, and currently
-     * don't support IPv6.
-     */
+    /* We only support dhcp on one IPv4 address per defined network */
     for (ii = 0;
          (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
          ii++) {
@@ -1724,14 +1822,6 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
                     ipv4def = ipdef;
                 }
             }
-        } else {
-            /* we currently only support IPv4 */
-            networkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported address family '%s' (%d) in network definition"),
-                               ipdef->family ? ipdef->family : "unspecified",
-                               VIR_SOCKET_FAMILY(&ipdef->address));
-            goto cleanup;
-
         }
     }
     if (ipv4def) {
@@ -1756,7 +1846,8 @@ cleanup:
 static int networkUndefine(virNetworkPtr net) {
     struct network_driver *driver = net->conn->networkPrivateData;
     virNetworkObjPtr network;
-    virNetworkIpDefPtr ipv4def;
+    virNetworkIpDefPtr ipdef;
+    int v4present = 0, v6present = 0;
     int ret = -1, ii;
 
     networkDriverLock(driver);
@@ -1781,12 +1872,17 @@ static int networkUndefine(virNetworkPtr net) {
 
     /* we only support dhcp on one IPv4 address per defined network */
     for (ii = 0;
-         (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
+         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
          ii++) {
-        if (ipv4def->nranges || ipv4def->nhosts)
-            break;
+        if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
+            if (ipdef->nranges || ipdef->nhosts)
+                v4present = 1;
+        } else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6)) {
+            v6present = 1;
+        }
     }
-    if (ipv4def) {
+
+    if (v4present) {
         dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
         if (dctx == NULL)
             goto cleanup;
-- 
1.7.3.4




More information about the libvir-list mailing list