[libvirt] [PATCH] v2:Support for adding a static route to a bridge
by Gene Czarcinski
This patch adds support for adding a static route for
a network. The "via" specifies the gateway's IP
address. Both IPv4 and IPv6 static routes are
supported although it is expected that this
functionality will have more use with IPv6.
Extensive tests are done to validate that the input
definitions are correct. For example, for a static
route ip definition, the address must be for a network
and not a host. Additional checks are added to ensure
that the specified gateway has a network defined on
this bridge.
Whan a static route is added to a bridge, there is a slight
possibility that the gateway address will be incorrect. If
this is handled as an error, that bridge becomes unusable and
can only be recovered by rebooting. If the error is
ignored, then that network can be destroyed and the network
definition file edited to correct the problem. Unfortunately,
the error message only appears in syslog. However, with
the checks performed when the network definition file is parsed,
it is unlikely that this condition will ever occur.
The command used is of the following form:
ip route add <address>/<prefix> via <gateway> dev <virbr-bridge> \
proto static metric 1
.
Signed-off-by: Gene Czarcinski <gene(a)czarc.net>
---
docs/formatnetwork.html.in | 32 +++-
docs/schemas/network.rng | 3 +
src/conf/network_conf.c | 168 ++++++++++++++++++++-
src/conf/network_conf.h | 2 +
src/libvirt_private.syms | 1 +
src/network/bridge_driver.c | 42 ++++++
src/util/virnetdev.c | 47 ++++++
src/util/virnetdev.h | 5 +
.../networkxml2xmlin/dhcp6host-routed-network.xml | 4 +
.../networkxml2xmlout/dhcp6host-routed-network.xml | 4 +
10 files changed, 304 insertions(+), 4 deletions(-)
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 4dd0415..f0cadf0 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -537,7 +537,9 @@
associated with the virtual network, and optionally enable DHCP
services. These elements are only valid for isolated networks
(no <code>forward</code> element specified), and for those with
- a forward mode of 'route' or 'nat'.
+ a forward mode of 'route' or 'nat'. Another optional addressing
+ element <code>via</code> can be used to establish a static
+ route for IPv4 or IPv6 networks.
</p>
<pre>
@@ -560,6 +562,7 @@
</dhcp>
</ip>
<ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
+ <ip family="ipv6" address="2001:db9:ca1:1::" prefix="64" via="2001:db8:ca2:2::2" />
</network></pre>
<dl>
@@ -633,7 +636,10 @@
IPv6, multiple addresses on a single network, <code>family</code>, and
<code>prefix</code> are support <span class="since">Since 0.8.7</span>.
Similar to IPv4, one IPv6 address per network can also have
- a <code>dhcp</code> definition. <span class="since">Since 1.0.1</span>
+ a <code>dhcp</code> definition. <span class="since">Since 1.0.1</span> The <code>via</code>
+ can be used to establish a static route for IPv4 or IPv6 networks. It is an error
+ to specify <code>via</code> and <code>dhcp</code> for the same IP address.
+ <span class="since">Since 1.0.4</span>
<dl>
<dt><code>tftp</code></dt>
@@ -809,6 +815,28 @@
</ip>
</network></pre>
+ <p>
+ Below is yet another IPv6 variation. This variation has only IPv6
+ defined with DHCPv6 on the primary IPv6 network. A second IPv6
+ network has a static link to a host on the first (virtual) IPv6
+ network. <span class="since">Since 1.0.4</span>
+ </p>
+
+ <pre>
+ <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>
+ <ip family="ipv6" address="2001:db8:ca2:8::" prefix="64" via="2001:db8:ca2::4" >
+ </ip>
+ </network></pre>
+
<h3><a name="examplesPrivate">Isolated network config</a></h3>
<p>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 6c3fae2..c1cca23 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -262,6 +262,9 @@
<attribute name="family"><ref name="addr-family"/></attribute>
</optional>
<optional>
+ <attribute name="via"><ref name="ipAddr"/></attribute>
+ </optional>
+ <optional>
<element name="tftp">
<attribute name="root"><text/></attribute>
</element>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index c022fe4..ea98d85 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1149,8 +1149,10 @@ virNetworkIPDefParseXML(const char *networkName,
xmlNodePtr cur, save;
char *address = NULL, *netmask = NULL;
- unsigned long prefix;
+ char *gateway = NULL;
+ unsigned long prefix = 0;
int result = -1;
+ virSocketAddr testAddr;
save = ctxt->node;
ctxt->node = node;
@@ -1162,6 +1164,7 @@ virNetworkIPDefParseXML(const char *networkName,
def->prefix = 0;
else
def->prefix = prefix;
+ gateway = virXPathString("string(./@via)", ctxt);
netmask = virXPathString("string(./@netmask)", ctxt);
@@ -1175,7 +1178,17 @@ virNetworkIPDefParseXML(const char *networkName,
}
- /* validate family vs. address */
+ if (gateway) {
+ if (virSocketAddrParse(&def->gateway, gateway, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Bad gateway address '%s' in definition of network '%s'"),
+ gateway, networkName);
+ goto cleanup;
+ }
+
+ }
+
+ /* validate family vs. address (and gateway address too) */
if (def->family == NULL) {
if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ||
VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_UNSPEC))) {
@@ -1184,6 +1197,20 @@ virNetworkIPDefParseXML(const char *networkName,
address, networkName);
goto cleanup;
}
+ if (gateway &&
+ (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET) ||
+ VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_UNSPEC)))) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("no family specified for non-IPv4 gateway address '%s' in network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (def->prefix > 0 && def->prefix > 32) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("invalid prefix=%u specified for IPv4 address '%s' in network '%s'"),
+ def->prefix, address, networkName);
+ goto cleanup;
+ }
} else if (STREQ(def->family, "ipv4")) {
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -1191,6 +1218,18 @@ virNetworkIPDefParseXML(const char *networkName,
address, networkName);
goto cleanup;
}
+ if (gateway && (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET))) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("family 'ipv4' specified for non-IPv4 gateway address '%s' in network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (def->prefix > 0 && def->prefix > 32) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("invalid prefix=%u specified for IPv4 address '%s' in network '%s'"),
+ def->prefix, address, networkName);
+ goto cleanup;
+ }
} else if (STREQ(def->family, "ipv6")) {
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -1198,6 +1237,24 @@ virNetworkIPDefParseXML(const char *networkName,
address, networkName);
goto cleanup;
}
+ if (def->prefix == 0 || def->prefix > 64) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("invalid prefix=%u specified for IPv6 address '%s' in network '%s'"),
+ def->prefix, address, networkName);
+ goto cleanup;
+ }
+ if (netmask) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("specifying netmask invalid for IPv6 address '%s' in network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (gateway && (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET6))) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("family 'ipv6' specified for non-IPv6 gateway address '%s' in network '%s'"),
+ gateway, networkName);
+ goto cleanup;
+ }
} else {
virReportError(VIR_ERR_XML_ERROR,
_("Unrecognized family '%s' in definition of network '%s'"),
@@ -1241,16 +1298,55 @@ virNetworkIPDefParseXML(const char *networkName,
}
}
+ /* if static route gateway specified, make sure the address is a network address */
+ if (gateway) {
+ if (def->prefix > 0) {
+ if (virSocketAddrMaskByPrefix(&def->address, def->prefix, &testAddr) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("error converting address '%s' with prefix=%u to network-address in network '%s'"),
+ address, def->prefix, networkName);
+ goto cleanup;
+ }
+ }
+ if (netmask) {
+ if (virSocketAddrMask(&def->address, &def->netmask, &testAddr) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("error converting address '%s' with netmask '%s' to network-address in network '%s'"),
+ address, netmask, networkName);
+ goto cleanup;
+ }
+ }
+ if (!virSocketAddrEqual(&def->address, &testAddr)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("address '%s' in network '%s' is not a network-address"),
+ address, networkName);
+ goto cleanup;
+ }
+ }
+
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
+ if (gateway) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("<dhcp> element unsupported when static route (via) previously defined in network '%s'"),
+ networkName);
+ goto cleanup;
+ }
if (virNetworkDHCPDefParseXML(networkName, cur, def) < 0)
goto cleanup;
} else if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "tftp")) {
char *root;
+ if (gateway) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("<tftp> element unsupported when static route (via) previously defined in network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported <tftp> element in an IPv6 element in network '%s'"),
@@ -1274,6 +1370,7 @@ cleanup:
}
VIR_FREE(address);
VIR_FREE(netmask);
+ VIR_FREE(gateway);
ctxt->node = save;
return result;
@@ -1828,6 +1925,58 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
goto error;
def->nips++;
}
+ /* now validate the correctness of any static route gateways specified
+ *
+ * note: the parameters within each definition are varified/assumed valid;
+ * the question being asked and answered here is if the specified gateway
+ * address has a network definition on this bridge
+ */
+ for (ii = 0; ii < nIps; ii++) {
+ int jj;
+ virSocketAddr testAddr, testGw;
+ bool addrMatch;
+ virNetworkIpDefPtr gwdef = &def->ips[ii];
+ if (VIR_SOCKET_ADDR_VALID(&gwdef->gateway)) {
+ addrMatch = false;
+ for (jj = 0; jj < nIps; jj++) {
+ virNetworkIpDefPtr def2 = &def->ips[jj];
+ if (VIR_SOCKET_ADDR_VALID(&def2->gateway))
+ continue;
+ if ((VIR_SOCKET_ADDR_IS_FAMILY(&gwdef->gateway, AF_INET6)) &&
+ (!VIR_SOCKET_ADDR_IS_FAMILY(&def2->address, AF_INET6)))
+ continue;
+ if ((VIR_SOCKET_ADDR_IS_FAMILY(&def2->address, AF_INET6)) &&
+ (!VIR_SOCKET_ADDR_IS_FAMILY(&gwdef->gateway, AF_INET6)))
+ continue;
+ if ((VIR_SOCKET_ADDR_IS_FAMILY(&gwdef->gateway, AF_INET) ||
+ VIR_SOCKET_ADDR_IS_FAMILY(&gwdef->gateway, AF_UNSPEC)) &&
+ VIR_SOCKET_ADDR_IS_FAMILY(&def2->address, AF_INET6))
+ continue;
+ if (def2->prefix > 0) {
+ virSocketAddrMaskByPrefix(&def2->address, def2->prefix, &testAddr);
+ virSocketAddrMaskByPrefix(&gwdef->gateway, def2->prefix, &testGw);
+ }
+ if (VIR_SOCKET_ADDR_VALID(&def2->netmask)) {
+ virSocketAddrMask(&def2->address, &def2->netmask, &testAddr);
+ virSocketAddrMask(&gwdef->gateway, &def2->netmask, &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,
+ _("invalid static route gateway specified: '%s'"),
+ gw);
+ VIR_FREE(gw);
+ goto error;
+ }
+ }
+ }
}
VIR_FREE(ipNodes);
@@ -2108,6 +2257,7 @@ virNetworkIpDefFormat(virBufferPtr buf,
const virNetworkIpDefPtr def)
{
int result = -1;
+ bool routeFlg = false;
virBufferAddLit(buf, "<ip");
@@ -2131,15 +2281,29 @@ virNetworkIpDefFormat(virBufferPtr buf,
if (def->prefix > 0) {
virBufferAsprintf(buf," prefix='%u'", def->prefix);
}
+ if (VIR_SOCKET_ADDR_VALID(&def->gateway)) {
+ char *addr = virSocketAddrFormat(&def->gateway);
+ if (!addr)
+ goto error;
+ routeFlg = true;
+ virBufferAsprintf(buf, " via='%s'", addr);
+ VIR_FREE(addr);
+ }
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
if (def->tftproot) {
+ if (routeFlg) /* this should not occur */
+ goto error; /* but in case it does, better to error out */
virBufferEscapeString(buf, "<tftp root='%s' />\n",
def->tftproot);
}
if ((def->nranges || def->nhosts)) {
int ii;
+
+ if (routeFlg) /* this should not occur */
+ goto error; /* but in case it does, better to error out */
+
virBufferAddLit(buf, "<dhcp>\n");
virBufferAdjustIndent(buf, 2);
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 6ce9a00..91aff5f 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -115,6 +115,8 @@ struct _virNetworkIpDef {
char *family; /* ipv4 or ipv6 - default is ipv4 */
virSocketAddr address; /* Bridge IP address */
+ virSocketAddr gateway; /* gateway IP address for ip-route */
+
/* One or the other of the following two will be used for a given
* IP address, but never both. The parser guarantees this.
* Use virNetworkIpDefPrefix/virNetworkIpDefNetmask rather
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ed46479..988b97a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1470,6 +1470,7 @@ virNetDevReplaceMacAddress;
virNetDevReplaceNetConfig;
virNetDevRestoreMacAddress;
virNetDevRestoreNetConfig;
+virNetDevSetGateway;
virNetDevSetIPv4Address;
virNetDevSetMAC;
virNetDevSetMTU;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 31c8585..07058f3 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2360,6 +2360,7 @@ out:
return ret;
}
+/* add an IP address to a bridge */
static int
networkAddAddrToBridge(virNetworkObjPtr network,
virNetworkIpDefPtr ipdef)
@@ -2380,6 +2381,28 @@ networkAddAddrToBridge(virNetworkObjPtr network,
return 0;
}
+/* add an IP route to a bridge */
+static int
+networkAddRouteToBridge(virNetworkObjPtr network,
+ virNetworkIpDefPtr ipdef)
+{
+ int prefix = virNetworkIpDefPrefix(ipdef);
+
+ if (prefix < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("bridge '%s' has an invalid netmask or IP address"),
+ network->def->bridge);
+ return -1;
+ }
+
+ if (virNetDevSetGateway(network->def->bridge,
+ &ipdef->address,
+ prefix,
+ &ipdef->gateway) < 0)
+ return -1;
+ return 0;
+}
+
static int
networkStartNetworkVirtual(struct network_driver *driver,
virNetworkObjPtr network)
@@ -2446,9 +2469,12 @@ networkStartNetworkVirtual(struct network_driver *driver,
if (networkAddIptablesRules(driver, network) < 0)
goto err1;
+ /* first, do all of the addresses */
for (ii = 0;
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
ii++) {
+ if (VIR_SOCKET_ADDR_VALID(&ipdef->gateway))
+ continue; /* this is a static route specification */
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET))
v4present = true;
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
@@ -2464,6 +2490,22 @@ networkStartNetworkVirtual(struct network_driver *driver,
if (virNetDevSetOnline(network->def->bridge, 1) < 0)
goto err2;
+ /* then do all of the static routes */
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
+ 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(&ipdef->gateway)) {
+ if (networkAddRouteToBridge(network, ipdef) < 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 296871c..c90b3d2 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -729,6 +729,7 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED,
* Add an IP address to an interface. This function *does not* remove
* any previously added IP addresses - that must be done separately with
* brDelInetAddress.
+ * TODO: what is "brDelInetAddress"?
*
* Returns 0 in case of success or -1 in case of error.
*/
@@ -769,6 +770,52 @@ cleanup:
}
/**
+ * virNetDevSetGateway:
+ * @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 virNetDevSetGateway(const char *ifname,
+ virSocketAddr *addr,
+ unsigned int prefix,
+ virSocketAddr *gateway)
+{
+ 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", NULL);
+ virCommandAddArgFormat(cmd, "%s", gatewaystr);
+ virCommandAddArgList(cmd, "dev", ifname, NULL);
+ virCommandAddArgList(cmd, "proto", "static", "metric", NULL);
+ virCommandAddArgFormat(cmd, "%u", 1);
+
+ 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
* @addr: the IP address (IPv4 or IPv6)
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 06d0650..8b94ea8 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -42,6 +42,11 @@ int virNetDevSetIPv4Address(const char *ifname,
virSocketAddr *addr,
unsigned int prefix)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+int virNetDevSetGateway(const char *ifname,
+ virSocketAddr *addr,
+ unsigned int prefix,
+ virSocketAddr *gateway)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) 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 2693d87..dcad62d 100644
--- a/tests/networkxml2xmlin/dhcp6host-routed-network.xml
+++ b/tests/networkxml2xmlin/dhcp6host-routed-network.xml
@@ -19,4 +19,8 @@
<host id='0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66' name='badbob' ip='2001:db8:ac10:fd01::1:24' />
</dhcp>
</ip>
+ <ip address="192.168.222.0" netmask="255.255.255.0" via="192.168.122.10">
+ </ip>
+ <ip family="ipv6" address="2001:db8:ac10:fc00::" prefix="64" via="2001:db8:ac10:fd01::1:24">
+ </ip>
</network>
diff --git a/tests/networkxml2xmlout/dhcp6host-routed-network.xml b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
index 7305043..880c2dd 100644
--- a/tests/networkxml2xmlout/dhcp6host-routed-network.xml
+++ b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
@@ -21,4 +21,8 @@
<host id='0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66' name='badbob' ip='2001:db8:ac10:fd01::1:24' />
</dhcp>
</ip>
+ <ip address='192.168.222.0' netmask='255.255.255.0' via='192.168.122.10'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fc00::' prefix='64' via='2001:db8:ac10:fd01::1:24'>
+ </ip>
</network>
--
1.8.1.4
11 years, 7 months
[libvirt] [PATCH] Release VM lock before acquiring virDomainObjListPtr lock
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
When removing a VM from the virDomainObjListPtr, we must not
be holding the VM lock while acquiring the list lock. Re-order
code to ensure that we can release the VM lock early.
---
src/conf/domain_conf.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5e16ddf..d92e54a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2115,11 +2115,10 @@ void virDomainObjListRemove(virDomainObjListPtr doms,
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
- virObjectLock(doms);
virUUIDFormat(dom->def->uuid, uuidstr);
-
virObjectUnlock(dom);
+ virObjectLock(doms);
virHashRemoveEntry(doms->objs, uuidstr);
virObjectUnlock(doms);
}
--
1.8.1
11 years, 7 months
[libvirt] [PATCHv3 0/6] Fix memory corruption/crash in the connection close callback
by Peter Krempa
This series fixes the crash resulting from a race condition in the connection
close callback. To observe the crash apply the first patch only. To verify that
the patchset fixes the crash please apply all but 2/6 and verify using virsh.
2/6 fixes the crash in a redundant way in case the close callback is used. In
the case it isn't 2/6 itself can't fix the issue.
For a better explanation of this problem please see the description in 6/6.
Peter Krempa (4):
DO NOT APPLY UPSTREAM: Close callback race corruption crash
reproducer.
virsh: Move cmdConnect from virsh-host.c to virsh.c
virsh: Register and unregister the close callback also in cmdConnect
rpc: Fix connection close callback race condition and memory
corruption/crash
Viktor Mihajlovski (2):
libvirt: Increase connection reference count for callbacks
virsh: Unregister the connection close notifier upon termination
src/datatypes.c | 55 ++++++++++++++++++++----
src/datatypes.h | 22 +++++++---
src/libvirt.c | 30 ++++++++-----
src/remote/remote_driver.c | 62 ++++++++++++++++-----------
src/rpc/virnetclient.c | 9 +++-
tools/virsh-host.c | 67 -----------------------------
tools/virsh.c | 102 ++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 225 insertions(+), 122 deletions(-)
--
1.8.1.5
11 years, 7 months
[libvirt] remote connection issue 'virsh -c qemu+ssh:///root@localhost/system list'
by Yin Olivia-R63875
Hi,
I'm trying remote connection with qemu hypervisor on FSL PPC board.
The libvirt server is the PPC board.
root@ppc:~# ifconfig eth0 10.193.20.109
root@ppc:~# libvirtd -d
root@ppc:~# virsh -c qemu:///system define test.xml
root@ppc:~# virsh -c qemu:///system start test
root@ppc:~# virsh -c qemu:///system list --all
Id Name State
----------------------------------------------------
2 test running
Connect from an X86 PC (Ubuntu 10.04) to the PPC board.
user@x86:~$ virsh -c qemu+ssh://root@10.193.20.109/system list --all
The authenticity of host '10.193.20.109 (10.193.20.109)' can't be established.
RSA key fingerprint is 2f:56:07:08:da:7d:ac:41:45:57:d2:12:15:19:67:e0.
Are you sure you want to continue connecting (yes/no)? yes
root(a)10.193.20.109's password:
error: failed to connect to the hypervisor
error: End of file while reading data: Warning: Permanently added '10.193.20.109' (RSA) to the list of known hosts.
nc: invalid option -- 'U'
BusyBox v1.19.4 (2013-03-08 13:08:18 CST) multi-call binary.
Usage: nc [-iN] [-wN] [-l] [-p PORT] [-f FILE|IPADDR PORT] [-e PROG]: Input/output error
I tried to verify the remote connection on localhost. But it also failed as below:
root@mpc8572ds:~# virsh -c qemu+ssh:///root@localhost/system list --all
root@localhost's password:
error: failed to connect to the hypervisor
error: End of file while reading data: nc: invalid option -- 'U'
BusyBox v1.19.4 (2013-03-08 13:08:18 CST) multi-call binary.
Usage: nc [-iN] [-wN] [-l] [-p PORT] [-f FILE|IPADDR PORT] [-e PROG]: Input/output error
Could anyone give suggestion on this issue?
Best Regards,
Olivia
11 years, 7 months
[libvirt] [libvirt-php PATCH 0/1] use LDFLAGS for libvirt-php.so
by stefan.kuhn@foss-group.ch
I'm getting this warning:
* QA Notice: Files built without respecting LDFLAGS have been detected
* Please include the following list of files in your report:
* /usr/lib64/php5.3/lib/extensions/no-debug-non-zts-20090626/libvirt-php.so
The attached patch seems reasonable to me, please check it.
Thank you very much for the quick responses and for adding me to the authors! :-)
Stefan
Stefan Kuhn (1):
src/Makefile.am: use LDFLAGS for libvirt-php.so
src/Makefile.am | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--
1.7.12.4
11 years, 7 months
[libvirt] [libvirt-php PATCH 0/1] Fix: mutlithreading compilation: set_error needs 2 params
by stefan.kuhn@foss-group.ch
= Info =
Someone reports errors withing "PHP_FUNCTION(libvirt_domain_migrate)" due to "set_error":
libvirt-php.c: In function ‘zif_libvirt_domain_migrate’:
libvirt-php.c:5456:3: error: too few arguments to function ‘set_error’
= Background =
Michal Novotny already reacted to Gentoos bug:
Bug 437624 - =dev-php/libvirt-php-0.4.6: too few arguments to function ‘set_error’
https://bugs.gentoo.org/show_bug.cgi?id=437624
with commit:
bfca998ae921ab897f95025344fad71d9633ccf6
http://libvirt.org/git/?p=libvirt-php.git;a=commit;h=bfca998ae921ab897f95...
but some calls to "set_error" still lacked the 2nd param "TSRMLS_CC", especially in the "PHP_FUNCTION(libvirt_domain_migrate)".
= Patch =
Add 2nd param "TSRMLS_CC" to all calls of "set_error" where only 1 param was supplied.
Stefan Kuhn (1):
Fix: mutlithreading compilation: set_error needs 2 params
src/libvirt-php.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
--
1.7.12.4
11 years, 7 months
[libvirt] [PATCH 0/7 v3] Persistent vHBA support in storage driver
by Osier Yang
v2: https://www.redhat.com/archives/libvir-list/2013-February/msg00118.html
v2 - v3:
* 6/8 of v1 is ACKed/pushed.
* Use virStringToLong_ui instead of sscanf
* Fixed various nits pointed out by John
This is the 3rd part to implement NPIV migration support [1].
Part 1: (pushed)
https://www.redhat.com/archives/libvir-list/2013-February/msg00112.html
Part 2: (pushed)
https://www.redhat.com/archives/libvir-list/2013-January/msg00859.html
Osier Yang (7):
New XML attributes for storage pool source adapter
storage: Make the adapter name be consistent with node device driver
storage: Move virStorageBackendSCSIGetHostNumber into iscsi backend
phyp: Prohibit fc_host adapter for phyp driver
util: Add helper to get the scsi host name by iterating over sysfs
storage: Add startPool and stopPool for scsi backend
storage: Guess the parent if it's not specified for vHBA
docs/formatstorage.html.in | 17 +-
docs/schemas/storagepool.rng | 33 +++-
src/conf/storage_conf.c | 132 +++++++++++++-
src/conf/storage_conf.h | 23 ++-
src/libvirt_private.syms | 4 +
src/phyp/phyp_driver.c | 15 +-
src/storage/storage_backend_iscsi.c | 39 +++-
src/storage/storage_backend_scsi.c | 202 +++++++++++++++------
src/storage/storage_backend_scsi.h | 3 -
src/util/virutil.c | 202 +++++++++++++++++++++
src/util/virutil.h | 11 +-
.../pool-scsi-type-fc-host.xml | 15 ++
.../pool-scsi-type-scsi-host.xml | 15 ++
.../pool-scsi-type-fc-host.xml | 18 ++
.../pool-scsi-type-scsi-host.xml | 18 ++
tests/storagepoolxml2xmlout/pool-scsi.xml | 2 +-
tests/storagepoolxml2xmltest.c | 2 +
17 files changed, 669 insertions(+), 82 deletions(-)
create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-fc-host.xml
create mode 100644 tests/storagepoolxml2xmlin/pool-scsi-type-scsi-host.xml
create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-fc-host.xml
create mode 100644 tests/storagepoolxml2xmlout/pool-scsi-type-scsi-host.xml
--
1.8.0.1
11 years, 7 months
[libvirt] [PATCH 1/2] cgroup: export virCgroupRemoveRecursively
by Gao feng
We will use virCgroupRemoveRecursively to remove cgroup
directories in the coming patch.
Signed-off-by: Gao feng <gaofeng(a)cn.fujitsu.com>
---
src/libvirt_private.syms | 1 +
src/util/vircgroup.c | 4 ++--
src/util/vircgroup.h | 1 +
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index dc01bfa..8cc50c4 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1118,6 +1118,7 @@ virCgroupKillRecursive;
virCgroupMounted;
virCgroupMoveTask;
virCgroupPathOfController;
+virCgroupRemoveRecursively;
virCgroupRemove;
virCgroupSetBlkioDeviceWeight;
virCgroupSetBlkioWeight;
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
index 532e704..6998f13 100644
--- a/src/util/vircgroup.c
+++ b/src/util/vircgroup.c
@@ -686,7 +686,7 @@ cleanup:
#endif
#if defined _DIRENT_HAVE_D_TYPE
-static int virCgroupRemoveRecursively(char *grppath)
+int virCgroupRemoveRecursively(char *grppath)
{
DIR *grpdir;
struct dirent *ent;
@@ -735,7 +735,7 @@ static int virCgroupRemoveRecursively(char *grppath)
return rc;
}
#else
-static int virCgroupRemoveRecursively(char *grppath ATTRIBUTE_UNUSED)
+int virCgroupRemoveRecursively(char *grppath ATTRIBUTE_UNUSED)
{
/* Claim no support */
return -ENXIO;
diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h
index 2ed6ff9..ea42fa2 100644
--- a/src/util/vircgroup.h
+++ b/src/util/vircgroup.h
@@ -157,6 +157,7 @@ int virCgroupGetCpusetMems(virCgroupPtr group, char **mems);
int virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus);
int virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus);
+int virCgroupRemoveRecursively(char *grppath);
int virCgroupRemove(virCgroupPtr group);
void virCgroupFree(virCgroupPtr *group);
--
1.7.11.7
11 years, 7 months
[libvirt] [PATCH v3 1/2] Optimize machine option to set more options with it
by Li Zhang
From: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
Currently, -machine option is used only when dump-guest-core is set.
To use options defined in machine option for newer version of QEMU,
it needs to use -machine xxx, and to be compatible with older version
-M, this patch addes QEMU_CAPS_MACHINE_OPT capability for newer
version which supports -machine option.
Signed-off-by: Li Zhang <zhlcindy(a)linux.vnet.ibm.com>
---
v3 -> v2:
* Set QEMU_CAPS_MACHINE_OPT with help string
* Set QEMU_CAPS_DUMP_GUEST_CORE with QMP
v2 -> v1:
* Split the patch to 2 parts suggested by Daniel P.Berrange
* Rename QEMU_CAPS_MACH_OPT to QEMU_CAPS_MACHINE_OPT
* Remove version 1.1 assertion for QEMU_CAPS_MACHINE_OPT
src/qemu/qemu_capabilities.c | 11 +++++++++++
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_command.c | 32 ++++++++++++++++++--------------
tests/qemuhelptest.c | 4 ++++
tests/qemuxml2argvtest.c | 6 +++---
5 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 861d3c4..7d459db 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -213,6 +213,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
"virtio-ccw",
"dtb",
"megasas",
+
+ "machine-opt", /* 135 */
);
struct _virQEMUCaps {
@@ -1091,6 +1093,9 @@ virQEMUCapsComputeCmdFlags(const char *help,
if (strstr(help, "-dtb"))
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DTB);
+ if (strstr(help, "-machine"))
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_OPT);
+
/*
* Handling of -incoming arg with varying features
* -incoming tcp (kvm >= 79, qemu >= 0.10.0)
@@ -2442,6 +2447,12 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps,
virQEMUCapsInitQMPBasic(qemuCaps);
+ /* machine option is supported for newer version */
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_OPT);
+
+ /* -dump-guest-core is supported for newer version*/
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE);
+
if (!(archstr = qemuMonitorGetTargetArch(mon)))
goto cleanup;
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 7101f67..2595868 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -174,6 +174,7 @@ enum virQEMUCapsFlags {
QEMU_CAPS_VIRTIO_CCW = 132, /* -device virtio-*-ccw */
QEMU_CAPS_DTB = 133, /* -dtb file */
QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */
+ QEMU_CAPS_MACHINE_OPT = 135, /* -machine xxxx*/
QEMU_CAPS_LAST, /* this must always be the last item */
};
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index a0c278f..0ee634b 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5197,6 +5197,8 @@ qemuBuildMachineArgStr(virCommandPtr cmd,
const virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
/* This should *never* be NULL, since we always provide
* a machine in the capabilities data for QEMU. So this
* check is just here as a safety in case the unexpected
@@ -5204,27 +5206,29 @@ qemuBuildMachineArgStr(virCommandPtr cmd,
if (!def->os.machine)
return 0;
- if (!def->mem.dump_core) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_OPT)) {
/* if no parameter to the machine type is needed, we still use
* '-M' to keep the most of the compatibility with older versions.
*/
virCommandAddArgList(cmd, "-M", def->os.machine, NULL);
} else {
- if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- "%s", _("dump-guest-core is not available "
- " with this QEMU binary"));
- return -1;
- }
- /* However, in case there is a parameter to be added, we need to
- * use the "-machine" parameter because qemu is not parsing the
- * "-M" correctly */
virCommandAddArg(cmd, "-machine");
- virCommandAddArgFormat(cmd,
- "%s,dump-guest-core=%s",
- def->os.machine,
- virDomainMemDumpTypeToString(def->mem.dump_core));
+ virBufferAsprintf(&buf, "%s", def->os.machine);
+
+ if (def->mem.dump_core) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ "%s", _("dump-guest-core is not available "
+ " with this QEMU binary"));
+ return -1;
+ }
+
+ virBufferAsprintf(&buf, ",dump-guest-core=%s",
+ virDomainMemDumpTypeToString(def->mem.dump_core));
+ }
+
+ virCommandAddArg(cmd, virBufferContentAndReset(&buf));
}
return 0;
diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c
index a28109a..0c7e607 100644
--- a/tests/qemuhelptest.c
+++ b/tests/qemuhelptest.c
@@ -718,6 +718,7 @@ mymain(void)
QEMU_CAPS_SCSI_LSI,
QEMU_CAPS_BLOCKIO,
QEMU_CAPS_VNC,
+ QEMU_CAPS_MACHINE_OPT,
QEMU_CAPS_DEVICE_QXL,
QEMU_CAPS_DEVICE_VGA,
QEMU_CAPS_DEVICE_CIRRUS_VGA,
@@ -806,6 +807,7 @@ mymain(void)
QEMU_CAPS_VIRTIO_SCSI,
QEMU_CAPS_BLOCKIO,
QEMU_CAPS_VNC,
+ QEMU_CAPS_MACHINE_OPT,
QEMU_CAPS_DEVICE_QXL,
QEMU_CAPS_DEVICE_VGA,
QEMU_CAPS_DEVICE_CIRRUS_VGA,
@@ -903,6 +905,7 @@ mymain(void)
QEMU_CAPS_SECCOMP_SANDBOX,
QEMU_CAPS_DUMP_GUEST_CORE,
QEMU_CAPS_VNC,
+ QEMU_CAPS_MACHINE_OPT,
QEMU_CAPS_USB_REDIR_BOOTINDEX,
QEMU_CAPS_USB_HOST_BOOTINDEX,
QEMU_CAPS_DEVICE_QXL,
@@ -1009,6 +1012,7 @@ mymain(void)
QEMU_CAPS_SECCOMP_SANDBOX,
QEMU_CAPS_DUMP_GUEST_CORE,
QEMU_CAPS_VNC,
+ QEMU_CAPS_MACHINE_OPT,
QEMU_CAPS_USB_REDIR_BOOTINDEX,
QEMU_CAPS_USB_HOST_BOOTINDEX,
QEMU_CAPS_DEVICE_QXL,
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 38787ac..4b45c55 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -363,9 +363,9 @@ mymain(void)
DO_TEST("minimal-s390", QEMU_CAPS_NAME);
DO_TEST("machine-aliases1", NONE);
DO_TEST("machine-aliases2", QEMU_CAPS_KVM);
- DO_TEST("machine-core-on", QEMU_CAPS_DUMP_GUEST_CORE);
- DO_TEST("machine-core-off", QEMU_CAPS_DUMP_GUEST_CORE);
- DO_TEST_FAILURE("machine-core-on", NONE);
+ DO_TEST("machine-core-on", QEMU_CAPS_MACHINE_OPT, QEMU_CAPS_DUMP_GUEST_CORE);
+ DO_TEST("machine-core-off", QEMU_CAPS_MACHINE_OPT, QEMU_CAPS_DUMP_GUEST_CORE);
+ DO_TEST_FAILURE("machine-core-on", QEMU_CAPS_MACHINE_OPT, NONE);
DO_TEST("boot-cdrom", NONE);
DO_TEST("boot-network", NONE);
DO_TEST("boot-floppy", NONE);
--
1.7.10.1
11 years, 7 months