mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-31 18:15:25 +00:00
a79e7639da
WITH_VIRTUALPORT just checks that we are building on Linux and that IFLA_PORT_MAX is defined in linux/if_link.h. Back when 802.11Qb[gh] support was added, the IFLA_* stuff was new (introduced in kernel 2.6.35, backported to RHEL6 2.6.32 kernel at some point), and so this extra check was necessary, because libvirt was being built on Linux distros that didn't yet have IFLA_* (e.g. older RHEL6, all RHEL5). It's been in the kernel for a *very* long time now, so all supported versions of all Linux platforms libvirt builds on have it. Note that the above paragraph implies that the conditional compilation should be changed to #if defined(__linux__). However, the astute reader will notice that the code in question is sending and receiving netlink messages, so it really should be conditional on WITH_LIBNL (which implies __linux__) instead, so that's what this patch does. Signed-off-by: Laine Stump <laine@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
1373 lines
46 KiB
C
1373 lines
46 KiB
C
/*
|
|
* Copyright (C) 2009-2016 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "virnetdevvportprofile.h"
|
|
#include "virerror.h"
|
|
#include "viralloc.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NET
|
|
|
|
VIR_ENUM_IMPL(virNetDevVPort,
|
|
VIR_NETDEV_VPORT_PROFILE_LAST,
|
|
"none",
|
|
"802.1Qbg",
|
|
"802.1Qbh",
|
|
"openvswitch",
|
|
"midonet",
|
|
);
|
|
|
|
VIR_ENUM_IMPL(virNetDevVPortProfileOp,
|
|
VIR_NETDEV_VPORT_PROFILE_OP_LAST,
|
|
"create",
|
|
"save",
|
|
"restore",
|
|
"destroy",
|
|
"migrate out",
|
|
"migrate in start",
|
|
"migrate in finish",
|
|
"no-op",
|
|
);
|
|
|
|
#if defined(WITH_LIBNL)
|
|
|
|
# include <fcntl.h>
|
|
|
|
# include <net/if.h>
|
|
# include <linux/if_tun.h>
|
|
|
|
# include "virnetlink.h"
|
|
# include "virfile.h"
|
|
# include "virlog.h"
|
|
# include "virnetdev.h"
|
|
# include "virsocket.h"
|
|
|
|
VIR_LOG_INIT("util.netdevvportprofile");
|
|
|
|
# define MICROSEC_PER_SEC (1000 * 1000)
|
|
|
|
# define STATUS_POLL_TIMEOUT_USEC (10 * MICROSEC_PER_SEC)
|
|
# define STATUS_POLL_INTERVL_USEC (MICROSEC_PER_SEC / 8)
|
|
|
|
# define LLDPAD_PID_FILE "/var/run/lldpad.pid"
|
|
|
|
|
|
enum virNetDevVPortProfileLinkOp {
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE = 0x1,
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE = 0x2,
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE = 0x3,
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR = 0x4,
|
|
};
|
|
|
|
#endif
|
|
|
|
bool
|
|
virNetDevVPortProfileEqual(const virNetDevVPortProfile *a, const virNetDevVPortProfile *b)
|
|
{
|
|
/* NULL resistant */
|
|
if (!a && !b)
|
|
return true;
|
|
|
|
if (!a || !b)
|
|
return false;
|
|
|
|
if (a->virtPortType != b->virtPortType)
|
|
return false;
|
|
|
|
switch (a->virtPortType) {
|
|
case VIR_NETDEV_VPORT_PROFILE_NONE:
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
|
if (a->managerID != b->managerID ||
|
|
a->typeID != b->typeID ||
|
|
a->typeIDVersion != b->typeIDVersion ||
|
|
memcmp(a->instanceID, b->instanceID, VIR_UUID_BUFLEN) != 0)
|
|
return false;
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
|
if (STRNEQ(a->profileID, b->profileID))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
|
if (STRNEQ(a->profileID, b->profileID) ||
|
|
memcmp(a->interfaceID, b->interfaceID, VIR_UUID_BUFLEN) != 0)
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int virNetDevVPortProfileCopy(virNetDevVPortProfilePtr *dst, const virNetDevVPortProfile *src)
|
|
{
|
|
if (!src) {
|
|
*dst = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (VIR_ALLOC(*dst) < 0)
|
|
return -1;
|
|
|
|
memcpy(*dst, src, sizeof(*src));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* virNetDevVPortProfileCheckComplete() checks that all attributes
|
|
* required for the type of virtport are specified. When
|
|
* generateMissing is true, any missing attribute that can be
|
|
* autogenerated, will be (instanceid, interfaceid). If virtport ==
|
|
* NULL or virtPortType == NONE, then the result is always 0
|
|
* (success). If a required attribute is missing, an error is logged
|
|
* and -1 is returned.
|
|
*/
|
|
int
|
|
virNetDevVPortProfileCheckComplete(virNetDevVPortProfilePtr virtport,
|
|
bool generateMissing)
|
|
{
|
|
const char *missing = NULL;
|
|
|
|
if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)
|
|
return 0;
|
|
|
|
switch (virtport->virtPortType) {
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
|
if (!virtport->managerID_specified) {
|
|
missing = "managerid";
|
|
} else if (!virtport->typeID_specified) {
|
|
missing = "typeid";
|
|
} else if (!virtport->typeIDVersion_specified) {
|
|
missing = "typeidversion";
|
|
} else if (!virtport->instanceID_specified) {
|
|
if (generateMissing) {
|
|
if (virUUIDGenerate(virtport->instanceID) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("cannot generate a random uuid for instanceid"));
|
|
return -1;
|
|
}
|
|
virtport->instanceID_specified = true;
|
|
} else {
|
|
missing = "instanceid";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
|
if (!virtport->profileID[0])
|
|
missing = "profileid";
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
|
/* profileid is optional for openvswitch */
|
|
if (!virtport->interfaceID_specified) {
|
|
if (generateMissing) {
|
|
if (virUUIDGenerate(virtport->interfaceID) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("cannot generate a random uuid for interfaceid"));
|
|
return -1;
|
|
}
|
|
virtport->interfaceID_specified = true;
|
|
} else {
|
|
missing = "interfaceid";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_MIDONET:
|
|
if (!virtport->interfaceID_specified)
|
|
missing = "interfaceid";
|
|
break;
|
|
}
|
|
|
|
if (missing) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("missing %s in <virtualport type='%s'>"), missing,
|
|
virNetDevVPortTypeToString(virtport->virtPortType));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* virNetDevVPortProfileCheckNoExtras() checks that there are no
|
|
* attributes specified in this virtport that are inappropriate for
|
|
* the type. if virtport == NULL or virtPortType == NONE, then the
|
|
* result is always 0 (success). If an extra attribute is present,
|
|
* an error is logged and -1 is returned.
|
|
*/
|
|
int
|
|
virNetDevVPortProfileCheckNoExtras(const virNetDevVPortProfile *virtport)
|
|
{
|
|
const char *extra = NULL;
|
|
|
|
if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)
|
|
return 0;
|
|
|
|
switch (virtport->virtPortType) {
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
|
if (virtport->profileID[0])
|
|
extra = "profileid";
|
|
else if (virtport->interfaceID_specified)
|
|
extra = "interfaceid";
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
|
if (virtport->managerID_specified)
|
|
extra = "managerid";
|
|
else if (virtport->typeID_specified)
|
|
extra = "typeid";
|
|
else if (virtport->typeIDVersion_specified)
|
|
extra = "typeidversion";
|
|
else if (virtport->instanceID_specified)
|
|
extra = "instanceid";
|
|
else if (virtport->interfaceID_specified)
|
|
extra = "interfaceid";
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
|
if (virtport->managerID_specified)
|
|
extra = "managerid";
|
|
else if (virtport->typeID_specified)
|
|
extra = "typeid";
|
|
else if (virtport->typeIDVersion_specified)
|
|
extra = "typeidversion";
|
|
else if (virtport->instanceID_specified)
|
|
extra = "instanceid";
|
|
break;
|
|
}
|
|
|
|
if (extra) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("extra %s unsupported in <virtualport type='%s'>"),
|
|
extra,
|
|
virNetDevVPortTypeToString(virtport->virtPortType));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* virNetDevVPortProfileMerge() - merge the attributes in mods into
|
|
* orig. If anything that is set in mods has already been set in orig
|
|
* *and doesn't match*, log an error and return -1, otherwise return 0.
|
|
*/
|
|
static int
|
|
virNetDevVPortProfileMerge(virNetDevVPortProfilePtr orig,
|
|
const virNetDevVPortProfile *mods)
|
|
{
|
|
enum virNetDevVPortProfile otype;
|
|
|
|
if (!orig || !mods)
|
|
return 0;
|
|
|
|
otype = orig->virtPortType;
|
|
|
|
if (mods->virtPortType != VIR_NETDEV_VPORT_PROFILE_NONE) {
|
|
if (otype != VIR_NETDEV_VPORT_PROFILE_NONE &&
|
|
otype != mods->virtPortType) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports "
|
|
"with mismatched types (%s and %s)"),
|
|
virNetDevVPortTypeToString(otype),
|
|
virNetDevVPortTypeToString(mods->virtPortType));
|
|
return -1;
|
|
}
|
|
otype = orig->virtPortType = mods->virtPortType;
|
|
}
|
|
|
|
if (mods->managerID_specified &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->managerID_specified &&
|
|
(orig->managerID != mods->managerID)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports "
|
|
"with mismatched managerids (%d and %d)"),
|
|
orig->managerID, mods->managerID);
|
|
return -1;
|
|
}
|
|
orig->managerID = mods->managerID;
|
|
orig->managerID_specified = true;
|
|
}
|
|
|
|
if (mods->typeID_specified &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->typeID_specified &&
|
|
(orig->typeID != mods->typeID)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports "
|
|
"with mismatched typeids (%d and %d)"),
|
|
orig->typeID, mods->typeID);
|
|
return -1;
|
|
}
|
|
orig->typeID = mods->typeID;
|
|
orig->typeID_specified = true;
|
|
}
|
|
|
|
if (mods->typeIDVersion_specified &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->typeIDVersion_specified &&
|
|
(orig->typeIDVersion != mods->typeIDVersion)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports with "
|
|
"mismatched typeidversions (%d and %d)"),
|
|
orig->typeIDVersion, mods->typeIDVersion);
|
|
return -1;
|
|
}
|
|
orig->typeIDVersion = mods->typeIDVersion;
|
|
orig->typeIDVersion_specified = true;
|
|
}
|
|
|
|
if (mods->instanceID_specified &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->instanceID_specified &&
|
|
memcmp(orig->instanceID, mods->instanceID,
|
|
sizeof(orig->instanceID))) {
|
|
char origuuid[VIR_UUID_STRING_BUFLEN];
|
|
char modsuuid[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports with "
|
|
"mismatched instanceids ('%s' and '%s')"),
|
|
virUUIDFormat(orig->instanceID, origuuid),
|
|
virUUIDFormat(mods->instanceID, modsuuid));
|
|
return -1;
|
|
}
|
|
memcpy(orig->instanceID, mods->instanceID, sizeof(orig->instanceID));
|
|
orig->instanceID_specified = true;
|
|
}
|
|
|
|
if (mods->interfaceID_specified &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->interfaceID_specified &&
|
|
memcmp(orig->interfaceID, mods->interfaceID,
|
|
sizeof(orig->interfaceID))) {
|
|
char origuuid[VIR_UUID_STRING_BUFLEN];
|
|
char modsuuid[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports with "
|
|
"mismatched interfaceids ('%s' and '%s')"),
|
|
virUUIDFormat(orig->interfaceID, origuuid),
|
|
virUUIDFormat(mods->interfaceID, modsuuid));
|
|
return -1;
|
|
}
|
|
memcpy(orig->interfaceID, mods->interfaceID, sizeof(orig->interfaceID));
|
|
orig->interfaceID_specified = true;
|
|
}
|
|
|
|
if (mods->profileID[0] &&
|
|
(otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_8021QBH ||
|
|
otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
|
|
if (orig->profileID[0] &&
|
|
STRNEQ(orig->profileID, mods->profileID)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("attempt to merge virtualports with "
|
|
"mismatched profileids ('%s' and '%s')"),
|
|
orig->profileID, mods->profileID);
|
|
return -1;
|
|
}
|
|
if (virStrcpyStatic(orig->profileID, mods->profileID) < 0) {
|
|
/* this should never happen - it indicates mods->profileID
|
|
* isn't properly null terminated. */
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("corrupted profileid string"));
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* virNetDevVPortProfileMerge3() - create a new virNetDevVPortProfile
|
|
* that is a combination of the three input profiles. fromInterface is
|
|
* highest priority and fromPortgroup is lowest. As lower priority
|
|
* objects' attributes are merged in, if the attribute is unset in the
|
|
* result object, it is set from the lower priority object, but if it
|
|
* is already set in the result and the lower priority object wants to
|
|
* change it, that is an error.
|
|
*/
|
|
|
|
int virNetDevVPortProfileMerge3(virNetDevVPortProfilePtr *result,
|
|
const virNetDevVPortProfile *fromInterface,
|
|
const virNetDevVPortProfile *fromNetwork,
|
|
const virNetDevVPortProfile *fromPortgroup)
|
|
{
|
|
int ret = -1;
|
|
*result = NULL;
|
|
|
|
if ((!fromInterface || (fromInterface->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) &&
|
|
(!fromNetwork || (fromNetwork->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) &&
|
|
(!fromPortgroup || (fromPortgroup->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE))) {
|
|
return 0;
|
|
}
|
|
|
|
/* at least one of the source profiles is non-empty */
|
|
if (VIR_ALLOC(*result) < 0)
|
|
return ret;
|
|
|
|
/* start with the interface's profile. There are no pointers in a
|
|
* virtualPortProfile, so a shallow copy is sufficient.
|
|
*/
|
|
if (fromInterface)
|
|
**result = *fromInterface;
|
|
|
|
if (virNetDevVPortProfileMerge(*result, fromNetwork) < 0)
|
|
goto error;
|
|
if (virNetDevVPortProfileMerge(*result, fromPortgroup) < 0)
|
|
goto error;
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
if (ret < 0)
|
|
VIR_FREE(*result);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if defined(WITH_LIBNL)
|
|
|
|
static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
|
|
{
|
|
[IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
|
|
};
|
|
|
|
static uint32_t
|
|
virNetDevVPortProfileGetLldpadPid(void)
|
|
{
|
|
int fd;
|
|
uint32_t pid = 0;
|
|
|
|
fd = open(LLDPAD_PID_FILE, O_RDONLY);
|
|
if (fd >= 0) {
|
|
char buffer[10];
|
|
|
|
if (saferead(fd, buffer, sizeof(buffer)) <= sizeof(buffer)) {
|
|
unsigned int res;
|
|
char *endptr;
|
|
|
|
if (virStrToLong_ui(buffer, &endptr, 10, &res) == 0
|
|
&& (*endptr == '\0' || g_ascii_isspace(*endptr))
|
|
&& res != 0) {
|
|
pid = res;
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("error parsing pid of lldpad"));
|
|
}
|
|
}
|
|
} else {
|
|
virReportSystemError(errno,
|
|
_("Error opening file %s"), LLDPAD_PID_FILE);
|
|
}
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return pid;
|
|
}
|
|
|
|
/**
|
|
* virNetDevVPortProfileGetStatus:
|
|
*
|
|
* tb: top level netlink response attributes + values
|
|
* vf: The virtual function used in the request
|
|
* instanceId: instanceId of the interface (vm uuid in case of 802.1Qbh)
|
|
* is8021Qbg: whether this function is call for 8021Qbg
|
|
* status: pointer to a uint16 where the status will be written into
|
|
*
|
|
* Get the status from the IFLA_PORT_RESPONSE field; Returns 0 in
|
|
* case of success, < 0 otherwise with error having been reported
|
|
*/
|
|
static int
|
|
virNetDevVPortProfileGetStatus(struct nlattr **tb, int32_t vf,
|
|
const unsigned char *instanceId,
|
|
bool nltarget_kernel,
|
|
bool is8021Qbg,
|
|
uint16_t *status)
|
|
{
|
|
struct nlattr *tb_port[IFLA_PORT_MAX + 1] = { NULL, };
|
|
|
|
if (vf == PORT_SELF_VF && nltarget_kernel) {
|
|
if (tb[IFLA_PORT_SELF]) {
|
|
if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
|
|
ifla_port_policy)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("error parsing IFLA_PORT_SELF part"));
|
|
return -1;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("IFLA_PORT_SELF is missing"));
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (tb[IFLA_VF_PORTS]) {
|
|
int rem;
|
|
bool found = false;
|
|
struct nlattr *tb_vf_ports = { NULL, };
|
|
|
|
nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) {
|
|
|
|
if (nla_type(tb_vf_ports) != IFLA_VF_PORT) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("error while iterating over "
|
|
"IFLA_VF_PORTS part"));
|
|
return -1;
|
|
}
|
|
|
|
if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
|
|
ifla_port_policy)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("error parsing IFLA_VF_PORT part"));
|
|
return -1;
|
|
}
|
|
|
|
/* This ensures that the given VF is present in the
|
|
* IFLA_VF_PORTS list, and that its uuid matches the
|
|
* instanceId (in case we've associated it). If no
|
|
* instanceId is sent from the caller, that means we've
|
|
* disassociated it from this instanceId, and the uuid
|
|
* will either be unset (if still not associated with
|
|
* anything) or will be set to a new and different uuid
|
|
*/
|
|
if ((tb_port[IFLA_PORT_VF] &&
|
|
vf == *(uint32_t *)RTA_DATA(tb_port[IFLA_PORT_VF])) &&
|
|
(!instanceId ||
|
|
(tb_port[IFLA_PORT_INSTANCE_UUID] &&
|
|
!memcmp(instanceId,
|
|
(unsigned char *)
|
|
RTA_DATA(tb_port[IFLA_PORT_INSTANCE_UUID]),
|
|
VIR_UUID_BUFLEN)))) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
char instanceIdStr[VIR_UUID_STRING_BUFLEN] = "(none)";
|
|
|
|
if (instanceId)
|
|
virUUIDFormat(instanceId, instanceIdStr);
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find vf/instanceId %u/%s "
|
|
" in netlink response"),
|
|
vf, instanceIdStr);
|
|
|
|
/* go through all the entries again. This seems tedious,
|
|
* but experience has shown the resulting log to be
|
|
* very useful.
|
|
*/
|
|
VIR_WARN("IFLA_VF_PORTS entries that were returned:");
|
|
nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN] = "(none)";
|
|
|
|
if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
|
|
ifla_port_policy)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("error parsing IFLA_VF_PORT "
|
|
"during error reporting"));
|
|
return -1;
|
|
}
|
|
if (tb_port[IFLA_PORT_INSTANCE_UUID]) {
|
|
virUUIDFormat((unsigned char *)
|
|
RTA_DATA(tb_port[IFLA_PORT_INSTANCE_UUID]),
|
|
uuidstr);
|
|
}
|
|
VIR_WARN(" vf: %d uuid: %s",
|
|
tb_port[IFLA_PORT_VF] ?
|
|
*(uint32_t *)RTA_DATA(tb_port[IFLA_PORT_VF]) : -1,
|
|
uuidstr);
|
|
}
|
|
return -1;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("IFLA_VF_PORTS is missing"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (tb_port[IFLA_PORT_RESPONSE]) {
|
|
*status = *(uint16_t *)RTA_DATA(tb_port[IFLA_PORT_RESPONSE]);
|
|
} else {
|
|
if (is8021Qbg) {
|
|
/* no in-progress here; may be missing */
|
|
*status = PORT_PROFILE_RESPONSE_INPROGRESS;
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("no IFLA_PORT_RESPONSE found in netlink message"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virNetDevVPortProfileOpSetLink(const char *ifname, int ifindex,
|
|
bool nltarget_kernel,
|
|
const virMacAddr *macaddr,
|
|
int vlanid,
|
|
const char *profileId,
|
|
struct ifla_port_vsi *portVsi,
|
|
const unsigned char *instanceId,
|
|
const unsigned char *hostUUID,
|
|
int32_t vf,
|
|
uint8_t op)
|
|
{
|
|
int rc = -1;
|
|
struct nlmsghdr *resp = NULL;
|
|
struct nlmsgerr *err;
|
|
struct ifinfomsg ifinfo = {
|
|
.ifi_family = AF_UNSPEC,
|
|
.ifi_index = ifindex,
|
|
};
|
|
unsigned int recvbuflen = 0;
|
|
int src_pid = 0;
|
|
uint32_t dst_pid = 0;
|
|
struct nl_msg *nl_msg;
|
|
struct nlattr *vfports = NULL, *vfport;
|
|
char macStr[VIR_MAC_STRING_BUFLEN];
|
|
char hostUUIDStr[VIR_UUID_STRING_BUFLEN];
|
|
char instanceUUIDStr[VIR_UUID_STRING_BUFLEN];
|
|
const char *opName;
|
|
|
|
switch (op) {
|
|
case PORT_REQUEST_PREASSOCIATE:
|
|
opName = "PREASSOCIATE";
|
|
break;
|
|
case PORT_REQUEST_PREASSOCIATE_RR:
|
|
opName = "PREASSOCIATE_RR";
|
|
break;
|
|
case PORT_REQUEST_ASSOCIATE:
|
|
opName = "ASSOCIATE";
|
|
break;
|
|
case PORT_REQUEST_DISASSOCIATE:
|
|
opName = "DISASSOCIATE";
|
|
break;
|
|
default:
|
|
opName = "(unknown)";
|
|
break;
|
|
}
|
|
VIR_INFO("%s: ifname: %s ifindex: %d vf: %d vlanid: %d mac: %s "
|
|
"profileId: %s instanceId: %s hostUUID: %s",
|
|
opName, ifname ? ifname : "(unspecified)",
|
|
ifindex, vf, vlanid,
|
|
macaddr ? virMacAddrFormat(macaddr, macStr) : "(unspecified)",
|
|
profileId ? profileId : "(unspecified)",
|
|
(instanceId
|
|
? virUUIDFormat(instanceId, instanceUUIDStr)
|
|
: "(unspecified)"),
|
|
(hostUUID
|
|
? virUUIDFormat(hostUUID, hostUUIDStr)
|
|
: "(unspecified)"));
|
|
|
|
nl_msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST);
|
|
if (!nl_msg) {
|
|
virReportOOMError();
|
|
return rc;
|
|
}
|
|
|
|
if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
|
|
goto buffer_too_small;
|
|
|
|
if (ifname &&
|
|
nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
|
|
goto buffer_too_small;
|
|
|
|
if (macaddr || vlanid >= 0) {
|
|
struct nlattr *vfinfolist, *vfinfo;
|
|
|
|
if (!(vfinfolist = nla_nest_start(nl_msg, IFLA_VFINFO_LIST)))
|
|
goto buffer_too_small;
|
|
|
|
if (!(vfinfo = nla_nest_start(nl_msg, IFLA_VF_INFO)))
|
|
goto buffer_too_small;
|
|
|
|
if (macaddr) {
|
|
struct ifla_vf_mac ifla_vf_mac = {
|
|
.vf = vf,
|
|
.mac = { 0, },
|
|
};
|
|
|
|
virMacAddrGetRaw(macaddr, ifla_vf_mac.mac);
|
|
|
|
if (nla_put(nl_msg, IFLA_VF_MAC, sizeof(ifla_vf_mac),
|
|
&ifla_vf_mac) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (vlanid >= 0) {
|
|
struct ifla_vf_vlan ifla_vf_vlan = {
|
|
.vf = vf,
|
|
.vlan = vlanid,
|
|
.qos = 0,
|
|
};
|
|
|
|
if (nla_put(nl_msg, IFLA_VF_VLAN, sizeof(ifla_vf_vlan),
|
|
&ifla_vf_vlan) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
nla_nest_end(nl_msg, vfinfo);
|
|
nla_nest_end(nl_msg, vfinfolist);
|
|
}
|
|
|
|
if (vf == PORT_SELF_VF && nltarget_kernel) {
|
|
if (!(vfport = nla_nest_start(nl_msg, IFLA_PORT_SELF)))
|
|
goto buffer_too_small;
|
|
} else {
|
|
if (!(vfports = nla_nest_start(nl_msg, IFLA_VF_PORTS)))
|
|
goto buffer_too_small;
|
|
|
|
/* begin nesting vfports */
|
|
if (!(vfport = nla_nest_start(nl_msg, IFLA_VF_PORT)))
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (profileId) {
|
|
if (nla_put(nl_msg, IFLA_PORT_PROFILE, strlen(profileId) + 1,
|
|
profileId) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (portVsi) {
|
|
if (nla_put(nl_msg, IFLA_PORT_VSI_TYPE, sizeof(*portVsi),
|
|
portVsi) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (instanceId) {
|
|
if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, VIR_UUID_BUFLEN,
|
|
instanceId) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (hostUUID) {
|
|
if (nla_put(nl_msg, IFLA_PORT_HOST_UUID, VIR_UUID_BUFLEN,
|
|
hostUUID) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (vf != PORT_SELF_VF) {
|
|
if (nla_put(nl_msg, IFLA_PORT_VF, sizeof(vf), &vf) < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (nla_put(nl_msg, IFLA_PORT_REQUEST, sizeof(op), &op) < 0)
|
|
goto buffer_too_small;
|
|
|
|
/* end nesting of vport */
|
|
nla_nest_end(nl_msg, vfport);
|
|
|
|
if (vfports) {
|
|
/* end nesting of vfports */
|
|
nla_nest_end(nl_msg, vfports);
|
|
}
|
|
|
|
if (!nltarget_kernel) {
|
|
if ((src_pid = virNetlinkEventServiceLocalPid(NETLINK_ROUTE)) < 0)
|
|
goto cleanup;
|
|
if ((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
|
|
src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
|
|
goto cleanup;
|
|
|
|
if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
|
|
goto malformed_resp;
|
|
|
|
switch (resp->nlmsg_type) {
|
|
case NLMSG_ERROR:
|
|
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
goto malformed_resp;
|
|
|
|
if (err->error) {
|
|
virReportSystemError(-err->error,
|
|
_("error during virtual port configuration of ifindex %d"),
|
|
ifindex);
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
|
|
case NLMSG_DONE:
|
|
break;
|
|
|
|
default:
|
|
goto malformed_resp;
|
|
}
|
|
|
|
rc = 0;
|
|
cleanup:
|
|
nlmsg_free(nl_msg);
|
|
VIR_FREE(resp);
|
|
return rc;
|
|
|
|
malformed_resp:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("malformed netlink response message"));
|
|
goto cleanup;
|
|
|
|
buffer_too_small:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("allocated netlink buffer is too small"));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/**
|
|
* virNetDevVPortProfileGetNthParent
|
|
*
|
|
* @ifname : the name of the interface; ignored if ifindex is valid
|
|
* @ifindex : the index of the interface or -1 if ifname is given
|
|
* @nthParent : the nth parent interface to get
|
|
* @parent_ifindex : pointer to int
|
|
* @parent_ifname : pointer to buffer of size IFNAMSIZ
|
|
* @nth : the nth parent that is actually returned; if for example eth0.100
|
|
* was given and the 100th parent is to be returned, then eth0 will
|
|
* most likely be returned with nth set to 1 since the chain does
|
|
* not have more interfaces
|
|
*
|
|
* Get the nth parent interface of the given interface. 0 is the interface
|
|
* itself.
|
|
*
|
|
* Return 0 on success, < 0 otherwise
|
|
*/
|
|
static int
|
|
virNetDevVPortProfileGetNthParent(const char *ifname, int ifindex, unsigned int nthParent,
|
|
int *parent_ifindex, char *parent_ifname,
|
|
unsigned int *nth)
|
|
{
|
|
int rc = -1;
|
|
void *nlData = NULL;
|
|
struct nlattr *tb[IFLA_MAX + 1] = { NULL, };
|
|
bool end = false;
|
|
size_t i = 0;
|
|
|
|
*nth = 0;
|
|
|
|
if (ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
|
|
return -1;
|
|
|
|
while (!end && i <= nthParent) {
|
|
VIR_FREE(nlData);
|
|
rc = virNetlinkDumpLink(ifname, ifindex, &nlData, tb, 0, 0);
|
|
if (rc < 0)
|
|
break;
|
|
|
|
if (tb[IFLA_IFNAME]) {
|
|
if (virStrcpy(parent_ifname, (char*)RTA_DATA(tb[IFLA_IFNAME]),
|
|
IFNAMSIZ) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("buffer for root interface name is too small"));
|
|
rc = -1;
|
|
goto cleanup;
|
|
}
|
|
*parent_ifindex = ifindex;
|
|
}
|
|
|
|
if (tb[IFLA_LINK]) {
|
|
ifindex = *(int *)RTA_DATA(tb[IFLA_LINK]);
|
|
ifname = NULL;
|
|
} else {
|
|
end = true;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
*nth = i - 1;
|
|
|
|
cleanup:
|
|
VIR_FREE(nlData);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Returns 0 on success, -1 on general failure, and -2 on timeout */
|
|
static int
|
|
virNetDevVPortProfileOpCommon(const char *ifname, int ifindex,
|
|
bool nltarget_kernel,
|
|
const virMacAddr *macaddr,
|
|
int vlanid,
|
|
const char *profileId,
|
|
struct ifla_port_vsi *portVsi,
|
|
const unsigned char *instanceId,
|
|
const unsigned char *hostUUID,
|
|
int32_t vf,
|
|
uint8_t op,
|
|
bool setlink_only)
|
|
{
|
|
int rc;
|
|
int src_pid = 0;
|
|
uint32_t dst_pid = 0;
|
|
void *nlData = NULL;
|
|
struct nlattr *tb[IFLA_MAX + 1] = { NULL, };
|
|
int repeats = STATUS_POLL_TIMEOUT_USEC / STATUS_POLL_INTERVL_USEC;
|
|
uint16_t status = 0;
|
|
bool is8021Qbg = (profileId == NULL);
|
|
|
|
rc = virNetDevVPortProfileOpSetLink(ifname, ifindex,
|
|
nltarget_kernel,
|
|
macaddr,
|
|
vlanid,
|
|
profileId,
|
|
portVsi,
|
|
instanceId,
|
|
hostUUID,
|
|
vf,
|
|
op);
|
|
if (rc < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("sending of PortProfileRequest failed."));
|
|
return rc;
|
|
}
|
|
|
|
if (setlink_only) /* for re-associations on existing links */
|
|
return 0;
|
|
|
|
if (!nltarget_kernel &&
|
|
(((src_pid = virNetlinkEventServiceLocalPid(NETLINK_ROUTE)) < 0) ||
|
|
((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0))) {
|
|
rc = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
while (--repeats >= 0) {
|
|
VIR_FREE(nlData);
|
|
rc = virNetlinkDumpLink(NULL, ifindex, &nlData, tb, src_pid, dst_pid);
|
|
if (rc < 0)
|
|
goto cleanup;
|
|
|
|
rc = virNetDevVPortProfileGetStatus(tb, vf, instanceId, nltarget_kernel,
|
|
is8021Qbg, &status);
|
|
if (rc < 0)
|
|
goto cleanup;
|
|
if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
|
|
status == PORT_VDP_RESPONSE_SUCCESS) {
|
|
break;
|
|
} else if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
|
|
/* keep trying... */
|
|
} else {
|
|
virReportSystemError(EINVAL,
|
|
_("error %d during port-profile setlink on "
|
|
"interface %s (%d)"),
|
|
status, ifname, ifindex);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
g_usleep(STATUS_POLL_INTERVL_USEC);
|
|
}
|
|
|
|
if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("port-profile setlink timed out"));
|
|
rc = -2;
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(nlData);
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int
|
|
virNetDevVPortProfileGetPhysdevAndVlan(const char *ifname, int *root_ifindex, char *root_ifname,
|
|
int *vlanid)
|
|
{
|
|
int ret;
|
|
unsigned int nth;
|
|
int ifindex = -1;
|
|
|
|
*vlanid = -1;
|
|
while (1) {
|
|
if ((ret = virNetDevVPortProfileGetNthParent(ifname, ifindex, 1,
|
|
root_ifindex, root_ifname, &nth)) < 0)
|
|
return ret;
|
|
if (nth == 0)
|
|
break;
|
|
if (*vlanid == -1) {
|
|
if (virNetDevGetVLanID(root_ifname, vlanid) < 0) {
|
|
virResetLastError();
|
|
*vlanid = -1;
|
|
}
|
|
}
|
|
|
|
ifindex = *root_ifindex;
|
|
ifname = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Returns 0 on success, -1 on general failure, and -2 on timeout */
|
|
static int
|
|
virNetDevVPortProfileOp8021Qbg(const char *ifname,
|
|
const virMacAddr *macaddr,
|
|
int vf,
|
|
const virNetDevVPortProfile *virtPort,
|
|
enum virNetDevVPortProfileLinkOp virtPortOp,
|
|
bool setlink_only)
|
|
{
|
|
int op = PORT_REQUEST_ASSOCIATE;
|
|
struct ifla_port_vsi portVsi = {
|
|
.vsi_mgr_id = virtPort->managerID,
|
|
.vsi_type_version = virtPort->typeIDVersion,
|
|
};
|
|
bool nltarget_kernel = false;
|
|
int vlanid;
|
|
int physdev_ifindex = 0;
|
|
char physdev_ifname[IFNAMSIZ] = { 0, };
|
|
|
|
if (!ifname)
|
|
return -1;
|
|
|
|
vf = PORT_SELF_VF;
|
|
|
|
if (virNetDevVPortProfileGetPhysdevAndVlan(ifname, &physdev_ifindex,
|
|
physdev_ifname, &vlanid) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (vlanid < 0)
|
|
vlanid = 0;
|
|
|
|
portVsi.vsi_type_id[2] = virtPort->typeID >> 16;
|
|
portVsi.vsi_type_id[1] = virtPort->typeID >> 8;
|
|
portVsi.vsi_type_id[0] = virtPort->typeID;
|
|
|
|
switch (virtPortOp) {
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE:
|
|
op = PORT_REQUEST_PREASSOCIATE;
|
|
break;
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR:
|
|
op = PORT_REQUEST_PREASSOCIATE_RR;
|
|
break;
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE:
|
|
op = PORT_REQUEST_ASSOCIATE;
|
|
break;
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE:
|
|
op = PORT_REQUEST_DISASSOCIATE;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("operation type %d not supported"), virtPortOp);
|
|
return -1;
|
|
}
|
|
|
|
return virNetDevVPortProfileOpCommon(physdev_ifname, physdev_ifindex,
|
|
nltarget_kernel,
|
|
macaddr,
|
|
vlanid,
|
|
NULL,
|
|
&portVsi,
|
|
virtPort->instanceID,
|
|
NULL,
|
|
vf,
|
|
op,
|
|
setlink_only);
|
|
}
|
|
|
|
/* Returns 0 on success, -1 on general failure, and -2 on timeout */
|
|
static int
|
|
virNetDevVPortProfileOp8021Qbh(const char *ifname,
|
|
const virMacAddr *macaddr,
|
|
int32_t vf,
|
|
const virNetDevVPortProfile *virtPort,
|
|
const unsigned char *vm_uuid,
|
|
enum virNetDevVPortProfileLinkOp virtPortOp)
|
|
{
|
|
int rc = 0;
|
|
char *physfndev = NULL;
|
|
unsigned char hostuuid[VIR_UUID_BUFLEN];
|
|
bool nltarget_kernel = true;
|
|
int ifindex;
|
|
int vlanid = -1;
|
|
bool is_vf = false;
|
|
|
|
if (vf == -1) {
|
|
int isvf_ret = virNetDevIsVirtualFunction(ifname);
|
|
|
|
if (isvf_ret == -1)
|
|
goto cleanup;
|
|
is_vf = !!isvf_ret;
|
|
}
|
|
|
|
if (is_vf) {
|
|
if (virNetDevGetVirtualFunctionInfo(ifname, &physfndev, &vf) < 0) {
|
|
rc = -1;
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
physfndev = g_strdup(ifname);
|
|
}
|
|
|
|
rc = virNetDevGetIndex(physfndev, &ifindex);
|
|
if (rc < 0)
|
|
goto cleanup;
|
|
|
|
switch (virtPortOp) {
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR:
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE:
|
|
errno = virGetHostUUID(hostuuid);
|
|
if (errno) {
|
|
rc = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
|
|
nltarget_kernel,
|
|
macaddr,
|
|
vlanid,
|
|
virtPort->profileID,
|
|
NULL,
|
|
vm_uuid,
|
|
hostuuid,
|
|
vf,
|
|
(virtPortOp ==
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR) ?
|
|
PORT_REQUEST_PREASSOCIATE_RR
|
|
: PORT_REQUEST_ASSOCIATE,
|
|
false);
|
|
if (rc == -2)
|
|
/* Association timed out, disassociate */
|
|
virNetDevVPortProfileOpCommon(NULL, ifindex,
|
|
nltarget_kernel,
|
|
NULL,
|
|
vlanid,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
vf,
|
|
PORT_REQUEST_DISASSOCIATE,
|
|
false);
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE:
|
|
rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
|
|
nltarget_kernel,
|
|
NULL,
|
|
vlanid,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
vf,
|
|
PORT_REQUEST_DISASSOCIATE,
|
|
false);
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("operation type %d not supported"), virtPortOp);
|
|
rc = -1;
|
|
break;
|
|
default:
|
|
virReportEnumRangeError(virNetDevVPortProfileType, virtPortOp);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(physfndev);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* virNetDevVPortProfileAssociate:
|
|
*
|
|
* @macvtap_ifname: The name of the macvtap device
|
|
* @virtPort: pointer to the object holding port profile parameters
|
|
* @vmuuid : the UUID of the virtual machine
|
|
* @vmOp : The VM operation (i.e., create, no-op)
|
|
* @setlink_only : Only set the link - dont wait for the link to come up
|
|
*
|
|
* Associate a port on a switch 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.
|
|
*/
|
|
int
|
|
virNetDevVPortProfileAssociate(const char *macvtap_ifname,
|
|
const virNetDevVPortProfile *virtPort,
|
|
const virMacAddr *macvtap_macaddr,
|
|
const char *linkdev,
|
|
int vf,
|
|
const unsigned char *vmuuid,
|
|
virNetDevVPortProfileOp vmOp,
|
|
bool setlink_only)
|
|
{
|
|
int rc = 0;
|
|
char uuidStr[VIR_UUID_STRING_BUFLEN];
|
|
char macStr[VIR_MAC_STRING_BUFLEN];
|
|
|
|
VIR_DEBUG("profile:'%p' vmOp: %s device: %s@%s mac: %s uuid: %s",
|
|
virtPort, virNetDevVPortProfileOpTypeToString(vmOp),
|
|
NULLSTR_EMPTY(macvtap_ifname), linkdev,
|
|
(macvtap_macaddr
|
|
? virMacAddrFormat(macvtap_macaddr, macStr)
|
|
: "(unspecified)"),
|
|
vmuuid ? virUUIDFormat(vmuuid, uuidStr) : "(unspecified)");
|
|
|
|
if (!virtPort || vmOp == VIR_NETDEV_VPORT_PROFILE_OP_NO_OP)
|
|
return 0;
|
|
|
|
switch (virtPort->virtPortType) {
|
|
case VIR_NETDEV_VPORT_PROFILE_NONE:
|
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
|
case VIR_NETDEV_VPORT_PROFILE_LAST:
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
|
rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
|
|
vf, virtPort,
|
|
(vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
|
|
? VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE
|
|
: VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE,
|
|
setlink_only);
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
|
rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr, vf,
|
|
virtPort, vmuuid,
|
|
(vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
|
|
? VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR
|
|
: VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE);
|
|
if (vmOp != VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START && !rc) {
|
|
/* XXX bogus error handling */
|
|
ignore_value(virNetDevSetOnline(linkdev, true));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* virNetDevVPortProfileDisassociate:
|
|
*
|
|
* @macvtap_ifname: The name of the macvtap device
|
|
* @macvtap_macaddr : The MAC address of the macvtap
|
|
* @linkdev: The link device in case of macvtap
|
|
* @virtPort: point to object holding port profile parameters
|
|
*
|
|
* Returns 0 in case of success, != 0 otherwise with error
|
|
* having been reported.
|
|
*/
|
|
int
|
|
virNetDevVPortProfileDisassociate(const char *macvtap_ifname,
|
|
const virNetDevVPortProfile *virtPort,
|
|
const virMacAddr *macvtap_macaddr,
|
|
const char *linkdev,
|
|
int vf,
|
|
virNetDevVPortProfileOp vmOp)
|
|
{
|
|
int rc = 0;
|
|
char macStr[VIR_MAC_STRING_BUFLEN];
|
|
|
|
VIR_DEBUG("profile:'%p' vmOp: %s device: %s@%s mac: %s",
|
|
virtPort, virNetDevVPortProfileOpTypeToString(vmOp),
|
|
NULLSTR_EMPTY(macvtap_ifname), linkdev,
|
|
(macvtap_macaddr
|
|
? virMacAddrFormat(macvtap_macaddr, macStr)
|
|
: "(unspecified)"));
|
|
|
|
if (!virtPort)
|
|
return 0;
|
|
|
|
switch (virtPort->virtPortType) {
|
|
case VIR_NETDEV_VPORT_PROFILE_NONE:
|
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
|
case VIR_NETDEV_VPORT_PROFILE_LAST:
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
|
rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr, vf,
|
|
virtPort,
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE, false);
|
|
break;
|
|
|
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
|
/* avoid disassociating twice */
|
|
if (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH)
|
|
break;
|
|
if (vf < 0)
|
|
ignore_value(virNetDevSetOnline(linkdev, false));
|
|
rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr, vf,
|
|
virtPort, NULL,
|
|
VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#else /* !WITH_LIBNL */
|
|
int virNetDevVPortProfileAssociate(const char *macvtap_ifname G_GNUC_UNUSED,
|
|
const virNetDevVPortProfile *virtPort G_GNUC_UNUSED,
|
|
const virMacAddr *macvtap_macaddr G_GNUC_UNUSED,
|
|
const char *linkdev G_GNUC_UNUSED,
|
|
int vf G_GNUC_UNUSED,
|
|
const unsigned char *vmuuid G_GNUC_UNUSED,
|
|
virNetDevVPortProfileOp vmOp G_GNUC_UNUSED,
|
|
bool setlink_only G_GNUC_UNUSED)
|
|
{
|
|
virReportSystemError(ENOSYS, "%s",
|
|
_("Virtual port profile association not supported on this platform"));
|
|
return -1;
|
|
}
|
|
|
|
int virNetDevVPortProfileDisassociate(const char *macvtap_ifname G_GNUC_UNUSED,
|
|
const virNetDevVPortProfile *virtPort G_GNUC_UNUSED,
|
|
const virMacAddr *macvtap_macaddr G_GNUC_UNUSED,
|
|
const char *linkdev G_GNUC_UNUSED,
|
|
int vf G_GNUC_UNUSED,
|
|
virNetDevVPortProfileOp vmOp G_GNUC_UNUSED)
|
|
{
|
|
virReportSystemError(ENOSYS, "%s",
|
|
_("Virtual port profile association not supported on this platform"));
|
|
return -1;
|
|
}
|
|
#endif /* !WITH_LIBNL */
|