diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index d72bd0a9f36dfff1a7609de33e851d28f562bc32..a1198ce6beecadc65e35d512c1c92ed5c6d6e04a 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -546,6 +546,62 @@
starting.
+
+ Static route definitions are used to provide routing information
+ to the virtualization host for networks which are not directly
+ reachable from the virtualization host, but *are* reachable from
+ a guest domain that is itself reachable from the
+ host since 1.0.6.
+
+
+
+ Here is a fragment of a definition which shows the static
+ route specification as well as the IPv4 and IPv6 definitions
+ for network addresses which are referred to in the
+ gateway
gateway address specifications. Note
+ that the third static route specification includes the
+ metric
attribute specification with a value of 2.
+ This particular route would *not* be preferred if there was
+ another existing rout on the system with the same address and
+ prefix but with a lower value for the metric. If there is a
+ route in the host system configuration that should be overriden
+ by a route in a virtual network whenever the virtual network is
+ running, the configuration for the system-defined route should
+ be modified to have a higher metric, and the route on the
+ virtual network given a lower metric (for example, the default
+ metric of "1").
+
+
+
@@ -577,6 +633,7 @@
</dhcp>
</ip>
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
+ <route family="ipv6" address="2001:db9:ca1:1::" prefix="64" gateway="2001:db8:ca2:2::2" />
</network>
@@ -826,6 +883,33 @@
</ip>
</network>
+
+ Below is yet another IPv6 variation. This variation has only
+ IPv6 defined with DHCPv6 on the primary IPv6 network. A static
+ link if defined for a second IPv6 network which will not be
+ directly visible on the bridge interface but there will be a
+ static route defined for this network via the specified
+ gateway. Note that the gateway address must be directly
+ reachable via (on the same subnet as) one of the <ip>
+ addresses defined for this <network>.
+ Since 1.0.6
+
+
+
+ <network>
+ <name>net7</name>
+ <bridge name="virbr7" />
+ <forward mode="route"/>
+ <ip family="ipv6" address="2001:db8:ca2:7::1" prefix="64" >
+ <dhcp>
+ <range start="2001:db8:ca2:7::100" end="2001:db8:ca2::1ff" />
+ <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="lucas" ip="2001:db8:ca2:2:3::4" />
+ </dhcp>
+ </ip>
+ <route family="ipv6" address="2001:db8:ca2:8::" prefix="64" gateway="2001:db8:ca2:7::4" >
+ </route>
+ </network>
+
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 493edaeb6489be0dc565461e9bffc344dd4e9c7c..ded858092aebf5e1bd097cf42d597b5a51f40c26 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -316,6 +316,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 0c0e58eb6e89ce9ab1d8404e6ebbc01d0bbd8165..eb10c0a82bbc021624ed6f326a300d2618ca231e 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -142,6 +142,12 @@ virNetworkIpDefClear(virNetworkIpDefPtr def)
VIR_FREE(def->bootfile);
}
+static void
+virNetworkRouteDefClear(virNetworkRouteDefPtr def)
+{
+ VIR_FREE(def->family);
+}
+
static void
virNetworkDNSTxtDefClear(virNetworkDNSTxtDefPtr def)
{
@@ -221,6 +227,11 @@ virNetworkDefFree(virNetworkDefPtr def)
}
VIR_FREE(def->ips);
+ for (ii = 0 ; ii < def->nroutes && def->routes ; ii++) {
+ virNetworkRouteDefClear(&def->routes[ii]);
+ }
+ VIR_FREE(def->routes);
+
for (ii = 0; ii < def->nPortGroups && def->portGroups; ii++) {
virPortGroupDefClear(&def->portGroups[ii]);
}
@@ -1269,6 +1280,233 @@ cleanup:
return result;
}
+static int
+virNetworkRouteDefParseXML(const char *networkName,
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
+ virNetworkRouteDefPtr def)
+{
+ /*
+ * virNetworkRouteDef object is already allocated as part
+ * of an array. On failure clear: it out, but don't free it.
+ */
+
+ xmlNodePtr save;
+ char *address = NULL, *netmask = NULL;
+ char *gateway = NULL;
+ unsigned long prefix = 0, metric = 0;
+ int result = -1;
+ int prefixRc, metricRc;
+ virSocketAddr testAddr;
+
+ save = ctxt->node;
+ ctxt->node = node;
+
+ /* grab raw data from XML */
+ def->family = virXPathString("string(./@family)", ctxt);
+ address = virXPathString("string(./@address)", ctxt);
+ netmask = virXPathString("string(./@netmask)", ctxt);
+ gateway = virXPathString("string(./@gateway)", ctxt);
+ prefixRc = virXPathULong("string(./@prefix)", ctxt, &prefix);
+ if (prefixRc == -2) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid prefix specified "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+ def->has_prefix = (prefixRc == 0);
+ def->prefix = prefix;
+ metricRc = virXPathULong("string(./@metric)", ctxt, &metric);
+ if (metricRc == -2) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid metric specified "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+ if (metricRc == 0) {
+ def->has_metric = true;
+ if (metric == 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid metric value, must be > 0 "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+ }
+ def->metric = metric;
+
+ /* Note: both network and gateway addresses must be specified */
+
+ if (!address) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing required address attribute "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+
+ if (!gateway) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing required gateway attribute "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+
+ if (virSocketAddrParse(&def->address, address, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Bad network address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+
+ if (virSocketAddrParse(&def->gateway, gateway, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Bad gateway address '%s' "
+ "in route definition of network '%s'"),
+ gateway, networkName);
+ goto cleanup;
+ }
+
+ /* validate network address, etc. for each family */
+ if ((def->family == NULL) || (STREQ(def->family, "ipv4"))) {
+ if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ||
+ VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_UNSPEC))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ def->family == NULL ?
+ _("No family specified for non-IPv4 address '%s' "
+ "in route definition of network '%s'") :
+ _("IPv4 family specified for non-IPv4 address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ def->family == NULL ?
+ _("No family specified for non-IPv4 gateway '%s' "
+ "in route definition of network '%s'") :
+ _("IPv4 family specified for non-IPv4 gateway '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (netmask) {
+ if (virSocketAddrParse(&def->netmask, netmask, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Bad netmask address '%s' "
+ "in route definition of network '%s'"),
+ netmask, networkName);
+ goto cleanup;
+ }
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->netmask, AF_INET)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Network '%s' has invalid netmask '%s' "
+ "for address '%s' (both must be IPv4)"),
+ networkName, netmask, address);
+ goto cleanup;
+ }
+ if (def->has_prefix) {
+ /* can't have both netmask and prefix at the same time */
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Route definition '%s' cannot have both "
+ "a prefix and a netmask"),
+ networkName);
+ goto cleanup;
+ }
+ }
+ if (def->prefix > 32) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid prefix %u specified "
+ "in route definition of network '%s', "
+ "must be 0 - 32"),
+ def->prefix, networkName);
+ goto cleanup;
+ }
+ } else if (STREQ(def->family, "ipv6")) {
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("ipv6 family specified for non-IPv6 address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (netmask) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Specifying netmask invalid for IPv6 address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET6)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("ipv6 specified for non-IPv6 gateway address '%s' "
+ "in route definition of network '%s'"),
+ gateway, networkName);
+ goto cleanup;
+ }
+ if (def->prefix > 128) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid prefix %u specified "
+ "in route definition of network '%s', "
+ "must be 0 - 128"),
+ def->prefix, networkName);
+ goto cleanup;
+ }
+ } else {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Unrecognized family '%s' "
+ "in route definition of network'%s'"),
+ def->family, networkName);
+ goto cleanup;
+ }
+
+ /* make sure the address is a network address */
+ if (netmask) {
+ if (virSocketAddrMask(&def->address, &def->netmask, &testAddr) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("error converting address '%s' with netmask '%s' "
+ "to network-address "
+ "in route definition of network '%s'"),
+ address, netmask, networkName);
+ goto cleanup;
+ }
+ } else {
+ if (virSocketAddrMaskByPrefix(&def->address,
+ def->prefix, &testAddr) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("error converting address '%s' with prefix %u "
+ "to network-address "
+ "in route definition of network '%s'"),
+ address, def->prefix, networkName);
+ goto cleanup;
+ }
+ }
+ if (!virSocketAddrEqual(&def->address, &testAddr)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("address '%s' in route definition of network '%s' "
+ "is not a network address"),
+ address, networkName);
+ goto cleanup;
+ }
+
+ result = 0;
+
+cleanup:
+ if (result < 0) {
+ virNetworkRouteDefClear(def);
+ }
+ VIR_FREE(address);
+ VIR_FREE(netmask);
+ VIR_FREE(gateway);
+
+ ctxt->node = save;
+ return result;
+}
+
static int
virNetworkPortGroupParseXML(virPortGroupDefPtr def,
xmlNodePtr node,
@@ -1684,8 +1922,9 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
char *tmp;
char *stp = NULL;
xmlNodePtr *ipNodes = NULL;
+ xmlNodePtr *routeNodes = NULL;
xmlNodePtr *portGroupNodes = NULL;
- int nIps, nPortGroups;
+ int nIps, nPortGroups, nRoutes;
xmlNodePtr dnsNode = NULL;
xmlNodePtr virtPortNode = NULL;
xmlNodePtr forwardNode = NULL;
@@ -1839,6 +2078,69 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
}
VIR_FREE(ipNodes);
+ nRoutes = virXPathNodeSet("./route", ctxt, &routeNodes);
+ if (nRoutes < 0)
+ goto error;
+
+ if (nRoutes > 0) {
+ int ii;
+
+ /* allocate array to hold all the route definitions */
+ if (VIR_ALLOC_N(def->routes, nRoutes) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+ /* parse each definition */
+ for (ii = 0; ii < nRoutes; ii++) {
+ int ret = virNetworkRouteDefParseXML(def->name, routeNodes[ii],
+ ctxt, &def->routes[ii]);
+ if (ret < 0)
+ goto error;
+ def->nroutes++;
+ }
+
+ /* now validate the correctness of any static route gateways specified
+ *
+ * note: the parameters within each definition are verified/assumed valid;
+ * the question being asked and answered here is if the specified gateway
+ * is directly reachable from this bridge.
+ */
+ nRoutes = def->nroutes;
+ nIps = def->nips;
+ for (ii = 0; ii < nRoutes; ii++) {
+ int jj;
+ virSocketAddr testAddr, testGw;
+ bool addrMatch;
+ virNetworkRouteDefPtr gwdef = &def->routes[ii];
+ addrMatch = false;
+ for (jj = 0; jj < nIps; jj++) {
+ virNetworkIpDefPtr def2 = &def->ips[jj];
+ if (VIR_SOCKET_ADDR_FAMILY(&gwdef->gateway)
+ != VIR_SOCKET_ADDR_FAMILY(&def2->address)) {
+ continue;
+ }
+ int prefix = virNetworkIpDefPrefix(def2);
+ virSocketAddrMaskByPrefix(&def2->address, prefix, &testAddr);
+ virSocketAddrMaskByPrefix(&gwdef->gateway, prefix, &testGw);
+ if (VIR_SOCKET_ADDR_VALID(&testAddr) &&
+ VIR_SOCKET_ADDR_VALID(&testGw) &&
+ virSocketAddrEqual(&testAddr, &testGw)) {
+ addrMatch = true;
+ break;
+ }
+ }
+ if (!addrMatch) {
+ char *gw = virSocketAddrFormat(&gwdef->gateway);
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unreachable static route gateway '%s' specified for network '%s'"),
+ gw, def->name);
+ VIR_FREE(gw);
+ goto error;
+ }
+ }
+ }
+ VIR_FREE(routeNodes);
+
forwardNode = virXPathNode("./forward", ctxt);
if (forwardNode &&
virNetworkForwardDefParseXML(def->name, forwardNode, ctxt, &def->forward) < 0) {
@@ -1911,6 +2213,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
return def;
error:
+ VIR_FREE(routeNodes);
VIR_FREE(stp);
virNetworkDefFree(def);
VIR_FREE(ipNodes);
@@ -2135,6 +2438,53 @@ error:
return result;
}
+static int
+virNetworkRouteDefFormat(virBufferPtr buf,
+ const virNetworkRouteDefPtr def)
+{
+ int result = -1;
+
+ virBufferAddLit(buf, "family) {
+ virBufferAsprintf(buf, " family='%s'", def->family);
+ }
+ if (VIR_SOCKET_ADDR_VALID(&def->address)) {
+ char *addr = virSocketAddrFormat(&def->address);
+
+ if (!addr)
+ goto error;
+ virBufferAsprintf(buf, " address='%s'", addr);
+ VIR_FREE(addr);
+ }
+ if (VIR_SOCKET_ADDR_VALID(&def->netmask)) {
+ char *addr = virSocketAddrFormat(&def->netmask);
+
+ if (!addr)
+ goto error;
+ virBufferAsprintf(buf, " netmask='%s'", addr);
+ VIR_FREE(addr);
+ }
+ if (def->has_prefix) {
+ virBufferAsprintf(buf," prefix='%u'", def->prefix);
+ }
+ if (VIR_SOCKET_ADDR_VALID(&def->gateway)) {
+ char *addr = virSocketAddrFormat(&def->gateway);
+ if (!addr)
+ goto error;
+ virBufferAsprintf(buf, " gateway='%s'", addr);
+ VIR_FREE(addr);
+ }
+ if (def->has_metric && def->metric > 0) {
+ virBufferAsprintf(buf," metric='%u'", def->metric);
+ }
+ virBufferAddLit(buf, "/>\n");
+
+ result = 0;
+error:
+ return result;
+}
+
static int
virPortGroupDefFormat(virBufferPtr buf,
const virPortGroupDefPtr def)
@@ -2347,6 +2697,11 @@ virNetworkDefFormatInternal(virBufferPtr buf,
goto error;
}
+ for (ii = 0; ii < def->nroutes; ii++) {
+ if (virNetworkRouteDefFormat(buf, &def->routes[ii]) < 0)
+ goto error;
+ }
+
if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
goto error;
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index e187f0588cc2faae9a0d990ed2be29527b0e8b1c..43f80d4ebdbb85c0a239d489d6bdd0dcf58ee706 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -149,6 +149,25 @@ struct _virNetworkIpDef {
virSocketAddr bootserver;
};
+typedef struct _virNetworkRouteDef virNetworkRouteDef;
+typedef virNetworkRouteDef *virNetworkRouteDefPtr;
+struct _virNetworkRouteDef {
+ char *family; /* ipv4 or ipv6 - default is ipv4 */
+ virSocketAddr address; /* Routed Network IP address */
+
+ /* One or the other of the following two will be used for a given
+ * Network address, but never both. The parser guarantees this.
+ * The virSocketAddrGetIpPrefix() can be used to get a
+ * valid prefix.
+ */
+ virSocketAddr netmask; /* ipv4 - either netmask or prefix specified */
+ unsigned int prefix; /* ipv6 - only prefix allowed */
+ bool has_prefix; /* prefix= was specified */
+ unsigned int metric; /* value for metric (defaults to 1) */
+ bool has_metric; /* metric= was specified */
+ virSocketAddr gateway; /* gateway IP address for ip-route */
+ };
+
typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr;
struct _virNetworkForwardIfDef {
@@ -224,6 +243,9 @@ struct _virNetworkDef {
size_t nips;
virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
+ size_t nroutes;
+ virNetworkRouteDefPtr routes; /* ptr to array of static routes on this interface */
+
virNetworkDNSDef dns; /* dns related configuration */
virNetDevVPortProfilePtr virtPortProfile;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 2e2387e420a4f3e0701615ae1b57f6fbb530efb6..94c02abfe05f33befbb071a33e8a1a7f2a3dae00 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1498,6 +1498,7 @@ virMacAddrSetRaw;
# util/virnetdev.h
+virNetDevAddRoute;
virNetDevClearIPv4Address;
virNetDevExists;
virNetDevGetIndex;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 99c1316e0baf64b2b71a2292ea08cb08fd73824b..6c6ce6dd4c335ad7ad98e4df9719a0f26c90e9cc 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2390,6 +2390,7 @@ out:
return ret;
}
+/* add an IP address to a bridge */
static int
networkAddAddrToBridge(virNetworkObjPtr network,
virNetworkIpDefPtr ipdef)
@@ -2410,6 +2411,55 @@ networkAddAddrToBridge(virNetworkObjPtr network,
return 0;
}
+/* add an IP (static) route to a bridge */
+static int
+networkAddRouteToBridge(virNetworkObjPtr network,
+ virNetworkRouteDefPtr routedef)
+{
+ int prefix = 0;
+ unsigned int metric;
+ virSocketAddrPtr addr = &routedef->address;
+ virSocketAddrPtr mask = &routedef->netmask;
+ virSocketAddr zero;
+
+ /* this creates an all-0 address of the appropriate family */
+ ignore_value(virSocketAddrParse(&zero,
+ (VIR_SOCKET_ADDR_IS_FAMILY(addr,AF_INET)
+ ? "0.0.0.0" : "::"),
+ VIR_SOCKET_ADDR_FAMILY(addr)));
+
+ if (virSocketAddrEqual(addr, &zero)) {
+ if (routedef->has_prefix && routedef->prefix == 0)
+ prefix = 0;
+ else if ((VIR_SOCKET_ADDR_IS_FAMILY(mask, AF_INET) &&
+ virSocketAddrEqual(mask, &zero)))
+ prefix = 0;
+ else
+ prefix = virSocketAddrGetIpPrefix(addr, mask, routedef->prefix);
+ } else {
+ prefix = virSocketAddrGetIpPrefix(addr, mask, routedef->prefix);
+ }
+
+ if (prefix < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' has an invalid netmask "
+ "or IP address in route definition"),
+ network->def->name);
+ return -1;
+ }
+
+ if (routedef->has_metric && routedef->metric > 0)
+ metric = routedef->metric;
+ else
+ metric = 1;
+
+ if (virNetDevAddRoute(network->def->bridge, &routedef->address,
+ prefix, &routedef->gateway, metric) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
static int
networkStartNetworkVirtual(struct network_driver *driver,
virNetworkObjPtr network)
@@ -2418,6 +2468,7 @@ networkStartNetworkVirtual(struct network_driver *driver,
bool v4present = false, v6present = false;
virErrorPtr save_err = NULL;
virNetworkIpDefPtr ipdef;
+ virNetworkRouteDefPtr routedef;
char *macTapIfName = NULL;
int tapfd = -1;
@@ -2494,6 +2545,19 @@ networkStartNetworkVirtual(struct network_driver *driver,
if (virNetDevSetOnline(network->def->bridge, 1) < 0)
goto err2;
+ for (ii = 0; ii < network->def->nroutes; ii++) {
+ routedef = &network->def->routes[ii];
+ /* Add the IP route to the bridge */
+ /* ignore errors, error msg will be generated */
+ /* but libvirt will not know and net-destroy will work. */
+ if (VIR_SOCKET_ADDR_VALID(&routedef->gateway)) {
+ if (networkAddRouteToBridge(network, routedef) < 0) {
+ /* an error occurred adding the static route */
+ continue; /* for now, do nothing */
+ }
+ }
+ }
+
/* If forward.type != NONE, turn on global IP forwarding */
if (network->def->forward.type != VIR_NETWORK_FORWARD_NONE &&
networkEnableIpForwarding(v4present, v6present) < 0) {
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 251a66ae6cfb8539d6d7f540a51af41e6a63a084..cee40013ad38387c9e96072c2dbd39f5b00d6f65 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -821,6 +821,52 @@ cleanup:
return ret;
}
+/**
+ * virNetDevAddRoute:
+ * @ifname: the interface name
+ * @addr: the IP network address (IPv4 or IPv6)
+ * @prefix: number of 1 bits in the netmask
+ * @gateway: via address for route (same as @addr)
+ *
+ * Add a route for a network IP address to an interface. This function
+ * *does not* remove any previously added IP static routes.
+ *
+ * Returns 0 in case of success or -1 in case of error.
+ */
+
+int
+virNetDevAddRoute(const char *ifname,
+ virSocketAddrPtr addr,
+ unsigned int prefix,
+ virSocketAddrPtr gateway,
+ unsigned int metric)
+{
+ virCommandPtr cmd = NULL;
+ char *addrstr = NULL, *gatewaystr = NULL;
+ int ret = -1;
+
+ if (!(addrstr = virSocketAddrFormat(addr)))
+ goto cleanup;
+ if (!(gatewaystr = virSocketAddrFormat(gateway)))
+ goto cleanup;
+ cmd = virCommandNew(IP_PATH);
+ virCommandAddArgList(cmd, "route", "add", NULL);
+ virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix);
+ virCommandAddArgList(cmd, "via", gatewaystr, "dev", ifname,
+ "proto", "static", "metric", NULL);
+ virCommandAddArgFormat(cmd, "%u", metric);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ ret = 0;
+cleanup:
+ VIR_FREE(addrstr);
+ VIR_FREE(gatewaystr);
+ virCommandFree(cmd);
+ return ret;
+}
+
/**
* virNetDevClearIPv4Address:
* @ifname: the interface name
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 551ea222278a45f997000e4bcbf00eee630850df..0b394ad9828c5da2af6feabdd7088e16180d668b 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -42,6 +42,13 @@ int virNetDevSetIPv4Address(const char *ifname,
virSocketAddr *addr,
unsigned int prefix)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+int virNetDevAddRoute(const char *ifname,
+ virSocketAddrPtr addr,
+ unsigned int prefix,
+ virSocketAddrPtr gateway,
+ unsigned int metric)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4)
+ ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
int virNetDevClearIPv4Address(const char *ifname,
virSocketAddr *addr,
unsigned int prefix)
diff --git a/tests/networkxml2xmlin/dhcp6host-routed-network.xml b/tests/networkxml2xmlin/dhcp6host-routed-network.xml
index 2693d872fc3c4da71f9d5caa819424962dd66f3c..40f6dfe90f8e4d2b7e1bde2949b24fa28a41e743 100644
--- a/tests/networkxml2xmlin/dhcp6host-routed-network.xml
+++ b/tests/networkxml2xmlin/dhcp6host-routed-network.xml
@@ -19,4 +19,6 @@
+
+
diff --git a/tests/networkxml2xmlout/dhcp6host-routed-network.xml b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
index 1d3035ba37fd9547a74a640810e697d8d3525e69..fc8666b2580bff96f394ed4b7f16ba4b5bf22d1f 100644
--- a/tests/networkxml2xmlout/dhcp6host-routed-network.xml
+++ b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
@@ -21,4 +21,6 @@
+
+