From aa2cc72100d78997e7772e5ad4e76eec7f1fd8ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Tue, 22 Jul 2014 11:09:48 +0200
Subject: [PATCH 07/17] Domain conf: allow more than one IP address for net
 devices

Add the possibility to have more than one IP address configured for a
domain network interface. IP addresses can also have a prefix to define
the corresponding netmask.
---
 docs/formatdomain.html.in                       |  23 ++++
 docs/schemas/domaincommon.rng                   |  16 ++-
 src/conf/domain_conf.c                          | 149 ++++++++++++++++++++----
 src/conf/domain_conf.h                          |  15 ++-
 src/libvirt_private.syms                        |   1 +
 src/openvz/openvz_conf.c                        |   2 +-
 src/openvz/openvz_driver.c                      |   7 +-
 src/qemu/qemu_driver.c                          |  26 ++++-
 src/qemu/qemu_hotplug.c                         |   5 +-
 src/uml/uml_conf.c                              |   2 +-
 src/vbox/vbox_common.c                          |   6 +-
 src/xenconfig/xen_common.c                      |  21 ++--
 src/xenconfig/xen_sxpr.c                        |  18 ++-
 tests/lxcxml2xmldata/lxc-idmap.xml              |   2 +
 tests/openvzutilstest.c                         |   2 +-
 tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml |   2 +-
 tests/sexpr2xmldata/sexpr2xml-net-routed.xml    |   2 +-
 17 files changed, 240 insertions(+), 59 deletions(-)

Index: libvirt-1.2.5/docs/formatdomain.html.in
===================================================================
--- libvirt-1.2.5.orig/docs/formatdomain.html.in
+++ libvirt-1.2.5/docs/formatdomain.html.in
@@ -3928,6 +3928,29 @@ qemu-kvm -net nic,model=? /dev/null
       <span class="since">Since 0.9.5</span>
     </p>
 
+    <h5><a name="ipconfig">IP configuration</a></h5>
+<pre>
+  ...
+  &lt;devices&gt;
+    &lt;interface type='network'&gt;
+      &lt;source network='default'/&gt;
+      &lt;target dev='vnet0'/&gt;
+      <b>&lt;ip family='ipv4' address='192.168.122.5' prefix='24'/&gt;</b>
+    &lt;/interface&gt;
+  &lt;/devices&gt;
+  ...
+</pre>
+
+    <p>
+    <span class="since">Since 1.2.12</span> the network devices and host devices
+    with network capabilities can be provided zero or more IP addresses to set
+    on the target device. Note that some hypervisors or network device types
+    will simply ignore them or only use the first one. The <code>family</code>
+    attribute can be set to either <code>ipv4</code> or <code>ipv6</code>, the
+    <code>address</code> attribute holds the IP address. The <code>prefix</code>
+    is not mandatory since some hypervisors do not handle it.
+    </p>
+
     <h4><a name="elementsInput">Input devices</a></h4>
 
     <p>
Index: libvirt-1.2.5/docs/schemas/domaincommon.rng
===================================================================
--- libvirt-1.2.5.orig/docs/schemas/domaincommon.rng
+++ libvirt-1.2.5/docs/schemas/domaincommon.rng
@@ -2189,14 +2189,24 @@
           <empty/>
         </element>
       </optional>
-      <optional>
+      <zeroOrMore>
         <element name="ip">
           <attribute name="address">
-            <ref name="ipv4Addr"/>
+            <ref name="ipAddr"/>
           </attribute>
+          <optional>
+            <attribute name="family">
+              <ref name="addr-family"/>
+            </attribute>
+          </optional>
+          <optional>
+            <attribute name="prefix">
+              <ref name="ipPrefix"/>
+            </attribute>
+          </optional>
           <empty/>
         </element>
-      </optional>
+      </zeroOrMore>
       <optional>
         <element name="script">
           <attribute name="path">
Index: libvirt-1.2.5/src/conf/domain_conf.c
===================================================================
--- libvirt-1.2.5.orig/src/conf/domain_conf.c
+++ libvirt-1.2.5/src/conf/domain_conf.c
@@ -1367,6 +1367,8 @@ virDomainActualNetDefFree(virDomainActua
 
 void virDomainNetDefFree(virDomainNetDefPtr def)
 {
+    size_t i;
+
     if (!def)
         return;
 
@@ -1375,7 +1377,6 @@ void virDomainNetDefFree(virDomainNetDef
     switch (def->type) {
     case VIR_DOMAIN_NET_TYPE_ETHERNET:
         VIR_FREE(def->data.ethernet.dev);
-        VIR_FREE(def->data.ethernet.ipaddr);
         break;
 
     case VIR_DOMAIN_NET_TYPE_SERVER:
@@ -1392,7 +1393,6 @@ void virDomainNetDefFree(virDomainNetDef
 
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
         VIR_FREE(def->data.bridge.brname);
-        VIR_FREE(def->data.bridge.ipaddr);
         break;
 
     case VIR_DOMAIN_NET_TYPE_INTERNAL:
@@ -1418,6 +1418,10 @@ void virDomainNetDefFree(virDomainNetDef
     VIR_FREE(def->ifname_guest);
     VIR_FREE(def->ifname_guest_actual);
 
+    for (i = 0; i < def->nips; i++)
+        VIR_FREE(def->ips[i]);
+    VIR_FREE(def->ips);
+
     virDomainDeviceInfoClear(&def->info);
 
     VIR_FREE(def->filter);
@@ -4397,6 +4401,58 @@ virDomainHostdevDefParseXMLSubsys(xmlNod
     return ret;
 }
 
+static virDomainNetIpDefPtr
+virDomainNetIpParseXML(xmlNodePtr node)
+{
+    /* Parse the prefix in every case */
+    virDomainNetIpDefPtr ip = NULL;
+    char *prefixStr = NULL;
+    unsigned int prefixValue = 0;
+    char *familyStr = NULL;
+    int family = AF_UNSPEC;
+    char *address = NULL;
+
+    if (!(prefixStr = virXMLPropString(node, "prefix")) ||
+        (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) {
+        // Don't shout, as some old config may not have a prefix
+        VIR_DEBUG("Missing or invalid network prefix");
+    }
+
+    if (!(address = virXMLPropString(node, "address"))) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("Missing network address"));
+        goto error;
+    }
+
+    familyStr = virXMLPropString(node, "family");
+    if (familyStr && STREQ(familyStr, "ipv4"))
+        family = AF_INET;
+    else if (familyStr && STREQ(familyStr, "ipv6"))
+        family = AF_INET6;
+    else
+        family = virSocketAddrNumericFamily(address);
+
+    if (VIR_ALLOC(ip) < 0)
+        goto error;
+
+    if (virSocketAddrParse(&ip->address, address, family) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("Failed to parse IP address: '%s'"),
+                       address);
+        goto error;
+    }
+    ip->prefix = prefixValue;
+
+    return ip;
+
+ error:
+    VIR_FREE(prefixStr);
+    VIR_FREE(familyStr);
+    VIR_FREE(address);
+    VIR_FREE(ip);
+    return NULL;
+}
+
 static int
 virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
                                 xmlXPathContextPtr ctxt,
@@ -6602,6 +6658,31 @@ virDomainActualNetDefParseXML(xmlNodePtr
 #define NET_MODEL_CHARS \
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
 
+
+int
+virDomainNetAppendIpAddress(virDomainNetDefPtr def,
+                            const char *address,
+                            int family,
+                            unsigned int prefix)
+{
+    virDomainNetIpDefPtr ipDef = NULL;
+    if (VIR_ALLOC(ipDef) < 0)
+        return -1;
+
+    if (virSocketAddrParse(&ipDef->address, address, family) < 0)
+        goto error;
+    ipDef->prefix = prefix;
+
+    if (VIR_APPEND_ELEMENT(def->ips, def->nips, ipDef) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    VIR_FREE(ipDef);
+    return -1;
+}
+
 /* Parse the XML definition for a network interface
  * @param node XML nodeset to parse for net definition
  * @return 0 on success, -1 on failure
@@ -6644,6 +6725,9 @@ virDomainNetDefParseXML(virDomainXMLOpti
     virDomainActualNetDefPtr actual = NULL;
     xmlNodePtr oldnode = ctxt->node;
     int ret;
+    size_t i;
+    size_t nips = 0;
+    virDomainNetIpDefPtr *ips = NULL;
 
     if (VIR_ALLOC(def) < 0)
         return NULL;
@@ -6716,11 +6800,14 @@ virDomainNetDefParseXML(virDomainXMLOpti
                        xmlStrEqual(cur->name, BAD_CAST "source")) {
                 address = virXMLPropString(cur, "address");
                 port = virXMLPropString(cur, "port");
-            } else if (!address &&
-                       (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET ||
-                        def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) &&
-                       xmlStrEqual(cur->name, BAD_CAST "ip")) {
-                address = virXMLPropString(cur, "address");
+            } else if (xmlStrEqual(cur->name, BAD_CAST "ip")) {
+                virDomainNetIpDefPtr ip = NULL;
+
+                if (!(ip = virDomainNetIpParseXML(cur)))
+                    goto error;
+
+                if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0)
+                    goto error;
             } else if (!ifname &&
                        xmlStrEqual(cur->name, BAD_CAST "target")) {
                 ifname = virXMLPropString(cur, "dev");
@@ -6851,10 +6938,6 @@ virDomainNetDefParseXML(virDomainXMLOpti
             def->data.ethernet.dev = dev;
             dev = NULL;
         }
-        if (address != NULL) {
-            def->data.ethernet.ipaddr = address;
-            address = NULL;
-        }
         break;
 
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
@@ -6866,10 +6949,6 @@ virDomainNetDefParseXML(virDomainXMLOpti
         }
         def->data.bridge.brname = bridge;
         bridge = NULL;
-        if (address != NULL) {
-            def->data.bridge.ipaddr = address;
-            address = NULL;
-        }
         break;
 
     case VIR_DOMAIN_NET_TYPE_CLIENT:
@@ -6967,6 +7046,11 @@ virDomainNetDefParseXML(virDomainXMLOpti
         break;
     }
 
+    for (i = 0; i < nips; i++) {
+        if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0)
+            goto error;
+    }
+
     if (script != NULL) {
         def->script = script;
         script = NULL;
@@ -7119,6 +7203,7 @@ virDomainNetDefParseXML(virDomainXMLOpti
     VIR_FREE(mode);
     VIR_FREE(linkstate);
     VIR_FREE(addrtype);
+    VIR_FREE(ips);
     virNWFilterHashTableFree(filterparams);
 
     return def;
@@ -15545,6 +15630,30 @@ virDomainFSDefFormat(virBufferPtr buf,
     return 0;
 }
 
+static void
+virDomainNetIpsFormat(virBufferPtr buf, virDomainNetIpDefPtr *ips, size_t nips)
+{
+    size_t i;
+
+    /* Output IP addresses */
+    for (i = 0; i < nips; i++) {
+        virSocketAddrPtr address = &ips[i]->address;
+        char *ipStr = virSocketAddrFormat(address);
+        const char *familyStr = NULL;
+        if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET6))
+            familyStr = "ipv6";
+        else if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET))
+            familyStr = "ipv4";
+        virBufferAsprintf(buf, "<ip address='%s'",
+                          ipStr);
+        if (familyStr)
+            virBufferAsprintf(buf, " family='%s'", familyStr);
+        if (ips[i]->prefix != 0)
+            virBufferAsprintf(buf, " prefix='%u'", ips[i]->prefix);
+        virBufferAddLit(buf, "/>\n");
+    }
+}
+
 static int
 virDomainHostdevDefFormatSubsys(virBufferPtr buf,
                                 virDomainHostdevDefPtr def,
@@ -15748,7 +15857,6 @@ virDomainActualNetDefContentsFormat(virB
     return 0;
 }
 
-
 /* virDomainActualNetDefFormat() - format the ActualNetDef
  * info inside an <actual> element, as required for internal storage
  * of domain status
@@ -15869,18 +15977,11 @@ virDomainNetDefFormat(virBufferPtr buf,
         case VIR_DOMAIN_NET_TYPE_ETHERNET:
             virBufferEscapeString(buf, "<source dev='%s'/>\n",
                                   def->data.ethernet.dev);
-            if (def->data.ethernet.ipaddr)
-                virBufferAsprintf(buf, "<ip address='%s'/>\n",
-                                  def->data.ethernet.ipaddr);
             break;
 
         case VIR_DOMAIN_NET_TYPE_BRIDGE:
             virBufferEscapeString(buf, "<source bridge='%s'/>\n",
                                   def->data.bridge.brname);
-            if (def->data.bridge.ipaddr) {
-                virBufferAsprintf(buf, "<ip address='%s'/>\n",
-                                  def->data.bridge.ipaddr);
-            }
             break;
 
         case VIR_DOMAIN_NET_TYPE_SERVER:
@@ -15928,6 +16029,8 @@ virDomainNetDefFormat(virBufferPtr buf,
             return -1;
     }
 
+    virDomainNetIpsFormat(buf, def->ips, def->nips);
+
     virBufferEscapeString(buf, "<script path='%s'/>\n",
                           def->script);
     if (def->ifname &&
Index: libvirt-1.2.5/src/conf/domain_conf.h
===================================================================
--- libvirt-1.2.5.orig/src/conf/domain_conf.h
+++ libvirt-1.2.5/src/conf/domain_conf.h
@@ -427,6 +427,13 @@ enum virDomainHostdevCapsType {
     VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST
 };
 
+typedef struct _virDomainNetIpDef virDomainNetIpDef;
+typedef virDomainNetIpDef *virDomainNetIpDefPtr;
+struct _virDomainNetIpDef {
+    virSocketAddr address;       /* ipv4 or ipv6 address */
+    unsigned int prefix; /* number of 1 bits in the net mask */
+};
+
 typedef struct _virDomainHostdevCaps virDomainHostdevCaps;
 typedef virDomainHostdevCaps *virDomainHostdevCapsPtr;
 struct _virDomainHostdevCaps {
@@ -875,7 +882,6 @@ struct _virDomainNetDef {
     union {
         struct {
             char *dev;
-            char *ipaddr;
         } ethernet;
         struct {
             char *address;
@@ -896,7 +902,6 @@ struct _virDomainNetDef {
         } network;
         struct {
             char *brname;
-            char *ipaddr;
         } bridge;
         struct {
             char *name;
@@ -925,6 +930,8 @@ struct _virDomainNetDef {
     virNetDevBandwidthPtr bandwidth;
     virNetDevVlan vlan;
     int linkstate;
+    size_t nips;
+    virDomainNetIpDefPtr *ips;
 };
 
 /* Used for prefix of ifname of any network name generated dynamically
@@ -2428,6 +2435,10 @@ virDomainNetGetActualVirtPortProfile(vir
 virNetDevBandwidthPtr
 virDomainNetGetActualBandwidth(virDomainNetDefPtr iface);
 virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface);
+int virDomainNetAppendIpAddress(virDomainNetDefPtr def,
+                                const char *address,
+                                int family,
+                                unsigned int prefix);
 
 int virDomainControllerInsert(virDomainDefPtr def,
                               virDomainControllerDefPtr controller)
Index: libvirt-1.2.5/src/libvirt_private.syms
===================================================================
--- libvirt-1.2.5.orig/src/libvirt_private.syms
+++ libvirt-1.2.5/src/libvirt_private.syms
@@ -315,6 +315,7 @@ virDomainMemballoonModelTypeFromString;
 virDomainMemballoonModelTypeToString;
 virDomainMemDumpTypeFromString;
 virDomainMemDumpTypeToString;
+virDomainNetAppendIpAddress;
 virDomainNetDefFormat;
 virDomainNetDefFree;
 virDomainNetFind;
Index: libvirt-1.2.5/src/openvz/openvz_conf.c
===================================================================
--- libvirt-1.2.5.orig/src/openvz/openvz_conf.c
+++ libvirt-1.2.5/src/openvz/openvz_conf.c
@@ -230,7 +230,7 @@ openvzReadNetworkConf(virDomainDefPtr de
                 goto error;
 
             net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
-            if (VIR_STRDUP(net->data.ethernet.ipaddr, token) < 0)
+            if (virDomainNetAppendIpAddress(net, token, AF_UNSPEC, 0) < 0)
                 goto error;
 
             if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0)
Index: libvirt-1.2.5/src/openvz/openvz_driver.c
===================================================================
--- libvirt-1.2.5.orig/src/openvz/openvz_driver.c
+++ libvirt-1.2.5/src/openvz/openvz_driver.c
@@ -845,7 +845,7 @@ openvzDomainSetNetwork(virConnectPtr con
 
     if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
         (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
-         net->data.ethernet.ipaddr == NULL)) {
+         net->nips == 0)) {
         virBuffer buf = VIR_BUFFER_INITIALIZER;
         int veid = openvzGetVEID(vpsid);
 
@@ -896,9 +896,10 @@ openvzDomainSetNetwork(virConnectPtr con
         virCommandAddArg(cmd, "--netif_add");
         virCommandAddArgBuffer(cmd, &buf);
     } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
-              net->data.ethernet.ipaddr != NULL) {
+              net->nips > 0) {
         /* --ipadd ip */
-        virCommandAddArgList(cmd, "--ipadd", net->data.ethernet.ipaddr, NULL);
+        char *ipStr = virSocketAddrFormat(&net->ips[0]->address);
+        virCommandAddArgList(cmd, "--ipadd", ipStr, NULL);
     }
 
     /* TODO: processing NAT and physical device */
Index: libvirt-1.2.5/src/qemu/qemu_driver.c
===================================================================
--- libvirt-1.2.5.orig/src/qemu/qemu_driver.c
+++ libvirt-1.2.5/src/qemu/qemu_driver.c
@@ -5868,6 +5868,8 @@ static char *qemuConnectDomainXMLToNativ
                 (brname = virDomainNetGetActualBridgeName(net))) {
 
                 char *brnamecopy;
+                size_t j;
+
                 if (VIR_STRDUP(brnamecopy, brname) < 0)
                     goto cleanup;
 
@@ -5878,20 +5880,29 @@ static char *qemuConnectDomainXMLToNativ
                 net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
                 net->script = NULL;
                 net->data.ethernet.dev = brnamecopy;
-                net->data.ethernet.ipaddr = NULL;
+                for (j = 0; j < net->nips; j++)
+                    VIR_FREE(net->ips[j]);
+                VIR_FREE(net->ips);
+                net->nips = 0;
+
             } else {
                 /* actualType is either NETWORK or DIRECT. In either
                  * case, the best we can do is NULL everything out.
                  */
+                size_t j;
                 virDomainActualNetDefFree(net->data.network.actual);
                 memset(net, 0, sizeof(*net));
 
                 net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
                 net->script = NULL;
                 net->data.ethernet.dev = NULL;
-                net->data.ethernet.ipaddr = NULL;
+                for (j = 0; j < net->nips; j++)
+                    VIR_FREE(net->ips[j]);
+                VIR_FREE(net->ips);
+                net->nips = 0;
             }
         } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
+            size_t j;
             VIR_FREE(net->data.direct.linkdev);
 
             memset(net, 0, sizeof(*net));
@@ -5899,18 +5910,23 @@ static char *qemuConnectDomainXMLToNativ
             net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
             net->script = NULL;
             net->data.ethernet.dev = NULL;
-            net->data.ethernet.ipaddr = NULL;
+            for (j = 0; j < net->nips; j++)
+                VIR_FREE(net->ips[j]);
+            VIR_FREE(net->ips);
+            net->nips = 0;
         } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
             char *script = net->script;
             char *brname = net->data.bridge.brname;
-            char *ipaddr = net->data.bridge.ipaddr;
+            size_t nips = net->nips;
+            virDomainNetIpDefPtr *ips = net->ips;
 
             memset(net, 0, sizeof(*net));
 
             net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
             net->script = script;
             net->data.ethernet.dev = brname;
-            net->data.ethernet.ipaddr = ipaddr;
+            net->nips = nips;
+            net->ips = ips;
         }
 
         VIR_FREE(net->virtPortProfile);
Index: libvirt-1.2.5/src/qemu/qemu_hotplug.c
===================================================================
--- libvirt-1.2.5.orig/src/qemu/qemu_hotplug.c
+++ libvirt-1.2.5/src/qemu/qemu_hotplug.c
@@ -2050,8 +2050,9 @@ qemuDomainChangeNet(virQEMUDriverPtr dri
         case VIR_DOMAIN_NET_TYPE_ETHERNET:
             if (STRNEQ_NULLABLE(olddev->data.ethernet.dev,
                                 newdev->data.ethernet.dev) ||
-                STRNEQ_NULLABLE(olddev->data.ethernet.ipaddr,
-                                newdev->data.ethernet.ipaddr)) {
+                olddev->nips == 0 || newdev->nips == 0 ||
+                !virSocketAddrEqual(&olddev->ips[0]->address,
+                                    &newdev->ips[0]->address)) {
                 needReconnect = true;
             }
         break;
Index: libvirt-1.2.5/src/uml/uml_conf.c
===================================================================
--- libvirt-1.2.5.orig/src/uml/uml_conf.c
+++ libvirt-1.2.5/src/uml/uml_conf.c
@@ -175,7 +175,7 @@ umlBuildCommandLineNet(virConnectPtr con
         if (def->ifname) {
             virBufferAdd(&buf, def->ifname, -1);
         }
-        if (def->data.ethernet.ipaddr) {
+        if (def->nips > 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("IP address not supported for ethernet interface"));
             goto error;
Index: libvirt-1.2.5/tests/lxcxml2xmldata/lxc-idmap.xml
===================================================================
--- libvirt-1.2.5.orig/tests/lxcxml2xmldata/lxc-idmap.xml
+++ libvirt-1.2.5/tests/lxcxml2xmldata/lxc-idmap.xml
@@ -28,6 +28,8 @@
     <interface type='bridge'>
       <mac address='00:16:3e:0f:ef:8a'/>
       <source bridge='bri0'/>
+      <ip address='192.168.122.12' family='ipv4' prefix='24'/>
+      <ip address='192.168.122.13' family='ipv4' prefix='24'/>
       <target dev='veth0'/>
       <guest dev='eth2'/>
     </interface>
Index: libvirt-1.2.5/tests/openvzutilstest.c
===================================================================
--- libvirt-1.2.5.orig/tests/openvzutilstest.c
+++ libvirt-1.2.5/tests/openvzutilstest.c
@@ -95,7 +95,7 @@ testReadNetworkConf(const void *data ATT
         "  <devices>\n"
         "    <interface type='ethernet'>\n"
         "      <mac address='00:00:00:00:00:00'/>\n"
-        "      <ip address='194.44.18.88'/>\n"
+        "      <ip address='194.44.18.88' family='ipv4'/>\n"
         "    </interface>\n"
         "    <interface type='bridge'>\n"
         "      <mac address='00:18:51:c1:05:ee'/>\n"
Index: libvirt-1.2.5/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml
===================================================================
--- libvirt-1.2.5.orig/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml
+++ libvirt-1.2.5/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml
@@ -24,7 +24,7 @@
     <interface type='bridge'>
       <mac address='00:11:22:33:44:55'/>
       <source bridge='xenbr2'/>
-      <ip address='192.0.2.1'/>
+      <ip address='192.0.2.1' family='ipv4'/>
       <script path='vif-bridge'/>
       <target dev='vif6.0'/>
     </interface>
Index: libvirt-1.2.5/tests/sexpr2xmldata/sexpr2xml-net-routed.xml
===================================================================
--- libvirt-1.2.5.orig/tests/sexpr2xmldata/sexpr2xml-net-routed.xml
+++ libvirt-1.2.5/tests/sexpr2xmldata/sexpr2xml-net-routed.xml
@@ -23,7 +23,7 @@
     </disk>
     <interface type='ethernet'>
       <mac address='00:11:22:33:44:55'/>
-      <ip address='172.14.5.6'/>
+      <ip address='172.14.5.6' family='ipv4'/>
       <script path='vif-routed'/>
       <target dev='vif6.0'/>
     </interface>
Index: libvirt-1.2.5/src/vbox/vbox_tmpl.c
===================================================================
--- libvirt-1.2.5.orig/src/vbox/vbox_tmpl.c
+++ libvirt-1.2.5/src/vbox/vbox_tmpl.c
@@ -4472,7 +4472,11 @@ vboxAttachNetwork(virDomainDefPtr def, v
         } else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
             VIR_DEBUG("NIC(%zu): brname: %s", i, def->nets[i]->data.bridge.brname);
             VIR_DEBUG("NIC(%zu): script: %s", i, def->nets[i]->script);
-            VIR_DEBUG("NIC(%zu): ipaddr: %s", i, def->nets[i]->data.bridge.ipaddr);
+            if (def->nets[i]->nips > 0) {
+                char *ipStr = virSocketAddrFormat(&def->nets[i]->ips[0]->address);
+                VIR_DEBUG("NIC(%zu): ipaddr: %s", i, ipStr);
+                VIR_FREE(ipStr);
+            }
         }
 
         machine->vtbl->GetNetworkAdapter(machine, i, &adapter);
Index: libvirt-1.2.5/src/xenxs/xen_sxpr.c
===================================================================
--- libvirt-1.2.5.orig/src/xenxs/xen_sxpr.c
+++ libvirt-1.2.5/src/xenxs/xen_sxpr.c
@@ -563,14 +563,14 @@ xenParseSxprNets(virDomainDefPtr def,
                     VIR_STRDUP(net->script, tmp2) < 0)
                     goto cleanup;
                 tmp = sexpr_node(node, "device/vif/ip");
-                if (VIR_STRDUP(net->data.bridge.ipaddr, tmp) < 0)
+                if (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 0) < 0)
                     goto cleanup;
             } else {
                 net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
                 if (VIR_STRDUP(net->script, tmp2) < 0)
                     goto cleanup;
                 tmp = sexpr_node(node, "device/vif/ip");
-                if (VIR_STRDUP(net->data.ethernet.ipaddr, tmp) < 0)
+                if (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 0) < 0)
                     goto cleanup;
             }
 
@@ -1895,8 +1895,11 @@ xenFormatSxprNet(virConnectPtr conn,
             script = def->script;
 
         virBufferEscapeSexpr(buf, "(script '%s')", script);
-        if (def->data.bridge.ipaddr != NULL)
-            virBufferEscapeSexpr(buf, "(ip '%s')", def->data.bridge.ipaddr);
+        if (def->nips > 0) {
+            char *ipStr = virSocketAddrFormat(&def->ips[0]->address);
+            virBufferEscapeSexpr(buf, "(ip '%s')", ipStr);
+            VIR_FREE(ipStr);
+        }
         break;
 
     case VIR_DOMAIN_NET_TYPE_NETWORK:
@@ -1929,8 +1932,11 @@ xenFormatSxprNet(virConnectPtr conn,
         if (def->script)
             virBufferEscapeSexpr(buf, "(script '%s')",
                                  def->script);
-        if (def->data.ethernet.ipaddr != NULL)
-            virBufferEscapeSexpr(buf, "(ip '%s')", def->data.ethernet.ipaddr);
+        if (def->nips > 0) {
+            char *ipStr = virSocketAddrFormat(&def->ips[0]->address);
+            virBufferEscapeSexpr(buf, "(ip '%s')", ipStr);
+            VIR_FREE(ipStr);
+        }
         break;
 
     case VIR_DOMAIN_NET_TYPE_USER:
Index: libvirt-1.2.5/src/xenxs/xen_xm.c
===================================================================
--- libvirt-1.2.5.orig/src/xenxs/xen_xm.c
+++ libvirt-1.2.5/src/xenxs/xen_xm.c
@@ -774,12 +774,9 @@ xenParseXM(virConfPtr conf, int xendConf
             if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
                 if (bridge[0] && VIR_STRDUP(net->data.bridge.brname, bridge) < 0)
                     goto cleanup;
-                if (ip[0] && VIR_STRDUP(net->data.bridge.ipaddr, ip) < 0)
-                    goto cleanup;
-            } else {
-                if (ip[0] && VIR_STRDUP(net->data.ethernet.ipaddr, ip) < 0)
-                    goto cleanup;
             }
+            if (ip[0] && virDomainNetAppendIpAddress(net, ip, AF_INET, 0) < 0)
+                goto cleanup;
 
             if (script && script[0] &&
                 VIR_STRDUP(net->script, script) < 0)
@@ -1339,16 +1336,22 @@ static int xenFormatXMNet(virConnectPtr
     switch (net->type) {
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
         virBufferAsprintf(&buf, ",bridge=%s", net->data.bridge.brname);
-        if (net->data.bridge.ipaddr)
-            virBufferAsprintf(&buf, ",ip=%s", net->data.bridge.ipaddr);
+        if (net->nips > 0) {
+            char *ipStr = virSocketAddrFormat(&net->ips[0]->address);
+            virBufferAsprintf(&buf, ",ip=%s", ipStr);
+            VIR_FREE(ipStr);
+        }
         virBufferAsprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT);
         break;
 
     case VIR_DOMAIN_NET_TYPE_ETHERNET:
         if (net->script)
             virBufferAsprintf(&buf, ",script=%s", net->script);
-        if (net->data.ethernet.ipaddr)
-            virBufferAsprintf(&buf, ",ip=%s", net->data.ethernet.ipaddr);
+        if (net->nips > 0) {
+            char *ipStr = virSocketAddrFormat(&net->ips[0]->address);
+            virBufferAsprintf(&buf, ",ip=%s", ipStr);
+            VIR_FREE(ipStr);
+        }
         break;
 
     case VIR_DOMAIN_NET_TYPE_NETWORK:
