LXC network devices can now be assigned a custom NIC device name on the
container side. For example, this is configured with:
<interface type='network'>
<source network='default'/>
<guest dev="eth1"/>
</interface>
In this example the network card will appear as eth1 in the guest.
---
docs/schemas/domaincommon.rng | 17 +++++++++++++++++
src/conf/domain_conf.c | 27 +++++++++++++++++++++++++++
src/conf/domain_conf.h | 2 ++
src/lxc/lxc_container.c | 29 +++++++++++++++++++++++++----
src/lxc/lxc_process.c | 25 +++++++++++++++++++++++++
tests/lxcxml2xmldata/lxc-idmap.xml | 1 +
6 files changed, 97 insertions(+), 4 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 33d0308..e7ca992 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -2165,6 +2165,23 @@
</element>
</optional>
<optional>
+ <element name="guest">
+ <interleave>
+ <optional>
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="actual">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+ </interleave>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
<element name="mac">
<attribute name="address">
<ref name="uniMacAddr"/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index b7aa4f5..5cd6ae6 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1383,6 +1383,8 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
VIR_FREE(def->virtPortProfile);
VIR_FREE(def->script);
VIR_FREE(def->ifname);
+ VIR_FREE(def->ifname_guest);
+ VIR_FREE(def->ifname_guest_actual);
virDomainDeviceInfoClear(&def->info);
@@ -6618,6 +6620,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
char *bridge = NULL;
char *dev = NULL;
char *ifname = NULL;
+ char *ifname_guest = NULL;
+ char *ifname_guest_actual = NULL;
char *script = NULL;
char *address = NULL;
char *port = NULL;
@@ -6723,6 +6727,10 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
/* An auto-generated target name, blank it out */
VIR_FREE(ifname);
}
+ } else if ((!ifname_guest || !ifname_guest_actual) &&
+ xmlStrEqual(cur->name, BAD_CAST "guest")) {
+ ifname_guest = virXMLPropString(cur, "dev");
+ ifname_guest_actual = virXMLPropString(cur, "actual");
} else if (!linkstate &&
xmlStrEqual(cur->name, BAD_CAST "link")) {
linkstate = virXMLPropString(cur, "state");
@@ -6964,6 +6972,14 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
def->ifname = ifname;
ifname = NULL;
}
+ if (ifname_guest != NULL) {
+ def->ifname_guest = ifname_guest;
+ ifname_guest = NULL;
+ }
+ if (ifname_guest_actual != NULL) {
+ def->ifname_guest_actual = ifname_guest_actual;
+ ifname_guest_actual = NULL;
+ }
/* NIC model (see -net nic,model=?). We only check that it looks
* reasonable, not that it is a supported NIC type. FWIW kvm
@@ -15883,6 +15899,17 @@ virDomainNetDefFormat(virBufferPtr buf,
/* Skip auto-generated target names for inactive config. */
virBufferEscapeString(buf, "<target dev='%s'/>\n",
def->ifname);
}
+ if (def->ifname_guest || def->ifname_guest_actual) {
+ virBufferAddLit(buf, "<guest");
+ /* Skip auto-generated target names for inactive config. */
+ if (def->ifname_guest)
+ virBufferEscapeString(buf, " dev='%s'",
def->ifname_guest);
+
+ /* Only set if the host is running, so shouldn't pollute output */
+ if (def->ifname_guest_actual)
+ virBufferEscapeString(buf, " actual='%s'",
def->ifname_guest_actual);
+ virBufferAddLit(buf, "/>\n");
+ }
if (def->model) {
virBufferEscapeString(buf, "<model type='%s'/>\n",
def->model);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 1122eb2..60aa491 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -915,6 +915,8 @@ struct _virDomainNetDef {
} tune;
char *script;
char *ifname;
+ char *ifname_guest;
+ char *ifname_guest_actual;
virDomainDeviceInfo info;
char *filter;
virNWFilterHashTablePtr filterparams;
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index fd8ab16..c7423db 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -464,6 +464,21 @@ static int lxcContainerSetID(virDomainDefPtr def)
}
+static virDomainNetDefPtr
+lxcContainerGetNetDef(virDomainDefPtr vmDef, const char *devName)
+{
+ size_t i;
+ virDomainNetDefPtr netDef;
+
+ for (i = 0; i < vmDef->nnets; i++) {
+ netDef = vmDef->nets[i];
+ if (STREQ(netDef->ifname_guest_actual, devName))
+ return netDef;
+ }
+
+ return NULL;
+}
+
/**
* lxcContainerRenameAndEnableInterfaces:
* @nveths: number of interfaces
@@ -475,16 +490,23 @@ static int lxcContainerSetID(virDomainDefPtr def)
*
* Returns 0 on success or nonzero in case of error
*/
-static int lxcContainerRenameAndEnableInterfaces(bool privNet,
+static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef,
size_t nveths,
char **veths)
{
int rc = 0;
size_t i;
char *newname = NULL;
+ virDomainNetDefPtr netDef;
+ bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] ==
+ VIR_DOMAIN_FEATURE_STATE_ON;
for (i = 0; i < nveths; i++) {
- if (virAsprintf(&newname, "eth%zu", i) < 0) {
+ if (!(netDef = lxcContainerGetNetDef(vmDef, veths[i])))
+ return -1;
+
+ newname = netDef->ifname_guest;
+ if (!newname) {
rc = -1;
goto error_out;
}
@@ -1866,8 +1888,7 @@ static int lxcContainerChild(void *data)
}
/* rename and enable interfaces */
- if
(lxcContainerRenameAndEnableInterfaces(vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] ==
- VIR_DOMAIN_FEATURE_STATE_ON,
+ if (lxcContainerRenameAndEnableInterfaces(vmDef,
argv->nveths,
argv->veths) < 0) {
goto cleanup;
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 0aef13a..d532e20 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -259,6 +259,8 @@ char *virLXCProcessSetupInterfaceBridged(virConnectPtr conn,
if (virNetDevSetMAC(containerVeth, &net->mac) < 0)
goto cleanup;
+ if (VIR_STRDUP(net->ifname_guest_actual, containerVeth) < 0)
+ goto cleanup;
if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
{
if (virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac,
@@ -369,6 +371,7 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn,
{
int ret = -1;
size_t i;
+ size_t niface = 0;
for (i = 0; i < def->nnets; i++) {
char *veth = NULL;
@@ -451,6 +454,13 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn,
}
(*veths)[(*nveths)-1] = veth;
+
+ /* Make sure all net definitions will have a name in the container */
+ if (!def->nets[i]->ifname_guest) {
+ if (virAsprintf(&def->nets[i]->ifname_guest, "eth%zu",
niface) < 0)
+ return -1;
+ niface++;
+ }
}
ret = 0;
@@ -470,6 +480,18 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn,
return ret;
}
+static void
+virLXCProcessCleanInterfaces(virDomainDefPtr def)
+{
+ size_t i;
+
+ for (i = 0; i < def->nnets; i++) {
+ VIR_FREE(def->nets[i]->ifname_guest_actual);
+ def->nets[i]->ifname_guest_actual = NULL;
+ VIR_DEBUG("Cleared net names: %s", def->nets[i]->ifname_guest);
+ }
+}
+
extern virLXCDriverPtr lxc_driver;
static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon,
@@ -1306,6 +1328,9 @@ int virLXCProcessStart(virConnectPtr conn,
vm, false) < 0)
goto error;
+ /* We don't need the temporary NIC names anymore, clear them */
+ virLXCProcessCleanInterfaces(vm->def);
+
/* Write domain status to disk.
*
* XXX: Earlier we wrote the plain "live" domain XML to this
diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml
index 3cced21..946d363 100644
--- a/tests/lxcxml2xmldata/lxc-idmap.xml
+++ b/tests/lxcxml2xmldata/lxc-idmap.xml
@@ -29,6 +29,7 @@
<mac address='00:16:3e:0f:ef:8a'/>
<source bridge='bri0'/>
<target dev='veth0'/>
+ <guest dev='eth2'/>
</interface>
<console type='pty'>
<target type='lxc' port='0'/>
--
1.8.4.5