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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list