[libvirt] [PATCHv2 09/13] Change virtual network XML parsing/formatting to support IPv6

Laine Stump laine at laine.org
Wed Dec 22 18:58:08 UTC 2010


This commit adds support for IPv6 parsing and formatting to the
virtual network XML parser, including moving around data definitions
to allow for multiple <ip> elements on a single network, but only
changes the consumers of this API to accomodate for the changes in
API/structure, not to add any actual IPv6 functionality. That will
come in a later patch - this patch attempts to maintain the same final
functionality in both drivers that use the network XML parser - vbox
and "bridge" (the Linux bridge-based driver used by the qemu
hypervisor driver).

* src/libvirt_private.syms: Add new private API functions.
* src/conf/network_conf.[ch]: Change C data structure and
  parsing/formatting.
* src/network/bridge_driver.c: Update to use new parser/formatter.
* src/vbox/vbox_tmpl.c: update to use new parser/formatter
* docs/schemas/network.rng: changes to the schema -
  * there can now be more than one <ip> element.
  * ip address is now an ip-addr (ipv4 or ipv6) rather than ipv4-addr
  * new optional "prefix" attribute that can be used in place of "netmask"
  * new optional "family" attribute - "ipv4" or "ipv6"
    (will default to ipv4)
  * define data types for the above
* tests/networkxml2xml(in|out)/nat-network.xml: add multiple <ip> elements
  (including IPv6) to a single network definition to verify they are being
  correctly parsed and formatted.
---
V2 Changes:

* prefix in virNetworkIpDef struct is now unsigned int rather than int.
* caught a memory leak in an error patch of virNetworkIPParseXML().

 docs/schemas/network.rng                |   41 +++-
 src/conf/network_conf.c                 |  453 +++++++++++++++++++++----------
 src/conf/network_conf.h                 |   50 +++-
 src/libvirt_private.syms                |    5 +-
 src/network/bridge_driver.c             |  248 ++++++++++--------
 src/vbox/vbox_tmpl.c                    |   81 ++++--
 tests/networkxml2xmlin/nat-network.xml  |    8 +
 tests/networkxml2xmlout/nat-network.xml |    8 +
 8 files changed, 590 insertions(+), 304 deletions(-)

diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index ac13af2..bc34ddc 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -80,15 +80,21 @@
         </optional>
 
         <!-- <ip> element -->
-        <optional>
+        <zeroOrMore>
           <!-- The IP element sets up NAT'ing and an optional DHCP server
                local to the host. -->
           <element name="ip">
             <optional>
-              <attribute name="address"><ref name="ipv4-addr"/></attribute>
+              <attribute name="address"><ref name="ip-addr"/></attribute>
+            </optional>
+            <optional>
+              <choice>
+                <attribute name="netmask"><ref name="ipv4-addr"/></attribute>
+                <attribute name="prefix"><ref name="ip-prefix"/></attribute>
+              </choice>
             </optional>
             <optional>
-              <attribute name="netmask"><ref name="ipv4-addr"/></attribute>
+              <attribute name="family"><ref name="addr-family"/></attribute>
             </optional>
             <optional>
               <element name="tftp">
@@ -123,7 +129,7 @@
               </element>
             </optional>
           </element>
-        </optional>
+        </zeroOrMore>
       </interleave>
     </element>
   </define>
@@ -135,6 +141,33 @@
     </data>
   </define>
 
+  <!-- Based on http://blog.mes-stats.fr/2008/10/09/regex-ipv4-et-ipv6 -->
+  <define name='ipv6-addr'>
+    <data type='string'>
+      <!-- To understand this better, take apart the toplevel '|'s -->
+      <param name="pattern">(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|(([0-9A-Fa-f]{1,4}:){0,5}:((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|(::([0-9A-Fa-f]{1,4}:){0,5}((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:)</param>
+    </data>
+  </define>
+
+  <define name='ip-addr'>
+    <choice>
+      <ref name='ipv4-addr'/>
+      <ref name='ipv6-addr'/>
+    </choice>
+  </define>
+
+  <define name='ip-prefix'>
+    <data type='unsignedInt'>
+      <param name="maxInclusive">128</param>
+    </data>
+  </define>
+
+  <define name='addr-family'>
+    <data type='string'>
+      <param name="pattern">(ipv4)|(ipv6)</param>
+    </data>
+  </define>
+
   <!-- a 6 byte MAC address in ASCII-hex format, eg "12:34:56:78:9A:BC" -->
   <define name='mac-addr'>
     <data type='string'>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index b17c708..86442ce 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -87,9 +87,26 @@ virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets,
 }
 
 
+static void virNetworkIpDefClear(virNetworkIpDefPtr def)
+{
+    int ii;
+
+    VIR_FREE(def->family);
+    VIR_FREE(def->ranges);
+
+    for (ii = 0 ; ii < def->nhosts && def->hosts ; ii++) {
+        VIR_FREE(def->hosts[ii].mac);
+        VIR_FREE(def->hosts[ii].name);
+    }
+
+    VIR_FREE(def->hosts);
+    VIR_FREE(def->tftproot);
+    VIR_FREE(def->bootfile);
+}
+
 void virNetworkDefFree(virNetworkDefPtr def)
 {
-    int i;
+    int ii;
 
     if (!def)
         return;
@@ -99,16 +116,10 @@ void virNetworkDefFree(virNetworkDefPtr def)
     VIR_FREE(def->forwardDev);
     VIR_FREE(def->domain);
 
-    VIR_FREE(def->ranges);
-
-    for (i = 0 ; i < def->nhosts && def->hosts ; i++) {
-        VIR_FREE(def->hosts[i].mac);
-        VIR_FREE(def->hosts[i].name);
+    for (ii = 0 ; ii < def->nips && def->ips ; ii++) {
+        virNetworkIpDefClear(&def->ips[ii]);
     }
-    VIR_FREE(def->hosts);
-
-    VIR_FREE(def->tftproot);
-    VIR_FREE(def->bootfile);
+    VIR_FREE(def->ips);
 
     VIR_FREE(def);
 }
@@ -207,21 +218,48 @@ void virNetworkRemoveInactive(virNetworkObjListPtr nets,
     }
 }
 
+/* return ips[index], or NULL if there aren't enough ips */
+virNetworkIpDefPtr
+virNetworkDefGetIpByIndex(const virNetworkDefPtr def,
+                          int family, int n)
+{
+    int ii;
+
+    if (!def->ips || n >= def->nips)
+        return NULL;
+
+    if (family == AF_UNSPEC) {
+        return &def->ips[n];
+    }
+
+    /* find the nth ip of type "family" */
+    for (ii = 0; ii < def->nips; ii++) {
+        if (VIR_SOCKET_IS_FAMILY(&def->ips[ii].address, family)
+            && (n-- <= 0)) {
+            return &def->ips[ii];
+        }
+    }
+    /* failed to find enough of the right family */
+    return NULL;
+}
+
 /* return number of 1 bits in netmask for the network's ipAddress,
  * or -1 on error
  */
-int virNetworkDefPrefix(const virNetworkDefPtr def)
+int virNetworkIpDefPrefix(const virNetworkIpDefPtr def)
 {
-    if (VIR_SOCKET_HAS_ADDR(&def->netmask)) {
+    if (def->prefix > 0) {
+        return def->prefix;
+    } else if (VIR_SOCKET_HAS_ADDR(&def->netmask)) {
         return virSocketGetNumNetmaskBits(&def->netmask);
-    } else if (VIR_SOCKET_IS_FAMILY(&def->ipAddress, AF_INET)) {
+    } else if (VIR_SOCKET_IS_FAMILY(&def->address, AF_INET)) {
         /* Return the natural prefix for the network's ip address.
          * On Linux we could use the IN_CLASSx() macros, but those
          * aren't guaranteed on all platforms, so we just deal with
          * the bits ourselves.
          */
         const unsigned char *octets
-            = (const unsigned char *)(&def->ipAddress.data.inet4.sin_addr.s_addr);
+            = (const unsigned char *)(&def->address.data.inet4.sin_addr.s_addr);
         if ((octets[0] & 0x80) == 0) {
             /* Class A network */
             return 8;
@@ -233,6 +271,8 @@ int virNetworkDefPrefix(const virNetworkDefPtr def)
             return 24;
         }
         return -1;
+    } else if (VIR_SOCKET_IS_FAMILY(&def->address, AF_INET6)) {
+        return 64;
     }
     return -1;
 }
@@ -241,22 +281,23 @@ int virNetworkDefPrefix(const virNetworkDefPtr def)
  * definition, based on either the definition's netmask, or its
  * prefix. Return -1 on error (and set the netmask family to AF_UNSPEC)
  */
-int virNetworkDefNetmask(const virNetworkDefPtr def,
-                         virSocketAddrPtr netmask)
+int virNetworkIpDefNetmask(const virNetworkIpDefPtr def,
+                           virSocketAddrPtr netmask)
 {
     if (VIR_SOCKET_IS_FAMILY(&def->netmask, AF_INET)) {
         *netmask = def->netmask;
         return 0;
     }
 
-    return virSocketAddrPrefixToNetmask(virNetworkDefPrefix(def), netmask,
-                                        VIR_SOCKET_FAMILY(&def->ipAddress));
+    return virSocketAddrPrefixToNetmask(virNetworkIpDefPrefix(def), netmask,
+                                        VIR_SOCKET_FAMILY(&def->address));
 }
 
 
 static int
-virNetworkDHCPRangeDefParseXML(virNetworkDefPtr def,
-                               xmlNodePtr node) {
+virNetworkDHCPRangeDefParseXML(virNetworkIpDefPtr def,
+                               xmlNodePtr node)
+{
 
     xmlNodePtr cur;
 
@@ -390,33 +431,147 @@ virNetworkDHCPRangeDefParseXML(virNetworkDefPtr def,
 }
 
 static int
-virNetworkIPParseXML(virNetworkDefPtr def,
-                     xmlNodePtr node) {
-    xmlNodePtr cur;
+virNetworkIPParseXML(const char *networkName,
+                     virNetworkIpDefPtr def,
+                     xmlNodePtr node,
+                     xmlXPathContextPtr ctxt)
+{
+    /*
+     * virNetworkIpDef object is already allocated as part of an array.
+     * On failure clear it out, but don't free it.
+     */
 
-    cur = node->children;
-    while (cur != NULL) {
-        if (cur->type == XML_ELEMENT_NODE &&
-            xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
-            int result = virNetworkDHCPRangeDefParseXML(def, cur);
-            if (result)
-                return result;
+    xmlNodePtr cur, save;
+    char *address = NULL, *netmask = NULL;
+    unsigned long prefix;
+    int result = -1;
+
+    save = ctxt->node;
+    ctxt->node = node;
+
+    /* grab raw data from XML */
+    def->family = virXPathString("string(./@family)", ctxt);
+    address = virXPathString("string(./@address)", ctxt);
+    if (virXPathULong("string(./@prefix)", ctxt, &prefix) < 0)
+        def->prefix = 0;
+    else
+        def->prefix = prefix;
+
+    netmask = virXPathString("string(./@netmask)", ctxt);
+
+    if (address) {
+        if (virSocketParseAddr(address, &def->address, AF_UNSPEC) < 0) {
+            virNetworkReportError(VIR_ERR_XML_ERROR,
+                                  _("Bad address '%s' in definition of network '%s'"),
+                                  address, networkName);
+            goto error;
+        }
 
-        } else if (cur->type == XML_ELEMENT_NODE &&
-            xmlStrEqual(cur->name, BAD_CAST "tftp")) {
-            char *root;
+    }
 
-            if (!(root = virXMLPropString(cur, "root"))) {
-                cur = cur->next;
-                continue;
+    /* validate family vs. address */
+    if (def->family == NULL) {
+        if (!(VIR_SOCKET_IS_FAMILY(&def->address, AF_INET) ||
+              VIR_SOCKET_IS_FAMILY(&def->address, AF_UNSPEC))) {
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("no family specified for non-IPv4 address address '%s' in network '%s'"),
+                                  address, networkName);
+            goto error;
+        }
+    } else if (STREQ(def->family, "ipv4")) {
+        if (!VIR_SOCKET_IS_FAMILY(&def->address, AF_INET)) {
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("family 'ipv4' specified for non-IPv4 address '%s' in network '%s'"),
+                                  address, networkName);
+            goto error;
+        }
+    } else if (STREQ(def->family, "ipv6")) {
+        if (!VIR_SOCKET_IS_FAMILY(&def->address, AF_INET6)) {
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("family 'ipv6' specified for non-IPv6 address '%s' in network '%s'"),
+                                  address, networkName);
+            goto error;
+        }
+    } else {
+        virNetworkReportError(VIR_ERR_XML_ERROR,
+                              _("Unrecognized family '%s' in definition of network '%s'"),
+                              def->family, networkName);
+        goto error;
+    }
+
+    /* parse/validate netmask */
+    if (netmask) {
+        if (address == NULL) {
+            /* netmask is meaningless without an address */
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("netmask specified without address in network '%s'"),
+                                  networkName);
+            goto error;
+        }
+
+        if (!VIR_SOCKET_IS_FAMILY(&def->address, AF_INET)) {
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("netmask not supported for address '%s' in network '%s' (IPv4 only)"),
+                                  address, networkName);
+            goto error;
+        }
+
+        if (def->prefix > 0) {
+            /* can't have both netmask and prefix at the same time */
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("network '%s' has prefix='%u' but no address"),
+                                  networkName, def->prefix);
+            goto error;
+        }
+
+        if (virSocketParseAddr(netmask, &def->netmask, AF_UNSPEC) < 0)
+            goto error;
+
+        if (!VIR_SOCKET_IS_FAMILY(&def->netmask, AF_INET)) {
+            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                  _("network '%s' has invalid netmask '%s' for address '%s' (both must be IPv4)"),
+                                  networkName, netmask, address);
+            goto error;
+        }
+    }
+
+    if (VIR_SOCKET_IS_FAMILY(&def->address, AF_INET)) {
+        /* parse IPv4-related info */
+        cur = node->children;
+        while (cur != NULL) {
+            if (cur->type == XML_ELEMENT_NODE &&
+                xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
+                result = virNetworkDHCPRangeDefParseXML(def, cur);
+                if (result)
+                    goto error;
+
+            } else if (cur->type == XML_ELEMENT_NODE &&
+                       xmlStrEqual(cur->name, BAD_CAST "tftp")) {
+                char *root;
+
+                if (!(root = virXMLPropString(cur, "root"))) {
+                    cur = cur->next;
+                    continue;
+                }
+
+                def->tftproot = (char *)root;
             }
 
-            def->tftproot = root;
+            cur = cur->next;
         }
+    }
 
-        cur = cur->next;
+    result = 0;
+
+error:
+    if (result < 0) {
+        virNetworkIpDefClear(def);
     }
-    return 0;
+    VIR_FREE(address);
+    VIR_FREE(netmask);
+
+    ctxt->node = save;
+    return result;
 }
 
 static virNetworkDefPtr
@@ -424,8 +579,8 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
 {
     virNetworkDefPtr def;
     char *tmp;
-    char *ipAddress;
-    char *netmask;
+    xmlNodePtr *ipNodes = NULL;
+    int nIps;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
@@ -469,44 +624,32 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
     if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0)
         def->delay = 0;
 
-    ipAddress = virXPathString("string(./ip[1]/@address)", ctxt);
-    if (ipAddress) {
-        xmlNodePtr ip;
-
-        if (virSocketParseAddr(ipAddress, &def->ipAddress, AF_UNSPEC) < 0)
-            goto error;
+    nIps = virXPathNodeSet("./ip", ctxt, &ipNodes);
+    if (nIps > 0) {
+        int ii;
 
-        /* XXX someday we want IPv6, so will need to relax this */
-        if (!VIR_SOCKET_IS_FAMILY(&def->ipAddress, AF_INET)) {
-            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                  "%s", _("Only IPv4 addresses are supported"));
+        /* allocate array to hold all the addrs */
+        if (VIR_ALLOC_N(def->ips, nIps) < 0) {
+            virReportOOMError();
             goto error;
         }
-
-        if ((ip = virXPathNode("./ip[1]", ctxt)) &&
-            virNetworkIPParseXML(def, ip) < 0)
-            goto error;
-    }
-    VIR_FREE(ipAddress);
-
-    netmask = virXPathString("string(./ip[1]/@netmask)", ctxt);
-    if (netmask) {
-
-        if (virSocketParseAddr(netmask, &def->netmask, AF_UNSPEC) < 0)
-            goto error;
-
-        /* XXX someday we want IPv6, so will need to relax this */
-        if (!VIR_SOCKET_IS_FAMILY(&def->netmask, AF_INET)) {
-            virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                  "%s", _("Only IPv4 addresses are supported"));
-            goto error;
+        /* parse each addr */
+        for (ii = 0; ii < nIps; ii++) {
+            int ret = virNetworkIPParseXML(def->name, &def->ips[ii],
+                                           ipNodes[ii], ctxt);
+            if (ret < 0)
+                goto error;
+            def->nips++;
         }
     }
-    VIR_FREE(netmask);
-
 
     /* IPv4 forwarding setup */
     if (virXPathBoolean("count(./forward) > 0", ctxt)) {
+        if (def->nips == 0) {
+            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("Forwarding requested, but no IP address provided"));
+            goto error;
+        }
         tmp = virXPathString("string(./forward[1]/@mode)", ctxt);
         if (tmp) {
             if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
@@ -585,11 +728,101 @@ cleanup:
     return def;
 }
 
+static int
+virNetworkIpDefFormat(virBufferPtr buf,
+                      const virNetworkIpDefPtr def)
+{
+    int result = -1;
+
+    virBufferAddLit(buf, "  <ip");
+
+    if (def->family) {
+        virBufferVSprintf(buf, " family='%s'", def->family);
+    }
+    if (VIR_SOCKET_HAS_ADDR(&def->address)) {
+        char *addr = virSocketFormatAddr(&def->address);
+        if (!addr)
+            goto error;
+        virBufferVSprintf(buf, " address='%s'", addr);
+        VIR_FREE(addr);
+    }
+    if (VIR_SOCKET_HAS_ADDR(&def->netmask)) {
+        char *addr = virSocketFormatAddr(&def->netmask);
+        if (!addr)
+            goto error;
+        virBufferVSprintf(buf, " netmask='%s'", addr);
+        VIR_FREE(addr);
+    }
+    if (def->prefix > 0) {
+        virBufferVSprintf(buf," prefix='%u'", def->prefix);
+    }
+    virBufferAddLit(buf, ">\n");
+
+    if (def->tftproot) {
+        virBufferEscapeString(buf, "    <tftp root='%s' />\n",
+                              def->tftproot);
+    }
+    if ((def->nranges || def->nhosts)) {
+        int ii;
+        virBufferAddLit(buf, "    <dhcp>\n");
+        for (ii = 0 ; ii < def->nranges ; ii++) {
+            char *saddr = virSocketFormatAddr(&def->ranges[ii].start);
+            if (!saddr)
+                goto error;
+            char *eaddr = virSocketFormatAddr(&def->ranges[ii].end);
+            if (!eaddr) {
+                VIR_FREE(saddr);
+                goto error;
+            }
+            virBufferVSprintf(buf, "      <range start='%s' end='%s' />\n",
+                              saddr, eaddr);
+            VIR_FREE(saddr);
+            VIR_FREE(eaddr);
+        }
+        for (ii = 0 ; ii < def->nhosts ; ii++) {
+            virBufferAddLit(buf, "      <host ");
+            if (def->hosts[ii].mac)
+                virBufferVSprintf(buf, "mac='%s' ", def->hosts[ii].mac);
+            if (def->hosts[ii].name)
+                virBufferVSprintf(buf, "name='%s' ", def->hosts[ii].name);
+            if (VIR_SOCKET_HAS_ADDR(&def->hosts[ii].ip)) {
+                char *ipaddr = virSocketFormatAddr(&def->hosts[ii].ip);
+                if (!ipaddr)
+                    goto error;
+                virBufferVSprintf(buf, "ip='%s' ", ipaddr);
+                VIR_FREE(ipaddr);
+            }
+            virBufferAddLit(buf, "/>\n");
+        }
+        if (def->bootfile) {
+            virBufferEscapeString(buf, "      <bootp file='%s' ",
+                                  def->bootfile);
+            if (VIR_SOCKET_HAS_ADDR(&def->bootserver)) {
+                char *ipaddr = virSocketFormatAddr(&def->bootserver);
+                if (!ipaddr)
+                    goto error;
+                virBufferEscapeString(buf, "server='%s' ", ipaddr);
+                VIR_FREE(ipaddr);
+            }
+            virBufferAddLit(buf, "/>\n");
+        }
+
+        virBufferAddLit(buf, "    </dhcp>\n");
+    }
+
+    virBufferAddLit(buf, "  </ip>\n");
+
+    result = 0;
+error:
+    return result;
+}
+
 char *virNetworkDefFormat(const virNetworkDefPtr def)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
     unsigned char *uuid;
     char uuidstr[VIR_UUID_STRING_BUFLEN];
+    int ii;
 
     virBufferAddLit(&buf, "<network>\n");
     virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);
@@ -621,81 +854,9 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
     if (def->domain)
         virBufferVSprintf(&buf, "  <domain name='%s'/>\n", def->domain);
 
-    if (VIR_SOCKET_HAS_ADDR(&def->ipAddress) ||
-        VIR_SOCKET_HAS_ADDR(&def->netmask)) {
-        virBufferAddLit(&buf, "  <ip");
-
-        if (VIR_SOCKET_HAS_ADDR(&def->ipAddress)) {
-            char *addr = virSocketFormatAddr(&def->ipAddress);
-            if (!addr)
-                goto error;
-            virBufferVSprintf(&buf, " address='%s'", addr);
-            VIR_FREE(addr);
-        }
-
-        if (VIR_SOCKET_HAS_ADDR(&def->netmask)) {
-            char *addr = virSocketFormatAddr(&def->netmask);
-            if (!addr)
-                goto error;
-            virBufferVSprintf(&buf, " netmask='%s'", addr);
-            VIR_FREE(addr);
-        }
-
-        virBufferAddLit(&buf, ">\n");
-
-        if (def->tftproot) {
-            virBufferEscapeString(&buf, "    <tftp root='%s' />\n",
-                                  def->tftproot);
-        }
-        if ((def->nranges || def->nhosts)) {
-            int i;
-            virBufferAddLit(&buf, "    <dhcp>\n");
-            for (i = 0 ; i < def->nranges ; i++) {
-                char *saddr = virSocketFormatAddr(&def->ranges[i].start);
-                if (!saddr)
-                    goto error;
-                char *eaddr = virSocketFormatAddr(&def->ranges[i].end);
-                if (!eaddr) {
-                    VIR_FREE(saddr);
-                    goto error;
-                }
-                virBufferVSprintf(&buf, "      <range start='%s' end='%s' />\n",
-                                  saddr, eaddr);
-                VIR_FREE(saddr);
-                VIR_FREE(eaddr);
-            }
-            for (i = 0 ; i < def->nhosts ; i++) {
-                virBufferAddLit(&buf, "      <host ");
-                if (def->hosts[i].mac)
-                    virBufferVSprintf(&buf, "mac='%s' ", def->hosts[i].mac);
-                if (def->hosts[i].name)
-                    virBufferVSprintf(&buf, "name='%s' ", def->hosts[i].name);
-                if (VIR_SOCKET_HAS_ADDR(&def->hosts[i].ip)) {
-                    char *ipaddr = virSocketFormatAddr(&def->hosts[i].ip);
-                    if (!ipaddr)
-                        goto error;
-                    virBufferVSprintf(&buf, "ip='%s' ", ipaddr);
-                    VIR_FREE(ipaddr);
-                }
-                virBufferAddLit(&buf, "/>\n");
-            }
-            if (def->bootfile) {
-                virBufferEscapeString(&buf, "      <bootp file='%s' ",
-                                      def->bootfile);
-                if (VIR_SOCKET_HAS_ADDR(&def->bootserver)) {
-                    char *ipaddr = virSocketFormatAddr(&def->bootserver);
-                    if (!ipaddr)
-                        goto error;
-                    virBufferEscapeString(&buf, "server='%s' ", ipaddr);
-                    VIR_FREE(ipaddr);
-                }
-                virBufferAddLit(&buf, "/>\n");
-            }
-
-            virBufferAddLit(&buf, "    </dhcp>\n");
-        }
-
-        virBufferAddLit(&buf, "  </ip>\n");
+    for (ii = 0; ii < def->nips; ii++) {
+        if (virNetworkIpDefFormat(&buf, &def->ips[ii]) < 0)
+            goto error;
     }
 
     virBufferAddLit(&buf, "</network>\n");
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index a922d28..a51794d 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -56,6 +56,33 @@ struct _virNetworkDHCPHostDef {
     virSocketAddr ip;
 };
 
+typedef struct _virNetworkIpDef virNetworkIpDef;
+typedef virNetworkIpDef *virNetworkIpDefPtr;
+struct _virNetworkIpDef {
+    char *family;               /* ipv4 or ipv6 - default is ipv4 */
+    virSocketAddr address;      /* Bridge IP address */
+
+    /* The first two items below are taken directly from the XML (one
+     * or the other is given, but not both) and the 3rd is derived
+     * from the first two. When formatting XML, always use netMasktStr
+     * if it's non-NULL, but when using netmask in other code, use
+     * netmask, as it will automatically take into account prefix or
+     * natural netmasks (when no netmask or prefix is specified).
+     */
+    unsigned int prefix;        /* ipv6 - only prefix allowed */
+    virSocketAddr netmask;      /* ipv4 - either netmask or prefix specified */
+
+    unsigned int nranges;        /* Zero or more dhcp ranges */
+    virNetworkDHCPRangeDefPtr ranges;
+
+    unsigned int nhosts;         /* Zero or more dhcp hosts */
+    virNetworkDHCPHostDefPtr hosts;
+
+    char *tftproot;
+    char *bootfile;
+    virSocketAddr bootserver;
+   };
+
 typedef struct _virNetworkDef virNetworkDef;
 typedef virNetworkDef *virNetworkDefPtr;
 struct _virNetworkDef {
@@ -70,18 +97,8 @@ struct _virNetworkDef {
     int forwardType;    /* One of virNetworkForwardType constants */
     char *forwardDev;   /* Destination device for forwarding */
 
-    virSocketAddr ipAddress;    /* Bridge IP address */
-    virSocketAddr netmask;
-
-    unsigned int nranges;        /* Zero or more dhcp ranges */
-    virNetworkDHCPRangeDefPtr ranges;
-
-    unsigned int nhosts;         /* Zero or more dhcp hosts */
-    virNetworkDHCPHostDefPtr hosts;
-
-    char *tftproot;
-    char *bootfile;
-    virSocketAddr bootserver;
+    int nips;
+    virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
 };
 
 typedef struct _virNetworkObj virNetworkObj;
@@ -133,9 +150,12 @@ virNetworkDefPtr virNetworkDefParseNode(xmlDocPtr xml,
 
 char *virNetworkDefFormat(const virNetworkDefPtr def);
 
-int virNetworkDefPrefix(const virNetworkDefPtr def);
-int virNetworkDefNetmask(const virNetworkDefPtr def,
-                         virSocketAddrPtr netmask);
+virNetworkIpDefPtr
+virNetworkDefGetIpByIndex(const virNetworkDefPtr def,
+                          int family, int n);
+int virNetworkIpDefPrefix(const virNetworkIpDefPtr def);
+int virNetworkIpDefNetmask(const virNetworkIpDefPtr def,
+                           virSocketAddrPtr netmask);
 
 int virNetworkSaveXML(const char *configDir,
                       virNetworkDefPtr def,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d9ba7b1..6cb8270 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -578,15 +578,16 @@ virNetworkAssignDef;
 virNetworkConfigFile;
 virNetworkDefFormat;
 virNetworkDefFree;
-virNetworkDefNetmask;
+virNetworkDefGetIpByIndex;
 virNetworkDefParseFile;
 virNetworkDefParseNode;
 virNetworkDefParseString;
-virNetworkDefPrefix;
 virNetworkDeleteConfig;
 virNetworkFindByName;
 virNetworkFindByUUID;
 virNetworkLoadAllConfigs;
+virNetworkIpDefNetmask;
+virNetworkIpDefPrefix;
 virNetworkObjIsDuplicate;
 virNetworkObjListFree;
 virNetworkObjLock;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index ba9ff96..0f8b050 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -145,8 +145,7 @@ networkFindActiveConfigs(struct network_driver *driver) {
             obj->active = 1;
 
             /* Finally try and read dnsmasq pid if any */
-            if ((VIR_SOCKET_HAS_ADDR(&obj->def->ipAddress) ||
-                 obj->def->nranges) &&
+            if (obj->def->ips && (obj->def->nips > 0) &&
                 virFileReadPid(NETWORK_PID_DIR, obj->def->name,
                                &obj->dnsmasqPid) == 0) {
 
@@ -366,7 +365,7 @@ networkShutdown(void) {
 
 
 static int
-networkSaveDnsmasqHostsfile(virNetworkObjPtr network,
+networkSaveDnsmasqHostsfile(virNetworkIpDefPtr ipdef,
                             dnsmasqContext *dctx,
                             bool force)
 {
@@ -375,8 +374,8 @@ networkSaveDnsmasqHostsfile(virNetworkObjPtr network,
     if (! force && virFileExists(dctx->hostsfile->path))
         return 0;
 
-    for (i = 0 ; i < network->def->nhosts ; i++) {
-        virNetworkDHCPHostDefPtr host = &(network->def->hosts[i]);
+    for (i = 0; i < ipdef->nhosts; i++) {
+        virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
         if ((host->mac) && VIR_SOCKET_HAS_ADDR(&host->ip))
             dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name);
     }
@@ -390,13 +389,14 @@ networkSaveDnsmasqHostsfile(virNetworkObjPtr network,
 
 static int
 networkBuildDnsmasqArgv(virNetworkObjPtr network,
+                        virNetworkIpDefPtr ipdef,
                         const char *pidfile,
                         virCommandPtr cmd) {
     int r, ret = -1;
     int nbleases = 0;
     char *bridgeaddr;
 
-    if (!(bridgeaddr = virSocketFormatAddr(&network->def->ipAddress)))
+    if (!(bridgeaddr = virSocketFormatAddr(&ipdef->address)))
         goto cleanup;
     /*
      * NB, be careful about syntax for dnsmasq options in long format.
@@ -438,18 +438,18 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
      * clearly not practical
      *
      * virCommandAddArg(cmd, "--interface");
-     * virCommandAddArg(cmd, network->def->bridge);
+     * virCommandAddArg(cmd, ipdef->bridge);
      */
     virCommandAddArgList(cmd,
                          "--listen-address", bridgeaddr,
                          "--except-interface", "lo",
                          NULL);
 
-    for (r = 0 ; r < network->def->nranges ; r++) {
-        char *saddr = virSocketFormatAddr(&network->def->ranges[r].start);
+    for (r = 0 ; r < ipdef->nranges ; r++) {
+        char *saddr = virSocketFormatAddr(&ipdef->ranges[r].start);
         if (!saddr)
             goto cleanup;
-        char *eaddr = virSocketFormatAddr(&network->def->ranges[r].end);
+        char *eaddr = virSocketFormatAddr(&ipdef->ranges[r].end);
         if (!eaddr) {
             VIR_FREE(saddr);
             goto cleanup;
@@ -458,8 +458,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
         virCommandAddArgFormat(cmd, "%s,%s", saddr, eaddr);
         VIR_FREE(saddr);
         VIR_FREE(eaddr);
-        nbleases += virSocketGetRange(&network->def->ranges[r].start,
-                                      &network->def->ranges[r].end);
+        nbleases += virSocketGetRange(&ipdef->ranges[r].start,
+                                      &ipdef->ranges[r].end);
     }
 
     /*
@@ -467,19 +467,19 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
      * we have to add a special --dhcp-range option to enable the service in
      * dnsmasq.
      */
-    if (!network->def->nranges && network->def->nhosts) {
+    if (!ipdef->nranges && ipdef->nhosts) {
         virCommandAddArg(cmd, "--dhcp-range");
         virCommandAddArgFormat(cmd, "%s,static", bridgeaddr);
     }
 
-    if (network->def->nranges > 0) {
+    if (ipdef->nranges > 0) {
         virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases);
     }
 
-    if (network->def->nranges || network->def->nhosts)
+    if (ipdef->nranges || ipdef->nhosts)
         virCommandAddArg(cmd, "--dhcp-no-override");
 
-    if (network->def->nhosts > 0) {
+    if (ipdef->nhosts > 0) {
         dnsmasqContext *dctx = dnsmasqContextNew(network->def->name,
                                                  DNSMASQ_STATE_DIR);
         if (dctx == NULL) {
@@ -487,31 +487,30 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
             goto cleanup;
         }
 
-        if (networkSaveDnsmasqHostsfile(network, dctx, false) < 0) {
+        if (networkSaveDnsmasqHostsfile(ipdef, dctx, false) < 0) {
             virCommandAddArgPair(cmd, "--dhcp-hostsfile",
                                  dctx->hostsfile->path);
         }
         dnsmasqContextFree(dctx);
     }
 
-    if (network->def->tftproot) {
+    if (ipdef->tftproot) {
         virCommandAddArgList(cmd, "--enable-tftp",
-                             "--tftp-root", network->def->tftproot,
+                             "--tftp-root", ipdef->tftproot,
                              NULL);
     }
-    if (network->def->bootfile) {
-
+    if (ipdef->bootfile) {
         virCommandAddArg(cmd, "--dhcp-boot");
-        if (VIR_SOCKET_HAS_ADDR(&network->def->bootserver)) {
-            char *bootserver = virSocketFormatAddr(&network->def->bootserver);
+        if (VIR_SOCKET_HAS_ADDR(&ipdef->bootserver)) {
+            char *bootserver = virSocketFormatAddr(&ipdef->bootserver);
 
             if (!bootserver)
                 goto cleanup;
             virCommandAddArgFormat(cmd, "%s%s%s",
-                               network->def->bootfile, ",,", bootserver);
+                               ipdef->bootfile, ",,", bootserver);
             VIR_FREE(bootserver);
         } else {
-            virCommandAddArg(cmd, network->def->bootfile);
+            virCommandAddArg(cmd, ipdef->bootfile);
         }
     }
 
@@ -521,9 +520,9 @@ cleanup:
     return ret;
 }
 
-
 static int
-dhcpStartDhcpDaemon(virNetworkObjPtr network)
+dhcpStartDhcpDaemon(virNetworkObjPtr network,
+                    virNetworkIpDefPtr ipdef)
 {
     virCommandPtr cmd = NULL;
     char *pidfile = NULL;
@@ -531,7 +530,7 @@ dhcpStartDhcpDaemon(virNetworkObjPtr network)
 
     network->dnsmasqPid = -1;
 
-    if (!VIR_SOCKET_IS_FAMILY(&network->def->ipAddress, AF_INET)) {
+    if (!VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
                            "%s", _("cannot start dhcp daemon without IPv4 address for server"));
         goto cleanup;
@@ -556,7 +555,7 @@ dhcpStartDhcpDaemon(virNetworkObjPtr network)
     }
 
     cmd = virCommandNew(DNSMASQ);
-    if (networkBuildDnsmasqArgv(network, pidfile, cmd) < 0) {
+    if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd) < 0) {
         goto cleanup;
     }
 
@@ -584,8 +583,10 @@ cleanup:
 
 static int
 networkAddMasqueradingIptablesRules(struct network_driver *driver,
-                                    virNetworkObjPtr network) {
-    int prefix = virNetworkDefPrefix(network->def);
+                                      virNetworkObjPtr network,
+                                      virNetworkIpDefPtr ipdef)
+{
+    int prefix = virNetworkIpDefPrefix(ipdef);
 
     if (prefix < 0) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
@@ -596,7 +597,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
     /* allow forwarding packets from the bridge interface */
     if (iptablesAddForwardAllowOut(driver->iptables,
-                                   &network->def->ipAddress,
+                                   &ipdef->address,
                                    prefix,
                                    network->def->bridge,
                                    network->def->forwardDev) < 0) {
@@ -608,7 +609,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
     /* allow forwarding packets to the bridge interface if they are part of an existing connection */
     if (iptablesAddForwardAllowRelatedIn(driver->iptables,
-                                         &network->def->ipAddress,
+                                         &ipdef->address,
                                          prefix,
                                          network->def->bridge,
                                          network->def->forwardDev) < 0) {
@@ -643,7 +644,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
     /* First the generic masquerade rule for other protocols */
     if (iptablesAddForwardMasquerade(driver->iptables,
-                                     &network->def->ipAddress,
+                                     &ipdef->address,
                                      prefix,
                                      network->def->forwardDev,
                                      NULL) < 0) {
@@ -655,7 +656,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
     /* UDP with a source port restriction */
     if (iptablesAddForwardMasquerade(driver->iptables,
-                                     &network->def->ipAddress,
+                                     &ipdef->address,
                                      prefix,
                                      network->def->forwardDev,
                                      "udp") < 0) {
@@ -667,7 +668,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
     /* TCP with a source port restriction */
     if (iptablesAddForwardMasquerade(driver->iptables,
-                                     &network->def->ipAddress,
+                                     &ipdef->address,
                                      prefix,
                                      network->def->forwardDev,
                                      "tcp") < 0) {
@@ -681,25 +682,25 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
  masqerr5:
     iptablesRemoveForwardMasquerade(driver->iptables,
-                                    &network->def->ipAddress,
+                                    &ipdef->address,
                                     prefix,
                                     network->def->forwardDev,
                                     "udp");
  masqerr4:
     iptablesRemoveForwardMasquerade(driver->iptables,
-                                    &network->def->ipAddress,
+                                    &ipdef->address,
                                     prefix,
                                     network->def->forwardDev,
                                     NULL);
  masqerr3:
     iptablesRemoveForwardAllowRelatedIn(driver->iptables,
-                                        &network->def->ipAddress,
+                                        &ipdef->address,
                                         prefix,
                                         network->def->bridge,
                                         network->def->forwardDev);
  masqerr2:
     iptablesRemoveForwardAllowOut(driver->iptables,
-                                  &network->def->ipAddress,
+                                  &ipdef->address,
                                   prefix,
                                   network->def->bridge,
                                   network->def->forwardDev);
@@ -709,8 +710,9 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
 
 static int
 networkAddRoutingIptablesRules(struct network_driver *driver,
-                               virNetworkObjPtr network) {
-    int prefix = virNetworkDefPrefix(network->def);
+                               virNetworkObjPtr network,
+                               virNetworkIpDefPtr ipdef) {
+    int prefix = virNetworkIpDefPrefix(ipdef);
 
     if (prefix < 0) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
@@ -721,7 +723,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver,
 
     /* allow routing packets from the bridge interface */
     if (iptablesAddForwardAllowOut(driver->iptables,
-                                   &network->def->ipAddress,
+                                   &ipdef->address,
                                    prefix,
                                    network->def->bridge,
                                    network->def->forwardDev) < 0) {
@@ -733,7 +735,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver,
 
     /* allow routing packets to the bridge interface */
     if (iptablesAddForwardAllowIn(driver->iptables,
-                                  &network->def->ipAddress,
+                                  &ipdef->address,
                                   prefix,
                                   network->def->bridge,
                                   network->def->forwardDev) < 0) {
@@ -748,7 +750,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver,
 
  routeerr2:
     iptablesRemoveForwardAllowOut(driver->iptables,
-                                  &network->def->ipAddress,
+                                  &ipdef->address,
                                   prefix,
                                   network->def->bridge,
                                   network->def->forwardDev);
@@ -758,7 +760,8 @@ networkAddRoutingIptablesRules(struct network_driver *driver,
 
 static int
 networkAddIptablesRules(struct network_driver *driver,
-                        virNetworkObjPtr network) {
+                        virNetworkObjPtr network,
+                        virNetworkIpDefPtr ipdef) {
 
     /* allow DHCP requests through to dnsmasq */
     if (iptablesAddTcpInput(driver->iptables, network->def->bridge, 67) < 0) {
@@ -791,7 +794,7 @@ networkAddIptablesRules(struct network_driver *driver,
     }
 
     /* allow TFTP requests through to dnsmasq */
-    if (network->def->tftproot &&
+    if (ipdef && ipdef->tftproot &&
         iptablesAddUdpInput(driver->iptables, network->def->bridge, 69) < 0) {
         networkReportError(VIR_ERR_SYSTEM_ERROR,
                            _("failed to add iptables rule to allow TFTP requests from '%s'"),
@@ -824,29 +827,30 @@ networkAddIptablesRules(struct network_driver *driver,
         goto err7;
     }
 
-
-    /* If masquerading is enabled, set up the rules*/
-    if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT &&
-        networkAddMasqueradingIptablesRules(driver, network) < 0)
-        goto err8;
-    /* else if routing is enabled, set up the rules*/
-    else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE &&
-             networkAddRoutingIptablesRules(driver, network) < 0)
-        goto err8;
-
-    /* If we are doing local DHCP service on this network, attempt to
-     * add a rule that will fixup the checksum of DHCP response
-     * packets back to the guests (but report failure without
-     * aborting, since not all iptables implementations support it).
-     */
-
-    if ((VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) ||
-         network->def->nranges) &&
-        (iptablesAddOutputFixUdpChecksum(driver->iptables,
-                                         network->def->bridge, 68) < 0)) {
-        VIR_WARN("Could not add rule to fixup DHCP response checksums "
-                 "on network '%s'.", network->def->name);
-        VIR_WARN0("May need to update iptables package & kernel to support CHECKSUM rule.");
+    if (ipdef) {
+        /* If masquerading is enabled, set up the rules*/
+        if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT &&
+            networkAddMasqueradingIptablesRules(driver, network, ipdef) < 0)
+            goto err8;
+        /* else if routing is enabled, set up the rules*/
+        else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE &&
+                 networkAddRoutingIptablesRules(driver, network, ipdef) < 0)
+            goto err8;
+
+        /* If we are doing local DHCP service on this network, attempt to
+         * add a rule that will fixup the checksum of DHCP response
+         * packets back to the guests (but report failure without
+         * aborting, since not all iptables implementations support it).
+         */
+
+        if (ipdef && (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET) ||
+                      ipdef->nranges) &&
+            (iptablesAddOutputFixUdpChecksum(driver->iptables,
+                                             network->def->bridge, 68) < 0)) {
+            VIR_WARN("Could not add rule to fixup DHCP response checksums "
+                     "on network '%s'.", network->def->name);
+            VIR_WARN0("May need to update iptables package & kernel to support CHECKSUM rule.");
+        }
     }
 
     return 0;
@@ -861,7 +865,7 @@ networkAddIptablesRules(struct network_driver *driver,
     iptablesRemoveForwardRejectOut(driver->iptables,
                                    network->def->bridge);
  err5:
-    if (network->def->tftproot) {
+    if (ipdef && ipdef->tftproot) {
         iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 69);
     }
  err4tftp:
@@ -878,14 +882,16 @@ networkAddIptablesRules(struct network_driver *driver,
 
 static void
 networkRemoveIptablesRules(struct network_driver *driver,
-                         virNetworkObjPtr network) {
-    if (VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) ||
-        network->def->nranges) {
+                           virNetworkObjPtr network,
+                           virNetworkIpDefPtr ipdef) {
+
+    if (ipdef && (VIR_SOCKET_HAS_ADDR(&ipdef->address) ||
+                  ipdef->nranges)) {
         iptablesRemoveOutputFixUdpChecksum(driver->iptables,
                                            network->def->bridge, 68);
     }
-    if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE) {
-        int prefix = virNetworkDefPrefix(network->def);
+    if (ipdef && network->def->forwardType != VIR_NETWORK_FORWARD_NONE) {
+        int prefix = virNetworkIpDefPrefix(ipdef);
 
         if (prefix < 0) {
             networkReportError(VIR_ERR_INTERNAL_ERROR,
@@ -896,34 +902,35 @@ networkRemoveIptablesRules(struct network_driver *driver,
 
         if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
             iptablesRemoveForwardMasquerade(driver->iptables,
-                                            &network->def->ipAddress,
+                                            &ipdef->address,
                                             prefix,
                                             network->def->forwardDev,
                                             "tcp");
             iptablesRemoveForwardMasquerade(driver->iptables,
-                                            &network->def->ipAddress,
+                                            &ipdef->address,
                                             prefix,
                                             network->def->forwardDev,
                                             "udp");
             iptablesRemoveForwardMasquerade(driver->iptables,
-                                            &network->def->ipAddress,
+                                            &ipdef->address,
                                             prefix,
                                             network->def->forwardDev,
                                             NULL);
             iptablesRemoveForwardAllowRelatedIn(driver->iptables,
-                                                &network->def->ipAddress,
+                                                &ipdef->address,
                                                 prefix,
                                                 network->def->bridge,
                                                 network->def->forwardDev);
-        } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE)
+        } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
             iptablesRemoveForwardAllowIn(driver->iptables,
-                                         &network->def->ipAddress,
+                                         &ipdef->address,
                                          prefix,
                                          network->def->bridge,
                                          network->def->forwardDev);
+        }
 
         iptablesRemoveForwardAllowOut(driver->iptables,
-                                      &network->def->ipAddress,
+                                      &ipdef->address,
                                       prefix,
                                       network->def->bridge,
                                       network->def->forwardDev);
@@ -932,7 +939,7 @@ error:
     iptablesRemoveForwardAllowCross(driver->iptables, network->def->bridge);
     iptablesRemoveForwardRejectIn(driver->iptables, network->def->bridge);
     iptablesRemoveForwardRejectOut(driver->iptables, network->def->bridge);
-    if (network->def->tftproot)
+    if (ipdef && ipdef->tftproot)
         iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 69);
     iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 53);
     iptablesRemoveTcpInput(driver->iptables, network->def->bridge, 53);
@@ -951,10 +958,18 @@ networkReloadIptablesRules(struct network_driver *driver)
         virNetworkObjLock(driver->networks.objs[i]);
 
         if (virNetworkObjIsActive(driver->networks.objs[i])) {
-            networkRemoveIptablesRules(driver, driver->networks.objs[i]);
-            if (networkAddIptablesRules(driver, driver->networks.objs[i]) < 0) {
-                /* failed to add but already logged */
-            }
+           virNetworkIpDefPtr ipv4def;
+
+           /* Find the one allowed IPv4 ip address in the definition */
+           /* Even if none is found, we still call the functions below */
+           ipv4def = virNetworkDefGetIpByIndex(driver->networks.objs[i]->def,
+                                               AF_INET, 0);
+           networkRemoveIptablesRules(driver, driver->networks.objs[i],
+                                      ipv4def);
+           if (networkAddIptablesRules(driver, driver->networks.objs[i],
+                                       ipv4def) < 0) {
+               /* failed to add but already logged */
+           }
         }
 
         virNetworkObjUnlock(driver->networks.objs[i]);
@@ -1028,7 +1043,8 @@ cleanup:
  *      other scenarios where we can ruin host network connectivity.
  * XXX: Using a proper library is preferred over parsing /proc
  */
-static int networkCheckRouteCollision(virNetworkObjPtr network)
+static int networkCheckRouteCollision(virNetworkObjPtr network,
+                                      virNetworkIpDefPtr ipdef)
 {
     int ret = -1, len;
     unsigned int net_dest;
@@ -1036,18 +1052,18 @@ static int networkCheckRouteCollision(virNetworkObjPtr network)
     char *cur, *buf = NULL;
     enum {MAX_ROUTE_SIZE = 1024*64};
 
-    if (!VIR_SOCKET_IS_FAMILY(&network->def->ipAddress, AF_INET)) {
+    if (!VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
         /* Only support collision check for IPv4 */
         return 0;
     }
 
-    if (virNetworkDefNetmask(network->def, &netmask) < 0) {
+    if (virNetworkIpDefNetmask(ipdef, &netmask) < 0) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
                            _("Failed to get netmask of '%s'"),
                            network->def->bridge);
     }
 
-    net_dest = (network->def->ipAddress.data.inet4.sin_addr.s_addr &
+    net_dest = (ipdef->address.data.inet4.sin_addr.s_addr &
                 netmask.data.inet4.sin_addr.s_addr);
 
     /* Read whole routing table into memory */
@@ -1121,6 +1137,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
 {
     int err;
     virErrorPtr save_err;
+    virNetworkIpDefPtr ipv4def;
 
     if (virNetworkObjIsActive(network)) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1128,8 +1145,11 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
         return -1;
     }
 
+    /* find the one allowed IPv4 ip address in the definition */
+    ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
+
     /* Check to see if network collides with an existing route */
-    if (networkCheckRouteCollision(network) < 0)
+    if (ipv4def && networkCheckRouteCollision(network, ipv4def) < 0)
         return -1;
 
     if ((err = brAddBridge(driver->brctl, network->def->bridge))) {
@@ -1158,8 +1178,8 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
         goto err_delbr;
     }
 
-    if (VIR_SOCKET_HAS_ADDR(&network->def->ipAddress)) {
-        int prefix = virNetworkDefPrefix(network->def);
+    if (ipv4def) {
+        int prefix = virNetworkIpDefPrefix(ipv4def);
 
         if (prefix < 0) {
             networkReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1169,7 +1189,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
         }
 
         if ((err = brAddInetAddress(driver->brctl, network->def->bridge,
-                                    &network->def->ipAddress, prefix))) {
+                                    &ipv4def->address, prefix))) {
             networkReportError(VIR_ERR_INTERNAL_ERROR,
                                _("cannot set IP address on bridge '%s'"),
                                network->def->bridge);
@@ -1184,7 +1204,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
         goto err_delbr;
     }
 
-    if (networkAddIptablesRules(driver, network) < 0)
+    if (ipv4def && networkAddIptablesRules(driver, network, ipv4def) < 0)
         goto err_delbr1;
 
     if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE &&
@@ -1194,12 +1214,13 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
         goto err_delbr2;
     }
 
-    if ((VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) ||
-         network->def->nranges) &&
-        dhcpStartDhcpDaemon(network) < 0)
+    /*
+     * Start the dhcp daemon for the 1st (and only supported) ipv4
+     * address.
+     */
+    if (ipv4def && dhcpStartDhcpDaemon(network, ipv4def) < 0)
         goto err_delbr2;
 
-
     /* Persist the live configuration now we have bridge info  */
     if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) {
         goto err_kill;
@@ -1217,7 +1238,8 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
 
  err_delbr2:
     save_err = virSaveLastError();
-    networkRemoveIptablesRules(driver, network);
+    if (ipv4def)
+        networkRemoveIptablesRules(driver, network, ipv4def);
     if (save_err) {
         virSetError(save_err);
         virFreeError(save_err);
@@ -1246,6 +1268,7 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver,
 {
     int err;
     char *stateFile;
+    virNetworkIpDefPtr ipv4def;
 
     VIR_INFO(_("Shutting down network '%s'"), network->def->name);
 
@@ -1262,7 +1285,10 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver,
     if (network->dnsmasqPid > 0)
         kill(network->dnsmasqPid, SIGTERM);
 
-    networkRemoveIptablesRules(driver, network);
+    /* find the one allowed IPv4 ip address in the definition */
+    ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
+    if (ipv4def)
+        networkRemoveIptablesRules(driver, network, ipv4def);
 
     char ebuf[1024];
     if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 0))) {
@@ -1526,6 +1552,7 @@ cleanup:
 
 static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
     struct network_driver *driver = conn->networkPrivateData;
+    virNetworkIpDefPtr ipv4def;
     virNetworkDefPtr def;
     virNetworkObjPtr network = NULL;
     virNetworkPtr ret = NULL;
@@ -1556,12 +1583,15 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
         goto cleanup;
     }
 
-    if (network->def->nhosts > 0) {
+    /* we only support dhcp on one IPv4 address per defined network */
+    ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
+
+    if (ipv4def && ipv4def->nhosts > 0) {
         dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
         if (dctx == NULL)
             goto cleanup;
 
-        networkSaveDnsmasqHostsfile(network, dctx, true);
+        networkSaveDnsmasqHostsfile(ipv4def, dctx, true);
         dnsmasqContextFree(dctx);
     }
 
@@ -1577,7 +1607,8 @@ cleanup:
 
 static int networkUndefine(virNetworkPtr net) {
     struct network_driver *driver = net->conn->networkPrivateData;
-    virNetworkObjPtr network = NULL;
+    virNetworkObjPtr network;
+    virNetworkIpDefPtr ipv4def;
     int ret = -1;
 
     networkDriverLock(driver);
@@ -1600,7 +1631,10 @@ static int networkUndefine(virNetworkPtr net) {
                                network) < 0)
         goto cleanup;
 
-    if (network->def->nhosts > 0) {
+    /* find the one allowed IPv4 ip address in the definition */
+    ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
+
+    if (ipv4def && ipv4def->nhosts > 0) {
         dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
         if (dctx == NULL)
             goto cleanup;
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 728c501..03defb5 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -7038,9 +7038,23 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char *
     IHostNetworkInterface *networkInterface = NULL;
 
     virNetworkDefPtr def = virNetworkDefParseString(xml);
+    virNetworkIpDefPtr ipdef;
+    virSocketAddr netmask;
 
     if (   (!def)
-        || (def->forwardType != VIR_NETWORK_FORWARD_NONE))
+        || (def->forwardType != VIR_NETWORK_FORWARD_NONE)
+        || (def->nips == 0 || !def->ips))
+        goto cleanup;
+
+    /* Look for the first IPv4 IP address definition and use that.
+     * If there weren't any IPv4 addresses, ignore the network (since it's
+     * required below to have an IPv4 address)
+    */
+    ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0);
+    if (!ipdef)
+        goto cleanup;
+
+    if (virNetworkIpDefNetmask(ipdef, &netmask) < 0)
         goto cleanup;
 
     /* the current limitation of hostonly network is that you can't
@@ -7096,9 +7110,9 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char *
         /* Currently support only one dhcp server per network
          * with contigious address space from start to end
          */
-        if ((def->nranges >= 1) &&
-            VIR_SOCKET_HAS_ADDR(&def->ranges[0].start) &&
-            VIR_SOCKET_HAS_ADDR(&def->ranges[0].end)) {
+        if ((ipdef->nranges >= 1) &&
+            VIR_SOCKET_HAS_ADDR(&ipdef->ranges[0].start) &&
+            VIR_SOCKET_HAS_ADDR(&ipdef->ranges[0].end)) {
             IDHCPServer *dhcpServer = NULL;
 
             data->vboxObj->vtbl->FindDHCPServerByNetworkName(data->vboxObj,
@@ -7118,10 +7132,10 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char *
                 PRUnichar *toIPAddressUtf16   = NULL;
                 PRUnichar *trunkTypeUtf16     = NULL;
 
-                ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ipAddress);
-                networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &def->netmask);
-                fromIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ranges[0].start);
-                toIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ranges[0].end);
+                ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->address);
+                networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &netmask);
+                fromIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->ranges[0].start);
+                toIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->ranges[0].end);
 
                 if (ipAddressUtf16 == NULL || networkMaskUtf16 == NULL ||
                     fromIPAddressUtf16 == NULL || toIPAddressUtf16 == NULL) {
@@ -7158,13 +7172,13 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char *
             }
         }
 
-        if ((def->nhosts >= 1) &&
-            VIR_SOCKET_HAS_ADDR(&def->hosts[0].ip)) {
+        if ((ipdef->nhosts >= 1) &&
+            VIR_SOCKET_HAS_ADDR(&ipdef->hosts[0].ip)) {
             PRUnichar *ipAddressUtf16   = NULL;
             PRUnichar *networkMaskUtf16 = NULL;
 
-            ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->hosts[0].ip);
-            networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &def->netmask);
+            ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->hosts[0].ip);
+            networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &netmask);
 
             if (ipAddressUtf16 == NULL || networkMaskUtf16 == NULL) {
                 VBOX_UTF16_FREE(ipAddressUtf16);
@@ -7385,12 +7399,19 @@ static int vboxNetworkDestroy(virNetworkPtr network) {
 static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSED) {
     VBOX_OBJECT_HOST_CHECK(network->conn, char *, NULL);
     virNetworkDefPtr def  = NULL;
+    virNetworkIpDefPtr ipdef = NULL;
     char *networkNameUtf8 = NULL;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
         goto cleanup;
     }
+    if (VIR_ALLOC(ipdef) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    def->ips = ipdef;
+    def->nips = 1;
 
     if (virAsprintf(&networkNameUtf8, "HostInterfaceNetworking-%s", network->name) < 0) {
         virReportOOMError();
@@ -7427,8 +7448,8 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                                                                  networkNameUtf16,
                                                                  &dhcpServer);
                 if (dhcpServer) {
-                    def->nranges = 1;
-                    if (VIR_ALLOC_N(def->ranges, def->nranges) >=0 ) {
+                    ipdef->nranges = 1;
+                    if (VIR_ALLOC_N(ipdef->ranges, ipdef->nranges) >=0 ) {
                         PRUnichar *ipAddressUtf16     = NULL;
                         PRUnichar *networkMaskUtf16   = NULL;
                         PRUnichar *fromIPAddressUtf16 = NULL;
@@ -7443,13 +7464,13 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                          * with contigious address space from start to end
                          */
                         if (vboxSocketParseAddrUtf16(data, ipAddressUtf16,
-                                                     &def->ipAddress) < 0 ||
+                                                     &ipdef->address) < 0 ||
                             vboxSocketParseAddrUtf16(data, networkMaskUtf16,
-                                                     &def->netmask) < 0 ||
+                                                     &ipdef->netmask) < 0 ||
                             vboxSocketParseAddrUtf16(data, fromIPAddressUtf16,
-                                                     &def->ranges[0].start) < 0 ||
+                                                     &ipdef->ranges[0].start) < 0 ||
                             vboxSocketParseAddrUtf16(data, toIPAddressUtf16,
-                                                     &def->ranges[0].end) < 0) {
+                                                     &ipdef->ranges[0].end) < 0) {
                             errorOccurred = true;
                         }
 
@@ -7462,16 +7483,16 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                             goto cleanup;
                         }
                     } else {
-                        def->nranges = 0;
+                        ipdef->nranges = 0;
                         virReportOOMError();
                     }
 
-                    def->nhosts = 1;
-                    if (VIR_ALLOC_N(def->hosts, def->nhosts) >=0 ) {
-                        def->hosts[0].name = strdup(network->name);
-                        if (def->hosts[0].name == NULL) {
-                            VIR_FREE(def->hosts);
-                            def->nhosts = 0;
+                    ipdef->nhosts = 1;
+                    if (VIR_ALLOC_N(ipdef->hosts, ipdef->nhosts) >=0 ) {
+                        ipdef->hosts[0].name = strdup(network->name);
+                        if (ipdef->hosts[0].name == NULL) {
+                            VIR_FREE(ipdef->hosts);
+                            ipdef->nhosts = 0;
                             virReportOOMError();
                         } else {
                             PRUnichar *macAddressUtf16 = NULL;
@@ -7481,10 +7502,10 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                             networkInterface->vtbl->GetHardwareAddress(networkInterface, &macAddressUtf16);
                             networkInterface->vtbl->GetIPAddress(networkInterface, &ipAddressUtf16);
 
-                            VBOX_UTF16_TO_UTF8(macAddressUtf16, &def->hosts[0].mac);
+                            VBOX_UTF16_TO_UTF8(macAddressUtf16, &ipdef->hosts[0].mac);
 
                             if (vboxSocketParseAddrUtf16(data, ipAddressUtf16,
-                                                         &def->hosts[0].ip) < 0) {
+                                                         &ipdef->hosts[0].ip) < 0) {
                                 errorOccurred = true;
                             }
 
@@ -7496,7 +7517,7 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                             }
                         }
                     } else {
-                        def->nhosts = 0;
+                        ipdef->nhosts = 0;
                     }
 
                     VBOX_RELEASE(dhcpServer);
@@ -7509,9 +7530,9 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE
                     networkInterface->vtbl->GetIPAddress(networkInterface, &ipAddressUtf16);
 
                     if (vboxSocketParseAddrUtf16(data, networkMaskUtf16,
-                                                 &def->netmask) < 0 ||
+                                                 &ipdef->netmask) < 0 ||
                         vboxSocketParseAddrUtf16(data, ipAddressUtf16,
-                                                 &def->ipAddress) < 0) {
+                                                 &ipdef->address) < 0) {
                         errorOccurred = true;
                     }
 
diff --git a/tests/networkxml2xmlin/nat-network.xml b/tests/networkxml2xmlin/nat-network.xml
index 93ab186..23f7fcb 100644
--- a/tests/networkxml2xmlin/nat-network.xml
+++ b/tests/networkxml2xmlin/nat-network.xml
@@ -10,4 +10,12 @@
       <host mac="00:16:3e:3e:a9:1a" name="b.example.com" ip="192.168.122.11" />
     </dhcp>
   </ip>
+  <ip family="ipv4" address="192.168.123.1" netmask="255.255.255.0">
+  </ip>
+  <ip family="ipv6" address="2001:db8:ac10:fe01::1" prefix="64">
+  </ip>
+  <ip family="ipv6" address="2001:db8:ac10:fd01::1" prefix="64">
+  </ip>
+  <ip family="ipv4" address="10.24.10.1">
+  </ip>
 </network>
diff --git a/tests/networkxml2xmlout/nat-network.xml b/tests/networkxml2xmlout/nat-network.xml
index 036d4fb..eb71d9e 100644
--- a/tests/networkxml2xmlout/nat-network.xml
+++ b/tests/networkxml2xmlout/nat-network.xml
@@ -10,4 +10,12 @@
       <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
     </dhcp>
   </ip>
+  <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+  </ip>
+  <ip family='ipv4' address='10.24.10.1'>
+  </ip>
 </network>
-- 
1.7.3.4




More information about the libvir-list mailing list