[libvirt] [libvirt PATCH] Port-profile ID support using IFLA_VF_PORT_PROFILE netlink msg

Scott Feldman scofeldm at cisco.com
Sat May 8 07:05:29 UTC 2010


From: Scott Feldman <scofeldm at cisco.com>

This fleshes out the port profile ID proof-of-concept patch posted earlier
by David Allan, referenced here:

  https://www.redhat.com/archives/libvir-list/2010-March/msg01401.html

It uses the new IFLA_VF_PORT_PROFILE netlink msg to set/unset the port-
profile for the virtual switch port backing the VM device.  The new netlink
msg is being discussed on the netdev kernel mailing list here:

  http://marc.info/?l=linux-netdev&m=127312092712543&w=2
  http://marc.info/?l=linux-netdev&m=127312093412556&w=2

IFLA_VF_PORT_PROFILE is sent using RTM_SETLINK, and retrieved using
RTM_GETLINK.  IFLA_VF_PORT_PROFILE is sent using netlink multicast send
with RTNLGRP_LINK so the receiver of the msg can be in user-space or
kernel-space.

The device XML is:

    <interface type='direct'>
        <source dev='eth2' mode='private' profileid='dc_test'/>
        <mac address='00:16:3e:1a:b3:4b'/>
    </interface>

The port-profile ID msg is sent to source dev.

Tested with Cisco 10G Ethernet NIC using port-profiles defined in Cisco's
Unified Computing System Management software and above referenced kernel
patches.

Signed-off-by: Scott Feldman <scofeldm at cisco.com>
Signed-off-by: Roopa Prabhu<roprabhu at cisco.com>
---
 src/conf/domain_conf.c   |   13 +++
 src/conf/domain_conf.h   |    1 
 src/libvirt_macvtap.syms |    2 
 src/qemu/qemu_conf.c     |    7 ++
 src/qemu/qemu_driver.c   |   10 ++
 src/util/macvtap.c       |  200 +++++++++++++++++++++++++++++++++++++++++++++-
 src/util/macvtap.h       |    6 +
 7 files changed, 233 insertions(+), 6 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 3e45f79..968076f 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -484,6 +484,7 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
 
     case VIR_DOMAIN_NET_TYPE_DIRECT:
         VIR_FREE(def->data.direct.linkdev);
+        VIR_FREE(def->data.direct.profileid);
         break;
 
     case VIR_DOMAIN_NET_TYPE_USER:
@@ -1831,6 +1832,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
     char *internal = NULL;
     char *devaddr = NULL;
     char *mode = NULL;
+    char *profileid = NULL;
     virNWFilterHashTablePtr filterparams = NULL;
 
     if (VIR_ALLOC(def) < 0) {
@@ -1873,6 +1875,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
                        xmlStrEqual(cur->name, BAD_CAST "source")) {
                 dev  = virXMLPropString(cur, "dev");
                 mode = virXMLPropString(cur, "mode");
+                profileid = virXMLPropString(cur, "profileid");
             } else if ((network == NULL) &&
                        ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
                         (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2049,6 +2052,11 @@ virDomainNetDefParseXML(virCapsPtr caps,
         } else
             def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
 
+        if (profileid != NULL) {
+            def->data.direct.profileid = profileid;
+            profileid = NULL;
+        }
+
         def->data.direct.linkdev = dev;
         dev = NULL;
 
@@ -2114,6 +2122,7 @@ cleanup:
     VIR_FREE(internal);
     VIR_FREE(devaddr);
     VIR_FREE(mode);
+    VIR_FREE(profileid);
     virNWFilterHashTableFree(filterparams);
 
     return def;
@@ -5140,6 +5149,10 @@ virDomainNetDefFormat(virBufferPtr buf,
                               def->data.direct.linkdev);
         virBufferVSprintf(buf, " mode='%s'",
                    virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
+        if (def->data.direct.profileid) {
+            virBufferEscapeString(buf, " profileid='%s'",
+                                  def->data.direct.profileid);
+        }
         virBufferAddLit(buf, "/>\n");
         break;
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index fadc8bd..30ebf07 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -290,6 +290,7 @@ struct _virDomainNetDef {
         struct {
             char *linkdev;
             int mode;
+            char *profileid;
         } direct;
     } data;
     char *ifname;
diff --git a/src/libvirt_macvtap.syms b/src/libvirt_macvtap.syms
index ae229a0..9d4652e 100644
--- a/src/libvirt_macvtap.syms
+++ b/src/libvirt_macvtap.syms
@@ -3,3 +3,5 @@
 # macvtap.h
 openMacvtapTap;
 delMacvtap;
+setPortProfileId;
+unsetPortProfileId;
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 5fa8c0a..aff6f28 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1479,6 +1479,11 @@ qemudPhysIfaceConnect(virConnectPtr conn,
         net->model && STREQ(net->model, "virtio"))
         vnet_hdr = 1;
 
+    if (!STREQ(net->data.direct.profileid, ""))
+        setPortProfileId(net->data.direct.linkdev,
+                         net->data.direct.profileid,
+                         net->mac);
+
     rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode,
                         &res_ifname, vnet_hdr);
     if (rc >= 0) {
@@ -1501,6 +1506,8 @@ qemudPhysIfaceConnect(virConnectPtr conn,
                 close(rc);
                 rc = -1;
                 delMacvtap(net->ifname);
+                if (!STREQ(net->data.direct.profileid, ""))
+                    unsetPortProfileId(net->data.direct.linkdev);
             }
         }
     }
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index bb1079e..6ea37d4 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3586,8 +3586,11 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver,
     for (i = 0; i < def->nnets; i++) {
         virDomainNetDefPtr net = def->nets[i];
         if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
-            if (net->ifname)
+            if (net->ifname) {
                 delMacvtap(net->ifname);
+                if (!STREQ(net->data.direct.profileid, ""))
+                    unsetPortProfileId(net->data.direct.linkdev);
+            }
         }
     }
 #endif
@@ -8147,8 +8150,11 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
 
 #if WITH_MACVTAP
     if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
-        if (detach->ifname)
+        if (detach->ifname) {
             delMacvtap(detach->ifname);
+            if (!STREQ(detach->data.direct.profileid, ""))
+                unsetPortProfileId(detach->data.direct.linkdev);
+        }
     }
 #endif
 
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 5d129fd..825cf30 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -85,14 +85,14 @@ static void nlClose(int fd)
  * buffer will be returned.
  */
 static
-int nlComm(struct nlmsghdr *nlmsg,
+int nlComm(struct nlmsghdr *nlmsg, int nlgroups,
            char **respbuf, int *respbuflen)
 {
     int rc = 0;
     struct sockaddr_nl nladdr = {
             .nl_family = AF_NETLINK,
             .nl_pid    = 0,
-            .nl_groups = 0,
+            .nl_groups = nlgroups,
     };
     int rcvChunkSize = 1024; // expecting less than that
     int rcvoffset = 0;
@@ -287,7 +287,7 @@ link_add(const char *type,
 
     li->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)li;
 
-    if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+    if (nlComm(nlm, 0, &recvbuf, &recvbuflen) < 0)
         return -1;
 
     if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
@@ -371,7 +371,7 @@ link_del(const char *name)
     if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
         goto buffer_too_small;
 
-    if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+    if (nlComm(nlm, 0, &recvbuf, &recvbuflen) < 0)
         return -1;
 
     if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
@@ -568,6 +568,198 @@ configMacvtapTap(int tapfd, int vnet_hdr)
     return 0;
 }
 
+static int
+get_host_uuid(char *host_uuid, int len)
+{
+    const char *dmidecodearg[] = { "dmidecode", "-s", "system-uuid", NULL };
+    const char *const dmidecodeenv[] = { "LC_ALL=C", NULL };
+    char *binary, *newline;
+    int dmidecodestdout = -1;
+    int ret = -1;
+    pid_t child;
+
+    binary = virFindFileInPath(dmidecodearg[0]);
+    if (binary == NULL || access(binary, X_OK) != 0) {
+        VIR_FREE(binary);
+         return -1;
+    }
+    dmidecodearg[0] = binary;
+
+    if (virExec(dmidecodearg, dmidecodeenv, NULL,
+                &child, -1, &dmidecodestdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) {
+        ret = -1;
+        goto cleanup;
+    }
+
+    if((ret = saferead(dmidecodestdout, host_uuid, len)) <= 0) {
+       ret = -1;
+       goto cleanup;
+    }
+    host_uuid[ret-1] = '\0';
+
+    /* strip newline */
+    newline = strrchr(host_uuid, '\n');
+    if (newline)
+       *newline = '\0';
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(binary);
+
+    if (close(dmidecodestdout) < 0)
+        ret = -1;
+
+    return ret;
+}
+
+
+static int sendPortProfileMulticastMsg(const char *linkdev,
+                                     struct ifla_vf_port_profile *ivp)
+{
+    int rc = 0;
+    char nlmsgbuf[512];
+    struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+    char *recvbuf = NULL;
+    struct nlmsgerr *err;
+    char rtattbuf[256];
+    struct rtattr *rta;
+    int recvbuflen;
+    int ifindex;
+    struct ifinfomsg i = { .ifi_family = AF_UNSPEC };
+
+    if (ifaceGetIndex(true, linkdev, &ifindex) != 0)
+        return -1;
+
+    memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+    nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+        goto buffer_too_small;
+
+    rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+                       linkdev, strlen(linkdev) + 1);
+    if (!rta)
+        goto buffer_too_small;
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+        goto buffer_too_small;
+
+    rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT_PROFILE,
+                       ivp, sizeof(*ivp));
+    if (!rta)
+        goto buffer_too_small;
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+        goto buffer_too_small;
+
+    if (nlComm(nlm, RTNLGRP_LINK, &recvbuf, &recvbuflen) < 0)
+        return -1;
+
+    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;
+
+              default:
+                 virReportSystemError(-err->error,
+                                 _("error setting port profile on %s"),
+                                 linkdev);
+                 rc = -1;
+          }
+          break;
+        case NLMSG_DONE:
+          break;
+
+        default:
+          goto malformed_resp;
+    }
+
+    VIR_FREE(recvbuf);
+
+    return rc;
+
+malformed_resp:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("malformed netlink response message"));
+    VIR_FREE(recvbuf);
+    return -1;
+
+buffer_too_small:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("internal buffer is too small"));
+
+    return -1;
+}
+
+
+int unsetPortProfileId(const char *linkdev)
+{
+    int rc = 0;
+    struct ifla_vf_port_profile ivp;
+
+    memset(&ivp, 0, sizeof(struct ifla_vf_port_profile));
+    ivp.vf = -1;
+
+    if(!(rc = sendPortProfileMulticastMsg(linkdev, &ivp))) {
+       rc = ifaceDown(linkdev);
+       if (rc != 0) {
+           virReportSystemError(errno,
+                               ("cannot 'down' interface %s"),
+                               linkdev);
+           //Should we error out ?
+           //rc = -1;
+       }
+    }
+
+    return rc;
+}
+
+int setPortProfileId(const char *linkdev,
+                     const char *profileid,
+                     unsigned char *macaddress)
+{
+    int rc = 0;
+    struct ifla_vf_port_profile ivp;
+    char host_uuid[IFLA_VF_UUID_MAX] = "\0";
+
+    if (!profileid)
+        return -EINVAL;
+
+    memset(&ivp, 0, sizeof(struct ifla_vf_port_profile));
+    ivp.vf = -1;
+    strncpy((char *)ivp.port_profile, profileid, sizeof(ivp.port_profile));
+    ivp.port_profile[sizeof(ivp.port_profile)-1] = '\0';
+    memcpy(ivp.mac, macaddress, sizeof(ivp.mac));
+    get_host_uuid(host_uuid, IFLA_VF_UUID_MAX);
+    if (strlen(host_uuid)) {
+        strncpy((char *)ivp.host_uuid, host_uuid, sizeof(ivp.host_uuid));
+        ivp.port_profile[sizeof(ivp.port_profile)-1] = '\0';
+    }
+
+    if(!(rc = sendPortProfileMulticastMsg(linkdev, &ivp))) {
+       rc = ifaceUp(linkdev);
+       if (rc != 0) {
+           virReportSystemError(errno,
+                               ("cannot 'up' interface %s"),
+                               linkdev);
+           // Should we error out of here ?
+           //rc = -1;
+       }
+    }
+
+    return rc;
+}
 
 /**
  * openMacvtapTap:
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index 5d4ea5e..7f58a13 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -37,6 +37,12 @@ int openMacvtapTap(const char *ifname,
 
 void delMacvtap(const char *ifname);
 
+int setPortProfileId(const char *linkdev,
+                     const char *profileid,
+                     unsigned char *macaddress);
+
+int unsetPortProfileId(const char *linkdev);
+
 # endif /* WITH_MACVTAP */
 
 # define MACVTAP_MODE_PRIVATE_STR  "private"




More information about the libvir-list mailing list