Domain network devices can now have a <route> element

Network interfaces devices and host devices with net capabilities can
now have IPv4 and/or an IPv6 routes configured.
This commit is contained in:
Cédric Bosdonnat 2014-07-24 12:16:28 +02:00
parent 7100be40a5
commit c9a641f1e5
9 changed files with 230 additions and 6 deletions

View File

@ -4328,14 +4328,18 @@ qemu-kvm -net nic,model=? /dev/null
&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>
<b>&lt;ip address='192.168.122.5' prefix='24'/&gt;</b>
<b>&lt;route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/&gt;</b>
<b>&lt;route family='ipv4' via='192.168.122.1'/&gt;</b>
&lt;/interface&gt;
...
&lt;hostdev mode='capabilities' type='net'&gt;
&lt;source&gt;
&lt;interface&gt;eth0&lt;/interface&gt;
&lt;/source&gt;
<b>&lt;ip family='ipv4' address='192.168.122.6' prefix='24'/&gt;</b>
<b>&lt;ip address='192.168.122.6' prefix='24'/&gt;</b>
<b>&lt;route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/&gt;</b>
<b>&lt;route family='ipv4' via='192.168.122.1'/&gt;</b>
&lt;/hostdev&gt;
&lt;/devices&gt;
@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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

View File

@ -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;
if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0 ||
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(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;

View File

@ -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,

View File

@ -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;

View File

@ -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>

View File

@ -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>