[libvirt] [PATCHv5 13/18] Domain network devices can now have a <route> element

Cédric Bosdonnat cbosdonnat at suse.com
Tue Dec 30 10:27:22 UTC 2014


Network interfaces devices and host devices with net capabilities can
now have IPv4 and/or an IPv6 routes configured.
---
 docs/formatdomain.html.in            |  19 ++++-
 docs/schemas/domaincommon.rng        |  31 ++++++++
 src/conf/domain_conf.c               | 135 ++++++++++++++++++++++++++++++++++-
 src/conf/domain_conf.h               |  12 ++++
 src/util/virnetdev.c                 |  31 +++++++-
 src/util/virnetdev.h                 |   2 +-
 src/util/virsocketaddr.h             |   2 +
 tests/lxcxml2xmldata/lxc-hostdev.xml |   2 +
 tests/lxcxml2xmldata/lxc-idmap.xml   |   2 +
 9 files changed, 230 insertions(+), 6 deletions(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 3f203a5..499879e 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -4328,14 +4328,18 @@ qemu-kvm -net nic,model=? /dev/null
     <interface type='network'>
       <source network='default'/>
       <target dev='vnet0'/>
-      <b><ip family='ipv4' address='192.168.122.5' prefix='24'/></b>
+      <b><ip address='192.168.122.5' prefix='24'/></b>
+      <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b>
+      <b><route family='ipv4' via='192.168.122.1'/></b>
     </interface>
     ...
     <hostdev mode='capabilities' type='net'>
       <source>
         <interface>eth0</interface>
       </source>
-      <b><ip family='ipv4' address='192.168.122.6' prefix='24'/></b>
+      <b><ip address='192.168.122.6' prefix='24'/></b>
+      <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b>
+      <b><route family='ipv4' via='192.168.122.1'/></b>
     </hostdev>
 
   </devices>
@@ -4352,6 +4356,17 @@ qemu-kvm -net nic,model=? /dev/null
     is not mandatory since some hypervisors do not handle it.
     </p>
 
+    <p>
+    <span class="since">Since 1.2.12</span> route elements can also be added
+    to define the network routes to use for the network device. This element
+    has a <code>family</code> attribute set either to <code>ipv4</code> or
+    <code>ipv6</code>, a mandatory <code>via</code> attribute defining the
+    IP address to route throught and optional <code>address</code> and <code>prefix</code>
+    attributes defining the target network range. If those aren't given, then
+    a default route will be set.
+    This is only used by the LXC driver.
+    </p>
+
     <h5><a name="elementVhostuser">vhost-user interface</a></h5>
 
     <p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index c12aedf..879e064 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -2329,6 +2329,11 @@
           <empty/>
         </element>
       </zeroOrMore>
+      <zeroOrMore>
+        <element name="route">
+          <ref name="route"/>
+        </element>
+      </zeroOrMore>
       <optional>
         <element name="script">
           <attribute name="path">
@@ -3597,6 +3602,27 @@
     </element>
   </define>
 
+  <define name="route">
+    <interleave>
+      <attribute name="family">
+        <ref name="addr-family"/>
+      </attribute>
+      <attribute name="via">
+        <ref name="ipAddr"/>
+      </attribute>
+      <optional>
+        <attribute name="address">
+          <ref name="ipAddr"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="prefix">
+          <ref name="ipPrefix"/>
+        </attribute>
+      </optional>
+    </interleave>
+  </define>
+
   <define name="hostdev">
     <element name="hostdev">
       <interleave>
@@ -3832,6 +3858,11 @@
           <empty/>
         </element>
       </zeroOrMore>
+      <zeroOrMore>
+        <element name="route">
+          <ref name="route"/>
+        </element>
+      </zeroOrMore>
     </interleave>
   </define>
 
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9447ed6..b9858cd 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1475,7 +1475,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
         VIR_FREE(def->ips[i]);
     VIR_FREE(def->ips);
 
-    virDomainDeviceInfoClear(&def->info);
+    for (i = 0; i < def->nroutes; i++)
+        VIR_FREE(def->routes[i]);
+    VIR_FREE(def->routes);
+
+        virDomainDeviceInfoClear(&def->info);
 
     VIR_FREE(def->filter);
     virNWFilterHashTableFree(def->filterparams);
@@ -1847,6 +1851,9 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def)
             for (i = 0; i < def->source.caps.u.net.nips; i++)
                 VIR_FREE(def->source.caps.u.net.ips[i]);
             VIR_FREE(def->source.caps.u.net.ips);
+            for (i = 0; i < def->source.caps.u.net.nroutes; i++)
+                VIR_FREE(def->source.caps.u.net.routes[i]);
+            VIR_FREE(def->source.caps.u.net.routes);
             break;
         }
         break;
@@ -4831,6 +4838,64 @@ virDomainNetIpParseXML(xmlNodePtr node)
     return NULL;
 }
 
+static virDomainNetRouteDefPtr
+virDomainNetRouteParse(xmlNodePtr node)
+{
+    virDomainNetRouteDefPtr route = NULL;
+    char *familyStr = NULL;
+    int family = AF_UNSPEC;
+    char *via = NULL;
+    char *to = NULL;
+    char *prefixStr = NULL;
+
+    to = virXMLPropString(node, "address");
+    if (!(via = virXMLPropString(node, "via"))) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("Missing route 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(via);
+
+    if (VIR_ALLOC(route) < 0)
+        goto error;
+
+    if (virSocketAddrParse(&route->via, via, family) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("Failed to parse IP address: '%s'"),
+                       via);
+        goto error;
+    }
+
+    if (to && virSocketAddrParse(&route->to, to, family) < 0) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("Failed to parse IP address: '%s'"),
+                       to);
+        goto error;
+    }
+
+    if (!(prefixStr = virXMLPropString(node, "prefix")) ||
+        (virStrToLong_ui(prefixStr, NULL, 10, &route->prefix) < 0)) {
+    }
+
+    return route;
+
+ error:
+    VIR_FREE(familyStr);
+    VIR_FREE(via);
+    VIR_FREE(to);
+    VIR_FREE(prefixStr);
+    VIR_FREE(route);
+
+    return NULL;
+}
+
 static int
 virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
                                 xmlXPathContextPtr ctxt,
@@ -4840,6 +4905,8 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
     xmlNodePtr sourcenode;
     xmlNodePtr *ipnodes = NULL;
     int nipnodes;
+    xmlNodePtr *routenodes = NULL;
+    int nroutenodes;
     int ret = -1;
 
     /* @type is passed in from the caller rather than read from the
@@ -4914,6 +4981,26 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
                 }
             }
         }
+
+        /* Look for possible gateways */
+        if ((nroutenodes = virXPathNodeSet("./route", ctxt, &routenodes)) < 0)
+            goto error;
+
+        if (nroutenodes) {
+            size_t i;
+            for (i = 0; i < nroutenodes; i++) {
+                virDomainNetRouteDefPtr route = virDomainNetRouteParse(routenodes[i]);
+
+                if (!route)
+                    goto error;
+
+                if (VIR_APPEND_ELEMENT(def->source.caps.u.net.routes,
+                                       def->source.caps.u.net.nroutes, route) < 0) {
+                    VIR_FREE(route);
+                    goto error;
+                }
+            }
+        }
         break;
     default:
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -4924,6 +5011,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
     ret = 0;
  error:
     VIR_FREE(ipnodes);
+    VIR_FREE(routenodes);
     return ret;
 }
 
@@ -7367,6 +7455,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
     size_t i;
     size_t nips = 0;
     virDomainNetIpDefPtr *ips = NULL;
+    size_t nroutes = 0;
+    virDomainNetRouteDefPtr *routes = NULL;
 
     if (VIR_ALLOC(def) < 0)
         return NULL;
@@ -7463,6 +7553,13 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
 
                 if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0)
                     goto error;
+            } else if (xmlStrEqual(cur->name, BAD_CAST "route")) {
+                virDomainNetRouteDefPtr route = NULL;
+                if (!(route = virDomainNetRouteParse(cur)))
+                    goto error;
+
+                if (VIR_APPEND_ELEMENT(routes, nroutes, route) < 0)
+                    goto error;
             } else if (!ifname &&
                        xmlStrEqual(cur->name, BAD_CAST "target")) {
                 ifname = virXMLPropString(cur, "dev");
@@ -7773,6 +7870,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
         if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0)
             goto error;
     }
+    def->nroutes = nroutes;
+    def->routes = routes;
 
     if (script != NULL) {
         def->script = script;
@@ -17179,6 +17278,37 @@ virDomainNetIpsFormat(virBufferPtr buf, virDomainNetIpDefPtr *ips, size_t nips)
     }
 }
 
+static void
+virDomainNetRoutesFormat(virBufferPtr buf,
+                         virDomainNetRouteDefPtr *routes,
+                         size_t nroutes)
+{
+    size_t i;
+
+    for (i = 0; i < nroutes; i++) {
+        virDomainNetRouteDefPtr route = routes[i];
+        const char *familyStr = NULL;
+        char *via = virSocketAddrFormat(&route->via);
+        char *to = NULL;
+
+        if (VIR_SOCKET_ADDR_IS_FAMILY(&route->via, AF_INET6))
+            familyStr = "ipv6";
+        else if (VIR_SOCKET_ADDR_IS_FAMILY(&route->via, AF_INET))
+            familyStr = "ipv4";
+        virBufferAsprintf(buf, "<route family='%s' via='%s'", familyStr, via);
+
+        if (VIR_SOCKET_ADDR_VALID(&route->to)) {
+            to = virSocketAddrFormat(&route->to);
+            virBufferAsprintf(buf, " address='%s'", to);
+        }
+
+        if (route->prefix > 0)
+            virBufferAsprintf(buf, " prefix='%d'", route->prefix);
+
+        virBufferAddLit(buf, "/>\n");
+    }
+}
+
 static int
 virDomainHostdevDefFormatSubsys(virBufferPtr buf,
                                 virDomainHostdevDefPtr def,
@@ -17334,6 +17464,8 @@ virDomainHostdevDefFormatCaps(virBufferPtr buf,
     if (def->source.caps.type == VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET) {
         virDomainNetIpsFormat(buf, def->source.caps.u.net.ips,
                               def->source.caps.u.net.nips);
+        virDomainNetRoutesFormat(buf, def->source.caps.u.net.routes,
+                                 def->source.caps.u.net.nroutes);
     }
 
     return 0;
@@ -17718,6 +17850,7 @@ virDomainNetDefFormat(virBufferPtr buf,
     }
 
     virDomainNetIpsFormat(buf, def->ips, def->nips);
+    virDomainNetRoutesFormat(buf, def->routes, def->nroutes);
 
     virBufferEscapeString(buf, "<script path='%s'/>\n",
                           def->script);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 9292613..ac1f4f8 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -485,6 +485,14 @@ struct _virDomainNetIpDef {
     unsigned int prefix; /* number of 1 bits in the net mask */
 };
 
+typedef struct _virDomainNetRouteDef virDomainNetRouteDef;
+typedef virDomainNetRouteDef *virDomainNetRouteDefPtr;
+struct _virDomainNetRouteDef {
+    virSocketAddr via;
+    virSocketAddr to;
+    unsigned int prefix;
+};
+
 typedef struct _virDomainHostdevCaps virDomainHostdevCaps;
 typedef virDomainHostdevCaps *virDomainHostdevCapsPtr;
 struct _virDomainHostdevCaps {
@@ -500,6 +508,8 @@ struct _virDomainHostdevCaps {
             char *iface;
             size_t nips;
             virDomainNetIpDefPtr *ips;
+            size_t nroutes;
+            virDomainNetRouteDefPtr *routes;
         } net;
     } u;
 };
@@ -1002,6 +1012,8 @@ struct _virDomainNetDef {
     int linkstate;
     size_t nips;
     virDomainNetIpDefPtr *ips;
+    size_t nroutes;
+    virDomainNetRouteDefPtr *routes;
 };
 
 /* Used for prefix of ifname of any network name generated dynamically
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 64c14e0..ef96b2b 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -992,8 +992,33 @@ virNetDevAddRoute(const char *ifname,
     void *addrData = NULL;
     size_t addrDataLen;
     int errCode;
+    virSocketAddr defaultAddr;
+    virSocketAddrPtr actualAddr;
+    char *toStr = NULL;
+    char *viaStr = NULL;
+
+    actualAddr = addr;
+
+    /* If we have no valid network address, then use the default one */
+    if (!addr || !VIR_SOCKET_ADDR_VALID(addr)) {
+        VIR_DEBUG("computing default address");
+        int family = VIR_SOCKET_ADDR_FAMILY(gateway);
+        if (family == AF_INET) {
+            if (virSocketAddrParseIPv4(&defaultAddr, VIR_SOCKET_ADDR_IPV4_ALL) < 0)
+                goto cleanup;
+        } else {
+            if (virSocketAddrParseIPv6(&defaultAddr, VIR_SOCKET_ADDR_IPV6_ALL) < 0)
+                goto cleanup;
+        }
+
+        actualAddr = &defaultAddr;
+    }
+
+    toStr = virSocketAddrFormat(actualAddr);
+    viaStr = virSocketAddrFormat(gateway);
+    VIR_DEBUG("Adding route %s/%d via %s", toStr, prefix, viaStr);
 
-    if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0 ||
+    if (virNetDevGetIPAddressBinary(actualAddr, &addrData, &addrDataLen) < 0 ||
         virNetDevGetIPAddressBinary(gateway, &gatewayData, &addrDataLen) < 0)
         goto cleanup;
 
@@ -1010,7 +1035,7 @@ virNetDevAddRoute(const char *ifname,
 
     memset(&rtmsg, 0, sizeof(rtmsg));
 
-    rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(addr);
+    rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(gateway);
     rtmsg.rtm_table = RT_TABLE_MAIN;
     rtmsg.rtm_scope = RT_SCOPE_UNIVERSE;
     rtmsg.rtm_protocol = RTPROT_BOOT;
@@ -1043,6 +1068,8 @@ virNetDevAddRoute(const char *ifname,
 
     ret = 0;
  cleanup:
+    VIR_FREE(toStr);
+    VIR_FREE(viaStr);
     nlmsg_free(nlmsg);
     return ret;
 
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 19e17cf..fb7988f 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -96,7 +96,7 @@ int virNetDevAddRoute(const char *ifname,
                       unsigned int prefix,
                       virSocketAddrPtr gateway,
                       unsigned int metric)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4)
     ATTRIBUTE_RETURN_CHECK;
 int virNetDevClearIPAddress(const char *ifname,
                             virSocketAddr *addr,
diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h
index ba8c912..99ab46f 100644
--- a/src/util/virsocketaddr.h
+++ b/src/util/virsocketaddr.h
@@ -55,6 +55,8 @@ typedef struct {
     ((s)->data.sa.sa_family)
 
 # define VIR_SOCKET_ADDR_DEFAULT_PREFIX 24
+# define VIR_SOCKET_ADDR_IPV4_ALL "0.0.0.0"
+# define VIR_SOCKET_ADDR_IPV6_ALL "::"
 
 typedef virSocketAddr *virSocketAddrPtr;
 
diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml
index 0596789..61e8655 100644
--- a/tests/lxcxml2xmldata/lxc-hostdev.xml
+++ b/tests/lxcxml2xmldata/lxc-hostdev.xml
@@ -37,6 +37,8 @@
       </source>
       <ip address='192.168.122.2' family='ipv4'/>
       <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='24'/>
+      <route family='ipv4' via='192.168.122.1'/>
+      <route family='ipv6' via='2003:db8:1:0:214:1234:fe0b:3595'/>
     </hostdev>
   </devices>
 </domain>
diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml
index d011927..2b04a65 100644
--- a/tests/lxcxml2xmldata/lxc-idmap.xml
+++ b/tests/lxcxml2xmldata/lxc-idmap.xml
@@ -30,6 +30,8 @@
       <source bridge='bri0'/>
       <ip address='192.168.122.12' family='ipv4' prefix='24'/>
       <ip address='192.168.122.13' family='ipv4' prefix='24'/>
+      <route family='ipv4' via='192.168.122.1'/>
+      <route family='ipv4' via='192.168.124.1' address='192.168.124.0' prefix='24'/>
       <target dev='veth0'/>
       <guest dev='eth2'/>
     </interface>
-- 
2.1.2




More information about the libvir-list mailing list