[libvirt] [PATCH] Added capability to set and restore the MAC address of a NIC when using PASSTHROUGH mode with DIRECT type NICs. This is required to support SRIOV capable NICs with firmware implemented on-card switches.

Dirk Herrendoerfer d.herrendoerfer at herrendoerfer.name
Fri Jun 10 11:40:04 UTC 2011


---
 src/libvirt_macvtap.syms |    2 +
 src/qemu/qemu_command.c  |   48 +++++++++++++++
 src/qemu/qemu_process.c  |   45 ++++++++++++++
 src/util/macvtap.c       |  149 ++++++++++++++++++++++++++++++++++++++++++++++
 src/util/macvtap.h       |    6 ++
 5 files changed, 250 insertions(+), 0 deletions(-)

diff --git a/src/libvirt_macvtap.syms b/src/libvirt_macvtap.syms
index b48565b..0520cb5 100644
--- a/src/libvirt_macvtap.syms
+++ b/src/libvirt_macvtap.syms
@@ -6,5 +6,7 @@
 # macvtap.h
 delMacvtap;
 openMacvtapTap;
+get_macaddr;
+set_macaddr;
 vpAssociatePortProfileId;
 vpDisassociatePortProfileId;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 4e68241..41972fa 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -118,6 +118,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
     int rc;
 #if WITH_MACVTAP
     char *res_ifname = NULL;
+    unsigned char old_macaddr[6];
     int vnet_hdr = 0;
     int err;
 
@@ -125,6 +126,53 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
         net->model && STREQ(net->model, "virtio"))
         vnet_hdr = 1;
 
+    /** Note: When using PASSTHROUGH mode with MACVTAP devices the link 
+     * devices'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 ( net->data.direct.mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU ) {
+	rc = get_macaddr(&old_macaddr, 6, net->data.direct.linkdev);
+        if (rc) {
+            virReportSystemError(rc,
+                                 _("Getting MAC address from '%s' "
+                                 "to '%02x:%02x:%02x:%02x:%02x:%02x' failed."),
+                                 net->data.direct.linkdev,
+                                 old_macaddr[0],old_macaddr[1],old_macaddr[2],
+                                 old_macaddr[3],old_macaddr[4],old_macaddr[5]);
+        } else {
+            char oldmacname[256], newmacname[256];
+
+            sprintf(oldmacname,"%02x:%02x:%02x:%02x:%02x:%02x",
+                    old_macaddr[0],old_macaddr[1],old_macaddr[2],
+                    old_macaddr[3],old_macaddr[4],old_macaddr[5]);
+
+            sprintf(newmacname,"/tmp/%s@%02x:%02x:%02x:%02x:%02x:%02x",
+                    net->data.direct.linkdev,
+                    net->mac[0],net->mac[1],net->mac[2],
+                    net->mac[3],net->mac[4],net->mac[5]);
+
+	    rc = symlink (oldmacname, newmacname);
+            if (rc) {
+                virReportSystemError(rc,
+                                     _("MAC link file creation failed for %s."),
+                                     net->data.direct.linkdev);
+            }
+        }
+
+	rc = set_macaddr(net->mac, 6, net->data.direct.linkdev);
+        if (rc) {
+            virReportSystemError(rc,
+                                 _("Setting MAC address on  '%s' to "
+                                 "'%02x:%02x:%02x:%02x:%02x:%02x' failed."),
+                                 net->data.direct.linkdev,
+                                 net->mac[0],net->mac[1],net->mac[2],
+                                 net->mac[3],net->mac[4],net->mac[5]);
+        }
+    }
+
     rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
                         net->data.direct.mode, vnet_hdr, def->uuid,
                         &net->data.direct.virtPortProfile, &res_ifname,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1efe024..127acb8 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -2663,6 +2663,51 @@ void qemuProcessStop(struct qemud_driver *driver,
         if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
             delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
                        &net->data.direct.virtPortProfile);
+
+            if ( net->data.direct.mode == VIR_DOMAIN_NETDEV_MACVTAP_MODE_PASSTHRU ) {
+                char newmacname[256], linkdata[64];
+                unsigned int old_macaddr[6];
+
+                sprintf(newmacname,"/tmp/%s@%02x:%02x:%02x:%02x:%02x:%02x",
+                        net->data.direct.linkdev,
+                        net->mac[0],net->mac[1],net->mac[2],
+                        net->mac[3],net->mac[4],net->mac[5]);
+
+                ret = readlink (newmacname, linkdata, 64);
+                if ( ret == 17 ) {
+                    linkdata[17] = 0;
+
+                    ret = sscanf(linkdata,"%x:%x:%x:%x:%x:%x",
+                                 &old_macaddr[0],&old_macaddr[1],&old_macaddr[2],
+                                 &old_macaddr[3],&old_macaddr[4],&old_macaddr[5]);
+                    if ( ret == 6 ) {
+                        unsigned char oldmac[6];
+			oldmac[0] = (unsigned char)(old_macaddr[0] & 0xFF) ;
+			oldmac[1] = (unsigned char)(old_macaddr[1] & 0xFF) ;
+			oldmac[2] = (unsigned char)(old_macaddr[2] & 0xFF) ;
+			oldmac[3] = (unsigned char)(old_macaddr[3] & 0xFF) ;
+			oldmac[4] = (unsigned char)(old_macaddr[4] & 0xFF) ;
+			oldmac[5] = (unsigned char)(old_macaddr[5] & 0xFF) ;
+
+                        VIR_WARN(("Resetting MAC address on '%s' "
+                                  "to '%02x:%02x:%02x:%02x:%02x:%02x'."),
+                                  net->data.direct.linkdev, 
+                                  oldmac[0],oldmac[1],oldmac[2],
+                                  oldmac[3],oldmac[4],oldmac[5]);
+                        /*reset mac and remove link file-ignore restults*/
+                        ret = set_macaddr(oldmac, 6, net->data.direct.linkdev);
+			ret = unlink(newmacname);
+                    } else {
+                        VIR_WARN(("Scanning MAC address from '%s' "
+                                  "failed ! Got %i values."),
+                                  linkdata, ret);
+                    }
+                } else {
+                    VIR_WARN(("Reading MAC address from '%s' failed ! "
+                              "Got %i bytes."),
+                              newmacname, ret);
+                }
+            }
             VIR_FREE(net->ifname);
         }
     }
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 068638e..1026a84 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -191,6 +191,155 @@ err_exit:
 
 # if WITH_MACVTAP
 
+/**
+ * get_macaddr:
+ * Get the MAC address of a network device
+ *
+ * @macaddress: Pointer where the MAC address will be stored
+ * @macaddrsize: Size of the MAC address.
+ * @srcdev: The interface name of the NIC to get the MAC from
+ *
+ * Returns zero in case of success,
+ * negative value otherwise with error reported.
+ *
+ */
+int
+get_macaddr(const unsigned char *macaddress, int macaddrsize,
+            const char *srcdev )
+{
+    int sockfd; 
+    int io; 
+    char buffer[1024]; 
+    struct ifreq ifr;
+
+    strcpy(ifr.ifr_name, srcdev);
+ 
+    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
+    if(sockfd < 0){ 
+        return -1; 
+    } 
+ 
+    io = ioctl(sockfd, SIOCGIFHWADDR, (char *)&ifr); 
+    if(io < 0){ 
+        return -1; 
+    } 
+
+    bcopy( ifr.ifr_ifru.ifru_hwaddr.sa_data, macaddress, macaddrsize);
+ 
+    return 0;
+}
+
+/**
+ * Set_macaddr:
+ * Set the MAC address of a network device
+ *
+ * @macaddress: MAC address to assign to the NIC
+ * @macaddrsize: Size of the MAC address.
+ * @srcdev: The interface name of the NIC 
+ *
+ * Returns zero in case of success,
+ * negative value otherwise with error reported.
+ *
+ */
+int
+set_macaddr(const unsigned char *macaddress, int macaddrsize,
+            const char *srcdev )
+{
+    int rc = 0;
+    struct nlmsghdr *resp;
+    struct nlmsgerr *err;
+    struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
+    int ifindex;
+    unsigned char *recvbuf = NULL;
+    unsigned int recvbuflen;
+    struct nl_msg *nl_msg;
+    struct nlattr *linkinfo;
+
+    if (ifaceGetIndex(true, srcdev, &ifindex) != 0)
+        return -1;
+
+    nl_msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST);
+
+    if (!nl_msg) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (nlmsg_append(nl_msg,  &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
+        goto buffer_too_small;
+
+    if (nla_put_u32(nl_msg, IFLA_LINK, ifindex) < 0)
+        goto buffer_too_small;
+
+    if (nla_put(nl_msg, IFLA_ADDRESS, macaddrsize, macaddress) < 0)
+        goto buffer_too_small;
+    
+    if (srcdev &&
+        nla_put(nl_msg, IFLA_IFNAME, strlen(srcdev)+1, srcdev) < 0)
+        goto buffer_too_small;
+    
+    if (nlComm(nl_msg, &recvbuf, &recvbuflen, 0) < 0) {
+        rc = -1;
+        goto err_exit;
+    }
+
+    if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+        goto malformed_resp;
+
+    resp = (struct nlmsghdr *)recvbuf;
+
+    switch (resp->nlmsg_type) {
+    case NLMSG_ERROR:
+        err = (struct nlmsgerr *)NLMSG_DATA(resp);
+        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+            goto malformed_resp;
+
+        switch (err->error) {
+
+        case 0:
+            break;
+
+        case -EEXIST:
+            rc = -1;
+            break;
+
+        default:
+            macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                         _("error setting device mac address"));
+            rc = -1;
+        }
+        break;
+
+    case NLMSG_DONE:
+        break;
+
+    default:
+        goto malformed_resp;
+    }
+
+err_exit:
+    nlmsg_free(nl_msg);
+
+    VIR_FREE(recvbuf);
+
+    return rc;
+
+malformed_resp:
+    nlmsg_free(nl_msg);
+
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("malformed netlink response message"));
+    VIR_FREE(recvbuf);
+    return -1;
+
+buffer_too_small:
+    nlmsg_free(nl_msg);
+
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("allocated netlink buffer is too small"));
+    return -1;
+}
+
 static int
 link_add(const char *type,
          const unsigned char *macaddress, int macaddrsize,
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index a1383c4..d71546a 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -75,6 +75,12 @@ enum virVMOperationType {
 
 #  include "internal.h"
 
+int get_macaddr(const unsigned char *macaddress, int macaddrsize,
+            const char *srcdev );
+
+int set_macaddr(const unsigned char *macaddress, int macaddrsize,
+            const char *srcdev );
+
 int openMacvtapTap(const char *ifname,
                    const unsigned char *macaddress,
                    const char *linkdev,
-- 
1.7.5.2




More information about the libvir-list mailing list