diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 56b67056bb8b079473c2d6cf110b6078b96bbe5e..e819544e2b5252dc51bf37af27591d6c81466dea 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -817,6 +817,9 @@ + + + @@ -902,6 +905,45 @@ + + + + + + 802.1Qbg + + + + + + + + + + + + + + + + + + + + + + + 802.1Qbh + + + + + + + + + + @@ -1769,4 +1811,31 @@ [a-zA-Z0-9_\.:]+ + + + + 0x[0-9a-fA-F]{1,2} + + + 0 + 255 + + + + + + + 0x[0-9a-fA-F]{1,6} + + + 0 + 16777215 + + + + + + 39 + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 443b8c03352034155ad463b5deb995cab4fc441e..f155f96880ff40e8fef19c8e9410353b15efd5cc 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -242,6 +242,11 @@ VIR_ENUM_IMPL(virDomainNetdevMacvtap, VIR_DOMAIN_NETDEV_MACVTAP_MODE_LAST, "private", "bridge") +VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST, + "none", + "802.1Qbg", + "802.1Qbh") + VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, "utc", "localtime", @@ -1807,6 +1812,190 @@ cleanup: } +static int +virVirtualPortProfileParamsParseXML(xmlNodePtr node, + virVirtualPortProfileParamsPtr virtPort) +{ + int ret = -1; + char *virtPortType; + char *virtPortManagerID = NULL; + char *virtPortTypeID = NULL; + char *virtPortTypeIDVersion = NULL; + char *virtPortInstanceID = NULL; + char *virtPortProfileID = NULL; + xmlNodePtr cur = node->children; + const char *msg = NULL; + + virtPortType = virXMLPropString(node, "type"); + if (!virtPortType) + return -1; + + while (cur != NULL) { + if (xmlStrEqual(cur->name, BAD_CAST "parameters")) { + + virtPortManagerID = virXMLPropString(cur, "managerid"); + virtPortTypeID = virXMLPropString(cur, "typeid"); + virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion"); + virtPortInstanceID = virXMLPropString(cur, "instanceid"); + virtPortProfileID = virXMLPropString(cur, "profileid"); + + break; + } + + cur = cur->next; + } + + virtPort->virtPortType = VIR_VIRTUALPORT_NONE; + + switch (virVirtualPortTypeFromString(virtPortType)) { + + case VIR_VIRTUALPORT_8021QBG: + if (virtPortManagerID != NULL && virtPortTypeID != NULL && + virtPortTypeIDVersion != NULL) { + unsigned int val; + + if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) { + msg = _("cannot parse value of managerid parameter"); + goto err_exit; + } + + if (val > 0xff) { + msg = _("value of managerid out of range"); + goto err_exit; + } + + virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val; + + if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) { + msg = _("cannot parse value of typeid parameter"); + goto err_exit; + } + + if (val > 0xffffff) { + msg = _("value for typeid out of range"); + goto err_exit; + } + + virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val; + + if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) { + msg = _("cannot parse value of typeidversion parameter"); + goto err_exit; + } + + if (val > 0xff) { + msg = _("value of typeidversion out of range"); + goto err_exit; + } + + virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val; + + if (virtPortInstanceID != NULL) { + if (virUUIDParse(virtPortInstanceID, + virtPort->u.virtPort8021Qbg.instanceID)) { + msg = _("cannot parse instanceid parameter as a uuid"); + goto err_exit; + } + } else { + if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) { + msg = _("cannot generate a random uuid for instanceid"); + goto err_exit; + } + } + + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG; + ret = 0; + } else { + msg = _("a parameter is missing for 802.1Qbg description"); + goto err_exit; + } + break; + + case VIR_VIRTUALPORT_8021QBH: + if (virtPortProfileID != NULL) { + if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID, + virtPortProfileID) != NULL) { + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH; + ret = 0; + } else { + msg = _("profileid parameter too long"); + goto err_exit; + } + } else { + msg = _("profileid parameter is missing for 802.1Qbh descripion"); + goto err_exit; + } + break; + + + default: + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + msg = _("unknown virtualport type"); + goto err_exit; + break; + } + +err_exit: + + if (msg) + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg); + + VIR_FREE(virtPortManagerID); + VIR_FREE(virtPortTypeID); + VIR_FREE(virtPortTypeIDVersion); + VIR_FREE(virtPortInstanceID); + VIR_FREE(virtPortProfileID); + VIR_FREE(virtPortType); + + return ret; +} + + +static void +virVirtualPortProfileFormat(virBufferPtr buf, + virVirtualPortProfileParamsPtr virtPort, + const char *indent) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE) + return; + + virBufferVSprintf(buf, "%s\n", + indent, + virVirtualPortTypeToString(virtPort->virtPortType)); + + switch (virtPort->virtPortType) { + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + break; + + case VIR_VIRTUALPORT_8021QBG: + virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID, + uuidstr); + virBufferVSprintf(buf, + "%s \n", + indent, + virtPort->u.virtPort8021Qbg.managerID, + virtPort->u.virtPort8021Qbg.typeID, + virtPort->u.virtPort8021Qbg.typeIDVersion, + uuidstr); + break; + + case VIR_VIRTUALPORT_8021QBH: + virBufferVSprintf(buf, + "%s \n", + indent, + virtPort->u.virtPort8021Qbh.profileID); + break; + } + + virBufferVSprintf(buf, "%s\n", indent); +} + + /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure @@ -1832,6 +2021,8 @@ virDomainNetDefParseXML(virCapsPtr caps, char *devaddr = NULL; char *mode = NULL; virNWFilterHashTablePtr filterparams = NULL; + virVirtualPortProfileParams virtPort; + bool virtPortParsed = false; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -1873,6 +2064,12 @@ virDomainNetDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "source")) { dev = virXMLPropString(cur, "dev"); mode = virXMLPropString(cur, "mode"); + } else if ((virtPortParsed == false) && + (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) && + xmlStrEqual(cur->name, BAD_CAST "virtualport")) { + if (virVirtualPortProfileParamsParseXML(cur, &virtPort)) + goto error; + virtPortParsed = true; } else if ((network == NULL) && ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) || (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) || @@ -2048,6 +2245,9 @@ virDomainNetDefParseXML(virCapsPtr caps, } else def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA; + if (virtPortParsed) + def->data.direct.virtPortProfile = virtPort; + def->data.direct.linkdev = dev; dev = NULL; @@ -5140,6 +5340,8 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferVSprintf(buf, " mode='%s'", virDomainNetdevMacvtapTypeToString(def->data.direct.mode)); virBufferAddLit(buf, "/>\n"); + virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile, + " "); break; case VIR_DOMAIN_NET_TYPE_USER: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9fd1dd6be056e5db99b026141f8a44351e7549ca..f87f6c8604e50535dbef491c2516d8a308e08722 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -38,6 +38,7 @@ # include "network.h" # include "nwfilter_params.h" # include "nwfilter_conf.h" +# include "macvtap.h" /* Private component of virDomainXMLFlags */ typedef enum { @@ -290,6 +291,7 @@ struct _virDomainNetDef { struct { char *linkdev; int mode; + virVirtualPortProfileParams virtPortProfile; } direct; } data; char *ifname; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b4d8e74cbd877f6741c291ad77163e4a0c56d11e..08d811b18ffd0578e5cf3c422b289ddcb1a037af 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1474,9 +1474,8 @@ int qemudPhysIfaceConnect(virConnectPtr conn, struct qemud_driver *driver, virDomainNetDefPtr net, - char *linkdev, - int brmode, - unsigned long long qemuCmdFlags) + unsigned long long qemuCmdFlags, + const unsigned char *vmuuid) { int rc; #if WITH_MACVTAP @@ -1488,8 +1487,9 @@ qemudPhysIfaceConnect(virConnectPtr conn, net->model && STREQ(net->model, "virtio")) vnet_hdr = 1; - rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode, - &res_ifname, vnet_hdr); + rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev, + net->data.direct.mode, vnet_hdr, vmuuid, + &net->data.direct.virtPortProfile, &res_ifname); if (rc >= 0) { VIR_FREE(net->ifname); net->ifname = res_ifname; @@ -1509,17 +1509,17 @@ qemudPhysIfaceConnect(virConnectPtr conn, if (err) { close(rc); rc = -1; - delMacvtap(net->ifname); + delMacvtap(net->ifname, + &net->data.direct.virtPortProfile); } } } #else (void)conn; (void)net; - (void)linkdev; - (void)brmode; (void)qemuCmdFlags; (void)driver; + (void)vmuuid; qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No support for macvtap device")); rc = -1; @@ -4139,9 +4139,8 @@ int qemudBuildCommandLine(virConnectPtr conn, goto no_memory; } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { int tapfd = qemudPhysIfaceConnect(conn, driver, net, - net->data.direct.linkdev, - net->data.direct.mode, - qemuCmdFlags); + qemuCmdFlags, + def->uuid); if (tapfd < 0) goto error; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 7fb4de57d2eec0c2111f50b6858dc0515457e693..516a59bd0859fd4215e7367c7178b2c7d7260b85 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -276,9 +276,8 @@ qemudOpenVhostNet(virDomainNetDefPtr net, int qemudPhysIfaceConnect(virConnectPtr conn, struct qemud_driver *driver, virDomainNetDefPtr net, - char *linkdev, - int brmode, - unsigned long long qemuCmdFlags); + unsigned long long qemuCmdFlags, + const unsigned char *vmuuid); int qemudProbeMachineTypes (const char *binary, virCapsGuestMachinePtr **machines, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d90820c6baa2802c57da5c1b9b7a74784708d000..e6ce9c1f46b982612bfeb1be11829a9297e019d0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3708,10 +3708,9 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, def = vm->def; for (i = 0; i < def->nnets; i++) { virDomainNetDefPtr net = def->nets[i]; - if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - if (net->ifname) - delMacvtap(net->ifname); - } + if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) + delMacvtap(net->ifname, + &net->data.direct.virtPortProfile); } #endif @@ -7466,9 +7465,8 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, } if ((tapfd = qemudPhysIfaceConnect(conn, driver, net, - net->data.direct.linkdev, - net->data.direct.mode, - qemuCmdFlags)) < 0) + qemuCmdFlags, + vm->def->uuid)) < 0) return -1; } @@ -8515,10 +8513,9 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver, virNWFilterTearNWFilter(detach); #if WITH_MACVTAP - if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - if (detach->ifname) - delMacvtap(detach->ifname); - } + if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) + delMacvtap(detach->ifname, + &detach->data.direct.virtPortProfile); #endif if ((driver->macFilter) && (detach->ifname != NULL)) { diff --git a/src/util/macvtap.c b/src/util/macvtap.c index 1f8dd2968ce19d828489ed36268218e348429aa6..715f329963f9d267f32f043265fcf27e876dbcc9 100644 --- a/src/util/macvtap.c +++ b/src/util/macvtap.c @@ -43,6 +43,7 @@ # include "util.h" # include "memory.h" +# include "logging.h" # include "macvtap.h" # include "interface.h" # include "conf/domain_conf.h" @@ -57,6 +58,16 @@ # define MACVTAP_NAME_PREFIX "macvtap" # define MACVTAP_NAME_PATTERN "macvtap%d" + +static int associatePortProfileId(const char *macvtap_ifname, + const virVirtualPortProfileParamsPtr virtPort, + int vf, + const unsigned char *vmuuid); + +static int disassociatePortProfileId(const char *macvtap_ifname, + const virVirtualPortProfileParamsPtr virtPort); + + static int nlOpen(void) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); @@ -577,8 +588,10 @@ configMacvtapTap(int tapfd, int vnet_hdr) * be NULL if this function is supposed to choose a name * @macaddress: The MAC address for the macvtap device * @linkdev: The interface name of the NIC to connect to the external bridge - * @mode_str: String describing the mode. Valid are 'bridge', 'vepa' and - * 'private'. + * @mode: int describing the mode for 'bridge', 'vepa' or 'private'. + * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it + * @vmuuid: The UUID of the VM the macvtap belongs to + * @virtPortProfile: pointer to object holding the virtual port profile data * @res_ifname: Pointer to a string pointer where the actual name of the * interface will be stored into if everything succeeded. It is up * to the caller to free the string. @@ -592,8 +605,10 @@ openMacvtapTap(const char *tgifname, const unsigned char *macaddress, const char *linkdev, int mode, - char **res_ifname, - int vnet_hdr) + int vnet_hdr, + const unsigned char *vmuuid, + virVirtualPortProfileParamsPtr virtPortProfile, + char **res_ifname) { const char *type = "macvtap"; int c, rc; @@ -639,6 +654,14 @@ create_name: cr_ifname = ifname; } + if (associatePortProfileId(cr_ifname, + virtPortProfile, + -1, + vmuuid) != 0) { + rc = -1; + goto link_del_exit; + } + rc = ifaceUp(cr_ifname); if (rc != 0) { virReportSystemError(errno, @@ -647,7 +670,7 @@ create_name: "MAC address"), cr_ifname); rc = -1; - goto link_del_exit; + goto disassociate_exit; } rc = openTap(cr_ifname, 10); @@ -656,14 +679,18 @@ create_name: if (configMacvtapTap(rc, vnet_hdr) < 0) { close(rc); rc = -1; - goto link_del_exit; + goto disassociate_exit; } *res_ifname = strdup(cr_ifname); } else - goto link_del_exit; + goto disassociate_exit; return rc; +disassociate_exit: + disassociatePortProfileId(cr_ifname, + virtPortProfile); + link_del_exit: link_del(cr_ifname); @@ -674,13 +701,103 @@ link_del_exit: /** * delMacvtap: * @ifname : The name of the macvtap interface + * @virtPortProfile: pointer to object holding the virtual port profile data * - * Delete an interface given its name. + * Delete an interface given its name. Disassociate + * it with the switch if port profile parameters + * were provided. */ void -delMacvtap(const char *ifname) +delMacvtap(const char *ifname, + virVirtualPortProfileParamsPtr virtPortProfile) { - link_del(ifname); + if (ifname) { + disassociatePortProfileId(ifname, + virtPortProfile); + link_del(ifname); + } } #endif + + +/** + * associatePortProfile + * + * @macvtap_ifname: The name of the macvtap device + * @virtPort: pointer to the object holding port profile parameters + * @vf: virtual function number, -1 if to be ignored + * @vmuuid : the UUID of the virtual machine + * + * Associate a port on a swtich with a profile. This function + * may notify a kernel driver or an external daemon to run + * the setup protocol. If profile parameters were not supplied + * by the user, then this function returns without doing + * anything. + * + * Returns 0 in case of success, != 0 otherwise with error + * having been reported. + */ +static int +associatePortProfileId(const char *macvtap_ifname, + const virVirtualPortProfileParamsPtr virtPort, + int vf, + const unsigned char *vmuuid) +{ + int rc = 0; + VIR_DEBUG("Associating port profile '%p' on link device '%s'", + virtPort, macvtap_ifname); + (void)vf; + (void)vmuuid; + + switch (virtPort->virtPortType) { + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + break; + + case VIR_VIRTUALPORT_8021QBG: + + break; + + case VIR_VIRTUALPORT_8021QBH: + + break; + } + + return rc; +} + + +/** + * disassociatePortProfile + * + * @macvtap_ifname: The name of the macvtap device + * @virtPort: point to object holding port profile parameters + * + * Returns 0 in case of success, != 0 otherwise with error + * having been reported. + */ +static int +disassociatePortProfileId(const char *macvtap_ifname, + const virVirtualPortProfileParamsPtr virtPort) +{ + int rc = 0; + VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ", + virtPort, macvtap_ifname); + + switch (virtPort->virtPortType) { + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + break; + + case VIR_VIRTUALPORT_8021QBG: + + break; + + case VIR_VIRTUALPORT_8021QBH: + + break; + } + + return rc; +} diff --git a/src/util/macvtap.h b/src/util/macvtap.h index 5d4ea5e32bf0c7ab57456987a6a790bdce67be0f..464562e4b5adfe18594d375fb41af53dd857482d 100644 --- a/src/util/macvtap.h +++ b/src/util/macvtap.h @@ -24,6 +24,40 @@ # include + +enum virVirtualPortType { + VIR_VIRTUALPORT_NONE, + VIR_VIRTUALPORT_8021QBG, + VIR_VIRTUALPORT_8021QBH, + + VIR_VIRTUALPORT_TYPE_LAST, +}; + +# ifdef IFLA_VF_PORT_PROFILE_MAX +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX +# else +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40 +# endif + +/* profile data for macvtap (VEPA) */ +typedef struct _virVirtualPortProfileParams virVirtualPortProfileParams; +typedef virVirtualPortProfileParams *virVirtualPortProfileParamsPtr; +struct _virVirtualPortProfileParams { + enum virVirtualPortType virtPortType; + union { + struct { + uint8_t managerID; + uint32_t typeID; // 24 bit valid + uint8_t typeIDVersion; + unsigned char instanceID[VIR_UUID_BUFLEN]; + } virtPort8021Qbg; + struct { + char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX]; + } virtPort8021Qbh; + } u; +}; + + # if defined(WITH_MACVTAP) # include "internal.h" @@ -32,10 +66,13 @@ int openMacvtapTap(const char *ifname, const unsigned char *macaddress, const char *linkdev, int mode, - char **res_ifname, - int vnet_hdr); + int vnet_hdr, + const unsigned char *vmuuid, + virVirtualPortProfileParamsPtr virtPortProfile, + char **res_ifname); -void delMacvtap(const char *ifname); +void delMacvtap(const char *ifname, + virVirtualPortProfileParamsPtr virtPortProfile); # endif /* WITH_MACVTAP */ @@ -44,4 +81,6 @@ void delMacvtap(const char *ifname); # define MACVTAP_MODE_BRIDGE_STR "bridge" +VIR_ENUM_DECL(virVirtualPort) + #endif /* __UTIL_MACVTAP_H__ */ diff --git a/tests/domainschemadata/portprofile.xml b/tests/domainschemadata/portprofile.xml new file mode 100644 index 0000000000000000000000000000000000000000..e51301d952b4072b2dad1359fb1bc0390964d26e --- /dev/null +++ b/tests/domainschemadata/portprofile.xml @@ -0,0 +1,36 @@ + + portprofile + 00000000-0000-0000-0000-000000000000 + 1048576 + + exe + /sh + + + + + + + + + + + + + + + + + + + + + + + + + + + +