From 3ba0469ce660c5f1fe523051c1ef54e81c0dc272 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Fri, 27 Jun 2014 10:41:22 +0200
Subject: [PATCH] lxc network configuration allows setting target container NIC
 name

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/formatdomain.html.in          | 17 +++++++++++++++++
 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              | 24 ++++++++++++++++++++++++
 tests/lxcxml2xmldata/lxc-idmap.xml |  1 +
 7 files changed, 113 insertions(+), 4 deletions(-)

Index: libvirt-1.2.5/docs/formatdomain.html.in
===================================================================
--- libvirt-1.2.5.orig/docs/formatdomain.html.in
+++ libvirt-1.2.5/docs/formatdomain.html.in
@@ -3757,6 +3757,23 @@ qemu-kvm -net nic,model=? /dev/null
       targets using these prefixes will be ignored.
     </p>
 
+    <p>
+      Note that for LXC containers, this defines the name of the interface
+      on the host side. <span class="since">Since 1.2.7</span>, to define
+      the name of the device on the guest side, the <code>guest</code>
+      element should be used, as in the following snippet:
+    </p>
+
+<pre>
+  ...
+  &lt;devices&gt;
+    &lt;interface type='network'&gt;
+      &lt;source network='default'/&gt;
+      <b>&lt;guest dev='myeth'/&gt;</b>
+    &lt;/interface&gt;
+  &lt;/devices&gt;
+  ...</pre>
+
     <h5><a name="elementsNICSBoot">Specifying boot order</a></h5>
 
 <pre>
Index: libvirt-1.2.5/docs/schemas/domaincommon.rng
===================================================================
--- libvirt-1.2.5.orig/docs/schemas/domaincommon.rng
+++ libvirt-1.2.5/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"/>
Index: libvirt-1.2.5/src/conf/domain_conf.c
===================================================================
--- libvirt-1.2.5.orig/src/conf/domain_conf.c
+++ libvirt-1.2.5/src/conf/domain_conf.c
@@ -1415,6 +1415,8 @@ void virDomainNetDefFree(virDomainNetDef
     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);
 
@@ -6621,6 +6623,8 @@ virDomainNetDefParseXML(virDomainXMLOpti
     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;
@@ -6726,6 +6730,10 @@ virDomainNetDefParseXML(virDomainXMLOpti
                     /* 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");
@@ -6967,6 +6975,14 @@ virDomainNetDefParseXML(virDomainXMLOpti
         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
@@ -15918,6 +15934,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);
Index: libvirt-1.2.5/src/conf/domain_conf.h
===================================================================
--- libvirt-1.2.5.orig/src/conf/domain_conf.h
+++ libvirt-1.2.5/src/conf/domain_conf.h
@@ -917,6 +917,8 @@ struct _virDomainNetDef {
     } tune;
     char *script;
     char *ifname;
+    char *ifname_guest;
+    char *ifname_guest_actual;
     virDomainDeviceInfo info;
     char *filter;
     virNWFilterHashTablePtr filterparams;
Index: libvirt-1.2.5/src/lxc/lxc_container.c
===================================================================
--- libvirt-1.2.5.orig/src/lxc/lxc_container.c
+++ libvirt-1.2.5/src/lxc/lxc_container.c
@@ -464,6 +464,21 @@ static int lxcContainerSetID(virDomainDe
 }
 
 
+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(virDomainDe
  *
  * 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;
         }
@@ -2130,8 +2152,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;
Index: libvirt-1.2.5/src/lxc/lxc_process.c
===================================================================
--- libvirt-1.2.5.orig/src/lxc/lxc_process.c
+++ libvirt-1.2.5/src/lxc/lxc_process.c
@@ -259,6 +259,8 @@ char *virLXCProcessSetupInterfaceBridged
 
     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(
 {
     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(
         }
 
         (*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,17 @@ static int virLXCProcessSetupInterfaces(
     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);
+        VIR_DEBUG("Cleared net names: %s", def->nets[i]->ifname_guest);
+    }
+}
+
 
 extern virLXCDriverPtr lxc_driver;
 static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon,
@@ -1306,6 +1327,9 @@ int virLXCProcessStart(virConnectPtr con
                                     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
Index: libvirt-1.2.5/tests/lxcxml2xmldata/lxc-idmap.xml
===================================================================
--- libvirt-1.2.5.orig/tests/lxcxml2xmldata/lxc-idmap.xml
+++ libvirt-1.2.5/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'/>
