From: Scott Feldman <scofeldm@xxxxxxxxx> Use the new IFLA_VF_PORTS netlink msg to associate/disassociate port-profiles on a virtual port backing the VM device. The new netlink msg type has been accepted by netdev kernel maintainer. Tested with Cisco's 10G Ethernet NIC using example XML: <interface type='direct'> <mac address='52:54:00:07:70:cf'/> <source dev='eth2' mode='private' profileid='test-network'/> <target dev='macvtap0'/> <model type='virtio'/> </interface> Changes since V1: - port to new netlink msg types Next steps for V3, etc: 1) merge with Stefan's latest patch, as much as possible 2) assign VM device mac addr to eth and set macvtap mac addr to random 3) figure out where host_uuid lives 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 | 260 +++++++++++++++++++++++++++++++++++++++++++++- src/util/macvtap.h | 7 + 7 files changed, 294 insertions(+), 6 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 20c9c51..577c2ea 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) || @@ -2050,6 +2053,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; @@ -2115,6 +2123,7 @@ cleanup: VIR_FREE(internal); VIR_FREE(devaddr); VIR_FREE(mode); + VIR_FREE(profileid); virNWFilterHashTableFree(filterparams); return def; @@ -5141,6 +5150,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 3e334dc..ea74f1c 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 (net->data.direct.profileid) + setPortProfileId(net->data.direct.linkdev, + net->data.direct.profileid, + NULL, NULL); + 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 (net->data.direct.profileid) + unsetPortProfileId(net->data.direct.linkdev); } } } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 65ca117..c5f2f78 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3695,8 +3695,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 (net->data.direct.profileid) + unsetPortProfileId(net->data.direct.linkdev); + } } } #endif @@ -8387,8 +8390,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 (detach->data.direct.profileid) + unsetPortProfileId(detach->data.direct.linkdev); + } } #endif diff --git a/src/util/macvtap.c b/src/util/macvtap.c index 1f8dd29..7803bc8 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; @@ -192,6 +192,27 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen) return pos; } +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static struct rtattr *nlNest(struct nlmsghdr *nlm, int totlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(nlm); + + if (nlm->nlmsg_len + NLMSG_ALIGN(sizeof(*nest)) > totlen) + return NULL; + nest->rta_type = type; + nest->rta_len = RTA_LENGTH(0); + nlm->nlmsg_len += sizeof(*nest); + nlAlign(nlm); + return nest; +} + +static int nlNestEnd(struct nlmsghdr *nlm, struct rtattr *nest) +{ + nest->rta_len = (char *)NLMSG_TAIL(nlm) - (char *)nest; + return nlm->nlmsg_len; +} static int link_add(const char *type, @@ -287,7 +308,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 +392,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 +589,237 @@ configMacvtapTap(int tapfd, int vnet_hdr) return 0; } +#if 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; +} +#endif + +static int +portProfileIdMcast(const char *linkdev, + const char request, + const char *profileid, + const unsigned short *instance_uuid, + const unsigned short *host_uuid) +{ + struct rtattr *port_self; + char nlmsgbuf[512]; + struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp; + struct ifinfomsg infomsg = { .ifi_family = AF_UNSPEC }; + char rtattbuf[256]; + struct rtattr *rta; + char *recvbuf = NULL; + int recvbuflen; + struct nlmsgerr *err; + int rc = 0; + + memset(&nlmsgbuf, 0, sizeof(nlmsgbuf)); + nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK); + + if (!nlAppend(nlm, sizeof(nlmsgbuf), &infomsg, sizeof(infomsg))) + 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; + + port_self = nlNest(nlm, sizeof(nlmsgbuf), IFLA_PORT_SELF); + if (!port_self) + return -1; + + switch (request) { + case PORT_REQUEST_ASSOCIATE: + if (profileid && strlen(profileid)) { + rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), + IFLA_PORT_PROFILE, + profileid, strlen(profileid) + 1); + if (!rta) + goto buffer_too_small; + + if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)) + goto buffer_too_small; + } + if (instance_uuid) { + rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), + IFLA_PORT_INSTANCE_UUID, + instance_uuid, PORT_UUID_MAX); + if (!rta) + goto buffer_too_small; + + if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)) + goto buffer_too_small; + } + if (host_uuid) { + rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), + IFLA_PORT_HOST_UUID, + host_uuid, PORT_UUID_MAX); + if (!rta) + goto buffer_too_small; + + if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)) + goto buffer_too_small; + } + break; + case PORT_REQUEST_DISASSOCIATE: + break; + } + + rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST, + &request, 1); + if (!rta) + goto buffer_too_small; + + if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)) + goto buffer_too_small; + + nlNestEnd(nlm, port_self); + + 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 +setPortProfileId(const char *linkdev, + char *profileid, + unsigned short *instance_uuid, + unsigned short *host_uuid) +{ + int rc; + + rc = ifaceDown(linkdev); + if (rc != 0) { + virReportSystemError(errno, + _("cannot 'down' interface %s"), + linkdev); + return -1; + } + + rc = portProfileIdMcast(linkdev, PORT_REQUEST_ASSOCIATE, + profileid, instance_uuid, host_uuid); + if (rc) + return rc; + + rc = ifaceUp(linkdev); + if (rc != 0) { + virReportSystemError(errno, + _("cannot 'up' interface %s"), + linkdev); + return -1; + } + + return rc; +} + +int +unsetPortProfileId(const char *linkdev) +{ + int rc; + + rc = portProfileIdMcast(linkdev, PORT_REQUEST_DISASSOCIATE, + NULL, NULL, NULL); + if (rc) + return rc; + + rc = ifaceDown(linkdev); + if (rc != 0) { + virReportSystemError(errno, + _("cannot 'down' interface %s"), + linkdev); + return -1; + } + + return rc; +} /** * openMacvtapTap: diff --git a/src/util/macvtap.h b/src/util/macvtap.h index 5d4ea5e..136779e 100644 --- a/src/util/macvtap.h +++ b/src/util/macvtap.h @@ -37,6 +37,13 @@ int openMacvtapTap(const char *ifname, void delMacvtap(const char *ifname); +int setPortProfileId(const char *linkdev, + char *profile_id, + unsigned short *instance_uuid, + unsigned short *host_uuid); + +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