From: Scott Feldman <scofeldm@xxxxxxxxx> 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@xxxxxxxxx> Signed-off-by: Roopa Prabhu<roprabhu@xxxxxxxxx> --- 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" -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list