diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 2466df735d6ace47fda93480a1d5f9d29f771ec4..bb1c0792b9170837d64ad56138f5a2afad083b15 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -5012,6 +5012,32 @@ qemu-kvm -net nic,model=? /dev/null
definitions. This is used by the LXC driver.
+
+ ...
+ <devices>
+ <interface type='ethernet'>
+ <source/>
+ <ip address='192.168.123.1' prefix='24'/>
+ <ip address='10.0.0.10' prefix='24' peer='192.168.122.5'/>
+ <route family='ipv4' address='192.168.42.0' prefix='24' gateway='192.168.123.4'/>
+ <source/>
+ ...
+ </interface>
+ ...
+ </devices>
+ ...
+
+
+
+ Since 2.0.0 network devices of type
+ "ethernet" can optionally be provided one or more IP addresses
+ and one or more routes to set on the host side of the
+ network device. These are configured as subelements of
+ the <source>
element of the interface, and
+ have the same attributes as the similarly named elements used to
+ configure the guest side of the interface (described above).
+
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 2d12da95e3f6cf269ff43ed141c4685d3c73bb32..964ff92215d4e5259a5eaa1c859d8f5df36767a0 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -2142,7 +2142,7 @@
-
+
@@ -2392,7 +2392,6 @@
-
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 3a81f7e2aaa813a17af217c7318b1e354aa39cb3..90d2eaa7ccd5a307955cfda6dcf83213bba2aec1 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1798,6 +1798,7 @@ virDomainNetDefClear(virDomainNetDefPtr def)
VIR_FREE(def->ifname_guest_actual);
virNetDevIPInfoClear(&def->guestIP);
+ virNetDevIPInfoClear(&def->hostIP);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def->filter);
@@ -4607,6 +4608,23 @@ virDomainRedirdevDefValidate(const virDomainDef *def,
}
+static int
+virDomainNetDefValidate(const virDomainNetDef *net)
+{
+ if ((net->hostIP.nroutes || net->hostIP.nips) &&
+ net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Invalid attempt to set network interface "
+ "host-side IP route and/or address info on "
+ "interface of type '%s'. This is only supported "
+ "on interfaces of type 'ethernet'"),
+ virDomainNetTypeToString(net->type));
+ return -1;
+ }
+ return 0;
+}
+
+
static int
virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev,
const virDomainDef *def)
@@ -4618,9 +4636,11 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev,
case VIR_DOMAIN_DEVICE_REDIRDEV:
return virDomainRedirdevDefValidate(def, dev->data.redirdev);
+ case VIR_DOMAIN_DEVICE_NET:
+ return virDomainNetDefValidate(dev->data.net);
+
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_FS:
- case VIR_DOMAIN_DEVICE_NET:
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
case VIR_DOMAIN_DEVICE_VIDEO:
@@ -8989,6 +9009,15 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+ xmlNodePtr tmpnode = ctxt->node;
+
+ ctxt->node = cur;
+ if (virDomainNetIPInfoParseXML(_("interface host IP"),
+ ctxt, &def->hostIP) < 0)
+ goto error;
+ ctxt->node = tmpnode;
+ }
if (!macaddr && xmlStrEqual(cur->name, BAD_CAST "mac")) {
macaddr = virXMLPropString(cur, "address");
} else if (!network &&
@@ -20692,6 +20721,7 @@ virDomainNetDefFormat(virBufferPtr buf,
{
unsigned int actualType = virDomainNetGetActualType(def);
bool publicActual = false;
+ int sourceLines = 0;
const char *typeStr;
virDomainHostdevDefPtr hostdef = NULL;
char macstr[VIR_MAC_STRING_BUFLEN];
@@ -20761,15 +20791,7 @@ virDomainNetDefFormat(virBufferPtr buf,
def->data.network.name);
virBufferEscapeString(buf, " portgroup='%s'",
def->data.network.portgroup);
- virBufferAddLit(buf, "/>\n");
-
- /* ONLY for internal status storage - format the ActualNetDef
- * as a subelement of so that no persistent config
- * data is overwritten.
- */
- if ((flags & VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET) &&
- (virDomainActualNetDefFormat(buf, def, flags) < 0))
- return -1;
+ sourceLines++;
break;
case VIR_DOMAIN_NET_TYPE_ETHERNET:
@@ -20783,13 +20805,16 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferAsprintf(buf, " mode='%s'",
def->data.vhostuser->data.nix.listen ?
"server" : "client");
- virBufferAddLit(buf, "/>\n");
+ sourceLines++;
}
break;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
- virBufferEscapeString(buf, " \n",
- def->data.bridge.brname);
+ if (def->data.bridge.brname) {
+ virBufferEscapeString(buf, "data.bridge.brname);
+ sourceLines++;
+ }
break;
case VIR_DOMAIN_NET_TYPE_SERVER:
@@ -20804,25 +20829,27 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferAsprintf(buf, "data.socket.port);
}
+ sourceLines++;
- if (def->type != VIR_DOMAIN_NET_TYPE_UDP) {
- virBufferAddLit(buf, "/>\n");
+ if (def->type != VIR_DOMAIN_NET_TYPE_UDP)
break;
- }
virBufferAddLit(buf, ">\n");
+ sourceLines++;
virBufferAdjustIndent(buf, 2);
virBufferAsprintf(buf, " \n",
def->data.socket.localaddr,
def->data.socket.localport);
virBufferAdjustIndent(buf, -2);
- virBufferAddLit(buf, " \n");
break;
case VIR_DOMAIN_NET_TYPE_INTERNAL:
- virBufferEscapeString(buf, " \n",
- def->data.internal.name);
+ if (def->data.internal.name) {
+ virBufferEscapeString(buf, "data.internal.name);
+ sourceLines++;
+ }
break;
case VIR_DOMAIN_NET_TYPE_DIRECT:
@@ -20830,7 +20857,7 @@ virDomainNetDefFormat(virBufferPtr buf,
def->data.direct.linkdev);
virBufferAsprintf(buf, " mode='%s'",
virNetDevMacVLanModeTypeToString(def->data.direct.mode));
- virBufferAddLit(buf, "/>\n");
+ sourceLines++;
break;
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
@@ -20845,12 +20872,44 @@ virDomainNetDefFormat(virBufferPtr buf,
break;
}
+ /* if sourceLines == 0 - no info at all so far
+ * sourceLines == 1 - first line written, no terminating ">"
+ * sourceLines > 1 - multiple lines, including subelements
+ */
+ if (def->hostIP.nips || def->hostIP.nroutes) {
+ if (sourceLines == 0) {
+ virBufferAddLit(buf, "\n");
+ sourceLines += 2;
+ } else if (sourceLines == 1) {
+ virBufferAddLit(buf, ">\n");
+ sourceLines++;
+ }
+ virBufferAdjustIndent(buf, 2);
+ if (virDomainNetIPInfoFormat(buf, &def->hostIP) < 0)
+ return -1;
+ virBufferAdjustIndent(buf, -2);
+ }
+ if (sourceLines == 1)
+ virBufferAddLit(buf, "/>\n");
+ else if (sourceLines > 1)
+ virBufferAddLit(buf, " \n");
+
if (virNetDevVlanFormat(&def->vlan, buf) < 0)
return -1;
if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
return -1;
if (virNetDevBandwidthFormat(def->bandwidth, buf) < 0)
return -1;
+
+ /* ONLY for internal status storage - format the ActualNetDef
+ * as a subelement of so that no persistent config
+ * data is overwritten.
+ */
+ if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
+ (flags & VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET) &&
+ (virDomainActualNetDefFormat(buf, def, flags) < 0))
+ return -1;
+
}
if (virDomainNetIPInfoFormat(buf, &def->guestIP) < 0)
@@ -20859,6 +20918,7 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferEscapeString(buf, "\n",
def->script);
virBufferEscapeString(buf, " \n", def->domain_name);
+
if (def->ifname &&
!((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
(STRPREFIX(def->ifname, VIR_NET_GENERATED_PREFIX) ||
@@ -20866,6 +20926,7 @@ virDomainNetDefFormat(virBufferPtr buf,
/* Skip auto-generated target names for inactive config. */
virBufferEscapeString(buf, " \n", def->ifname);
}
+
if (def->ifname_guest || def->ifname_guest_actual) {
virBufferAddLit(buf, " ) */
+ virNetDevIPInfo hostIP;
char *ifname_guest_actual;
char *ifname_guest;
virNetDevIPInfo guestIP;
diff --git a/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml b/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce455f7920465eec84c17e59b2b54c17bef6388a
--- /dev/null
+++ b/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml
@@ -0,0 +1,44 @@
+
+ 8675309
+ e21987a5-e98e-9c99-0e35-803e4d9ad1fe
+ 1048576
+ 1048576
+ 1
+
+ /machine
+
+
+ exe
+ /sbin/init
+
+
+
+
+
+
+ destroy
+ restart
+ restart
+
+ /usr/libexec/libvirt_lxc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c
index 1b16088e6746886a8668a95907779bae1a1979ad..2f7f77973472c506afbd7ae84ed68098e0c337bd 100644
--- a/tests/lxcxml2xmltest.c
+++ b/tests/lxcxml2xmltest.c
@@ -95,6 +95,7 @@ mymain(void)
DO_TEST("capabilities");
DO_TEST("sharenet");
DO_TEST("ethernet");
+ DO_TEST("ethernet-hostip");
DO_TEST_FULL("filesystem-root", 0, false,
VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS);