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