]> xenbits.xensource.com Git - libvirt.git/commitdiff
set and restore MAC address of a NIC when using PASSTHROUGH mode
authorDirk Herrendoerfer <d.herrendoerfer@herrendoerfer.name>
Tue, 21 Jun 2011 15:49:40 +0000 (11:49 -0400)
committerStefan Berger <stefanb@us.ibm.com>
Tue, 21 Jun 2011 15:49:40 +0000 (11:49 -0400)
The following patch addresses the problem that when a PASSTHROUGH
mode DIRECT NIC connection is made the MAC address of the NIC is
not automatically set and reset to the configured VM MAC and
back again.

The attached patch fixes this problem by setting and resetting the MAC
while remembering the previous setting while the VM is running.
This also works if libvirtd is restarted while the VM is running.

the patch passes make syntax-check

src/libvirt_private.syms
src/qemu/qemu_command.c
src/qemu/qemu_hotplug.c
src/qemu/qemu_process.c
src/util/interface.c
src/util/interface.h
src/util/macvtap.c
src/util/macvtap.h

index 8e844e9df479a63914e3a482cb02114d6c2f5499..09b01592ef5396d0a08789dda93e8c9fe8454074 100644 (file)
@@ -498,7 +498,9 @@ ifaceCheck;
 ifaceCtrl;
 ifaceGetFlags;
 ifaceGetIndex;
+ifaceGetMacaddr;
 ifaceGetVlanID;
+ifaceSetMacaddr;
 ifaceIsUp;
 
 
index 63462433207362743df26efe1592a63e712bb1ce..e9ca1c9f93bd59c1614d54047e80132e8715bf5e 100644 (file)
@@ -128,7 +128,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
     rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
                         net->data.direct.mode, vnet_hdr, def->uuid,
                         &net->data.direct.virtPortProfile, &res_ifname,
-                        vmop);
+                        vmop, driver->stateDir);
     if (rc >= 0) {
         qemuAuditNetDevice(def, net, res_ifname, true);
         VIR_FREE(net->ifname);
@@ -149,7 +149,9 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
             if (err) {
                 VIR_FORCE_CLOSE(rc);
                 delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
-                           &net->data.direct.virtPortProfile);
+                           net->data.direct.mode,
+                           &net->data.direct.virtPortProfile,
+                           driver->stateDir);
                 VIR_FREE(net->ifname);
             }
         }
index e80584976067a3dcd3142a539879edcc06435048..a7f11ab4363f202a5381d8cb9f7aff92da1f958e 100644 (file)
@@ -1610,7 +1610,9 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
 #if WITH_MACVTAP
     if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
         delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev,
-                   &detach->data.direct.virtPortProfile);
+                   detach->data.direct.mode,
+                   &detach->data.direct.virtPortProfile,
+                   driver->stateDir);
         VIR_FREE(detach->ifname);
     }
 #endif
index 8e09e52fad8c10025cb21620eee0d86395f97dba..b4411376a2dd8c61a0ea285726677968a19df610 100644 (file)
@@ -2876,7 +2876,8 @@ void qemuProcessStop(struct qemud_driver *driver,
         virDomainNetDefPtr net = def->nets[i];
         if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
             delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
-                       &net->data.direct.virtPortProfile);
+                       net->data.direct.mode,
+                       &net->data.direct.virtPortProfile, driver->stateDir);
             VIR_FREE(net->ifname);
         }
     }
index 04a922c939853f2910eb7bedb2f0489127e8d263..7651f1d1a61fc2a57a1476abcec3651206e6cea9 100644 (file)
@@ -390,3 +390,99 @@ ifaceGetVlanID(const char *vlanifname ATTRIBUTE_UNUSED,
     return ENOSYS;
 }
 #endif /* __linux__ */
+
+/**
+ * ifaceGetMacaddr:
+ * @ifname: interface name to set MTU for
+ * @macaddr: MAC address (VIR_MAC_BUFLEN in size)
+ *
+ * This function gets the @macaddr for a given interface @ifname.
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+#ifdef __linux__
+int
+ifaceGetMacaddr(const char *ifname,
+                unsigned char *macaddr)
+{
+    struct ifreq ifr;
+    int fd;
+
+    if (!ifname)
+        return EINVAL;
+
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0)
+        return errno;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
+        return EINVAL;
+
+    if (ioctl(fd, SIOCGIFHWADDR, (char *)&ifr) != 0)
+        return errno;
+
+    memcpy(macaddr, ifr.ifr_ifru.ifru_hwaddr.sa_data, VIR_MAC_BUFLEN);
+
+    return 0;
+}
+
+#else
+
+int
+ifaceGetMacaddr(const char *ifname ATTRIBUTE_UNUSED,
+                unsigned char *macaddr ATTRIBUTE_UNUSED)
+{
+    return ENOSYS;
+}
+
+#endif /* __linux__ */
+
+/**
+ * ifaceSetMacaddr:
+ * @ifname: interface name to set MTU for
+ * @macaddr: MAC address (VIR_MAC_BUFLEN in size)
+ *
+ * This function sets the @macaddr for a given interface @ifname. This
+ * gets rid of the kernel's automatically assigned random MAC.
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+#ifdef __linux__
+int
+ifaceSetMacaddr(const char *ifname,
+                const unsigned char *macaddr)
+{
+    struct ifreq ifr;
+    int fd;
+
+    if (!ifname)
+        return EINVAL;
+
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0)
+        return errno;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
+        return EINVAL;
+
+    /* To fill ifr.ifr_hdaddr.sa_family field */
+    if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+        return errno;
+
+    memcpy(ifr.ifr_hwaddr.sa_data, macaddr, VIR_MAC_BUFLEN);
+
+    return ioctl(fd, SIOCSIFHWADDR, &ifr) == 0 ? 0 : errno;
+}
+
+#else
+
+int
+ifaceSetMacaddr(const char *ifname ATTRIBUTE_UNUSED,
+                const unsigned char *macaddr ATTRIBUTE_UNUSED)
+{
+    return ENOSYS;
+}
+
+#endif /* __linux__ */
index 6470492041f796d0bc4b634f49cb3d35aa853622..fc0611b4d7c88341b39671462b39edcef79c982e 100644 (file)
@@ -32,4 +32,8 @@ int ifaceGetIndex(bool reportError, const char *ifname, int *ifindex);
 
 int ifaceGetVlanID(const char *vlanifname, int *vlanid);
 
+int ifaceSetMacaddr(const char *ifname, const unsigned char *macaddr);
+
+int ifaceGetMacaddr(const char *ifname, unsigned char *macaddr);
+
 #endif /* __VIR_INTERFACE_H__ */
index 068638e71b4cf827c6f3f846431a14a449fd60ac..48c7b0ebe70db8b55c1fac0a564f4051c0cc4108 100644 (file)
@@ -545,6 +545,103 @@ configMacvtapTap(int tapfd, int vnet_hdr)
     return 0;
 }
 
+/**
+ * replaceMacAdress:
+ * @macaddress: new MAC address for interface
+ * @linkdev: name of interface
+ * @stateDir: directory to store old MAC address
+ *
+ * Returns 0 on success, -1 in case of fatal error, error code otherwise.
+ *
+ */
+static int
+replaceMacAdress(const unsigned char *macaddress,
+                 const char *linkdev,
+                 char *stateDir)
+{
+    unsigned char oldmac[6];
+    int rc;
+
+    rc = ifaceGetMacaddr(linkdev, oldmac);
+
+    if (rc) {
+        virReportSystemError(rc,
+                             _("Getting MAC address from '%s' "
+                               "to '%02x:%02x:%02x:%02x:%02x:%02x' failed."),
+                             linkdev,
+                             oldmac[0], oldmac[1], oldmac[2],
+                             oldmac[3], oldmac[4], oldmac[5]);
+    } else {
+        char *path = NULL;
+        char macstr[VIR_MAC_STRING_BUFLEN];
+
+        if (virAsprintf(&path, "%s/%s",
+                        stateDir,
+                        linkdev) < 0) {
+            virReportOOMError();
+            return errno;
+        }
+        virFormatMacAddr(oldmac, macstr);
+        if (virFileWriteStr(path, macstr, O_CREAT|O_TRUNC|O_WRONLY) < 0) {
+            virReportSystemError(errno, _("Unable to preserve mac for %s"),
+                                 linkdev);
+            return errno;
+        }
+    }
+
+    rc = ifaceSetMacaddr(linkdev, macaddress);
+    if (rc) {
+        virReportSystemError(errno,
+                             _("Setting MAC address on  '%s' to "
+                               "'%02x:%02x:%02x:%02x:%02x:%02x' failed."),
+                             linkdev,
+                             macaddress[0], macaddress[1], macaddress[2],
+                             macaddress[3], macaddress[4], macaddress[5]);
+    }
+    return rc;
+}
+
+/**
+ * restoreMacAddress:
+ * @linkdev: name of interface
+ * @stateDir: directory containing old MAC address
+ *
+ * Returns 0 on success, -1 in case of fatal error, error code otherwise.
+ *
+ */
+static int
+restoreMacAddress(const char *linkdev,
+                  char *stateDir)
+{
+    char *oldmacname = NULL;
+    char *macstr = NULL;
+    char *path = NULL;
+    unsigned char oldmac[6];
+
+    if (virAsprintf(&path, "%s/%s",
+                    stateDir,
+                    linkdev) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (virFileReadAll(path, VIR_MAC_STRING_BUFLEN, &macstr) < 0) {
+        return errno;
+    }
+
+    if (virParseMacAddr(macstr, &oldmac[0]) != 0) {
+        macvtapError(VIR_ERR_INTERNAL_ERROR,
+                             _("Cannot parse MAC address from '%s'"),
+                             oldmacname);
+        return -1;
+    }
+
+    /*reset mac and remove file-ignore results*/
+    ignore_value(ifaceSetMacaddr(linkdev, oldmac));
+    ignore_value(unlink(path));
+    VIR_FREE(macstr);
+    return 0;
+}
 
 /**
  * openMacvtapTap:
@@ -575,7 +672,8 @@ openMacvtapTap(const char *tgifname,
                const unsigned char *vmuuid,
                virVirtualPortProfileParamsPtr virtPortProfile,
                char **res_ifname,
-               enum virVMOperationType vmOp)
+               enum virVMOperationType vmOp,
+               char *stateDir)
 {
     const char *type = "macvtap";
     int c, rc;
@@ -589,6 +687,19 @@ openMacvtapTap(const char *tgifname,
 
     VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virVMOperationTypeToString(vmOp));
 
+    /** Note: When using PASSTHROUGH mode with MACVTAP devices the link
+     * device's MAC address must be set to the VMs MAC address. In
+     * order to not confuse the first switch or bridge in line this MAC
+     * address must be reset when the VM is shut down.
+     * This is especially important when using SRIOV capable cards that
+     * emulate their switch in firmware.
+     */
+    if (mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU) {
+        if (replaceMacAdress(macaddress, linkdev, stateDir) != 0) {
+            return -1;
+        }
+    }
+
     if (tgifname) {
         if(ifaceGetIndex(false, tgifname, &ifindex) == 0) {
             if (STRPREFIX(tgifname,
@@ -684,8 +795,14 @@ void
 delMacvtap(const char *ifname,
            const unsigned char *macaddr,
            const char *linkdev,
-           virVirtualPortProfileParamsPtr virtPortProfile)
+           int mode,
+           virVirtualPortProfileParamsPtr virtPortProfile,
+           char *stateDir)
 {
+    if (mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU) {
+        restoreMacAddress(linkdev, stateDir);
+    }
+
     if (ifname) {
         vpDisassociatePortProfileId(ifname, macaddr,
                                     linkdev,
index a1383c454544b63e371a8e948e9f6e4477d68e44..2843596cd41c685f8c781da83a72974ae72f7879 100644 (file)
@@ -83,12 +83,15 @@ int openMacvtapTap(const char *ifname,
                    const unsigned char *vmuuid,
                    virVirtualPortProfileParamsPtr virtPortProfile,
                    char **res_ifname,
-                   enum virVMOperationType vmop);
+                   enum virVMOperationType vmop,
+                   char *stateDir);
 
 void delMacvtap(const char *ifname,
                 const unsigned char *macaddress,
                 const char *linkdev,
-                virVirtualPortProfileParamsPtr virtPortProfile);
+                int mode,
+                virVirtualPortProfileParamsPtr virtPortProfile,
+                char *stateDir);
 
 int vpAssociatePortProfileId(const char *macvtap_ifname,
                              const unsigned char *macvtap_macaddr,