From: Scott Feldman <scofeldm(a)cisco.com>
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(a)cisco.com>
Signed-off-by: Roopa Prabhu<roprabhu(a)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 | 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"