libvirt/src/util/virnetdevvportprofile.c

1276 lines
42 KiB
C
Raw Normal View History

/*
* Copyright (C) 2009-2012 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/>.
*
* Authors:
* Stefan Berger <stefanb@us.ibm.com>
* Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "virnetdevvportprofile.h"
#include "virterror_internal.h"
#include "memory.h"
#define VIR_FROM_THIS VIR_FROM_NET
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 WITH_VIRTUALPORT
# include <stdint.h>
# include <stdio.h>
# include <errno.h>
# include <fcntl.h>
# include <c-ctype.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <linux/if.h>
# include <linux/if_tun.h>
# include "virnetlink.h"
# include "virfile.h"
# include "logging.h"
# include "virnetdev.h"
# define MICROSEC_PER_SEC (1000 * 1000)
# define NLMSGBUF_SIZE 256
# define RATTBUF_SIZE 64
# 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(virNetDevVPortProfilePtr a, virNetDevVPortProfilePtr 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;
}
/* 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;
}
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(virNetDevVPortProfilePtr 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,
virNetDevVPortProfilePtr 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)) {
/* 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,
virNetDevVPortProfilePtr fromInterface,
virNetDevVPortProfilePtr fromNetwork,
virNetDevVPortProfilePtr 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) {
virReportOOMError();
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 WITH_VIRTUALPORT
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' || c_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)
{
int rc = -1;
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"));
goto cleanup;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("IFLA_PORT_SELF is missing"));
goto cleanup;
}
} 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"));
goto cleanup;
}
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"));
goto cleanup;
}
if (instanceId &&
tb_port[IFLA_PORT_INSTANCE_UUID] &&
!memcmp(instanceId,
(unsigned char *)
RTA_DATA(tb_port[IFLA_PORT_INSTANCE_UUID]),
VIR_UUID_BUFLEN) &&
tb_port[IFLA_PORT_VF] &&
vf == *(uint32_t *)RTA_DATA(tb_port[IFLA_PORT_VF])) {
found = true;
break;
}
}
if (!found) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not find netlink response with "
"expected parameters"));
goto cleanup;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("IFLA_VF_PORTS is missing"));
goto cleanup;
}
}
if (tb_port[IFLA_PORT_RESPONSE]) {
*status = *(uint16_t *)RTA_DATA(tb_port[IFLA_PORT_RESPONSE]);
rc = 0;
} else {
if (is8021Qbg) {
/* no in-progress here; may be missing */
*status = PORT_PROFILE_RESPONSE_INPROGRESS;
rc = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("no IFLA_PORT_RESPONSE found in netlink message"));
goto cleanup;
}
}
cleanup:
return rc;
}
static int
virNetDevVPortProfileOpSetLink(const char *ifname, int ifindex,
bool nltarget_kernel,
const virMacAddrPtr 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;
struct nlmsgerr *err;
struct ifinfomsg ifinfo = {
.ifi_family = AF_UNSPEC,
.ifi_index = ifindex,
};
unsigned char *recvbuf = NULL;
unsigned int recvbuflen = 0;
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
int src_pid = 0;
uint32_t dst_pid = 0;
struct nl_msg *nl_msg;
struct nlattr *vfports = NULL, *vfport;
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)
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
goto cleanup;
if ((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0)
goto cleanup;
}
if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen,
src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
goto cleanup;
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;
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(recvbuf);
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;
struct nlattr *tb[IFLA_MAX + 1] = { NULL, };
unsigned char *recvbuf = NULL;
bool end = false;
unsigned int i = 0;
*nth = 0;
if (ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
return -1;
while (!end && i <= nthParent) {
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
rc = virNetDevLinkDump(ifname, ifindex, tb, &recvbuf, 0, 0);
if (rc < 0)
break;
if (tb[IFLA_IFNAME]) {
if (!virStrcpy(parent_ifname, (char*)RTA_DATA(tb[IFLA_IFNAME]),
IFNAMSIZ)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("buffer for root interface name is too small"));
VIR_FREE(recvbuf);
return -1;
}
*parent_ifindex = ifindex;
}
if (tb[IFLA_LINK]) {
ifindex = *(int *)RTA_DATA(tb[IFLA_LINK]);
ifname = NULL;
} else
end = true;
VIR_FREE(recvbuf);
i++;
}
*nth = i - 1;
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 virMacAddrPtr 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;
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
int src_pid = 0;
uint32_t dst_pid = 0;
unsigned char *recvbuf = 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;
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
if (!nltarget_kernel &&
(((src_pid = virNetlinkEventServiceLocalPid(NETLINK_ROUTE)) < 0) ||
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0))) {
rc = -1;
goto cleanup;
}
while (--repeats >= 0) {
util: set src_pid for virNetlinkCommand when appropriate Until now, the nl_pid of the source address of every message sent by virNetlinkCommand has been set to the value of getpid(). Most of the time this doesn't matter, and in the one case where it does (communication with lldpad), it previously was the proper thing to do, because the netlink event service (which listens on a netlink socket for unsolicited messages from lldpad) coincidentally always happened to bind with a local nl_pid == getpid(). With the fix for: https://bugzilla.redhat.com/show_bug.cgi?id=816465 that particular nl_pid is now effectively a reserved value, so the netlink event service will always bind to something else (coincidentally "getpid() + (1 << 22)", but it really could be anything). The result is that communication between lldpad and libvirtd is broken (lldpad gets a "disconnected" error when it tries to send a directed message). The solution to this problem caused by a solution, is to query the netlink event service's nlhandle for its "local_port", and send that as the source nl_pid (but only when sending to lldpad, of course - in other cases we maintain the old behavior of sending getpid()). There are two cases where a message is being directed at lldpad - one in virNetDevLinkDump, and one in virNetDevVPortProfileOpSetLink. The case of virNetDevVPortProfileOpSetLink is simplest to explain - only if !nltarget_kernel, i.e. the message isn't targetted for the kernel, is the dst_pid set (by calling virNetDevVPortProfileGetLldpadPid()), so only in that case do we call virNetlinkEventServiceLocalPid() to set src_pid. For virNetDevLinkDump, it's a bit more complicated. The call to virNetDevVPortProfileGetLldpadPid() was effectively up one level (in virNetDevVPortProfileOpCommon), although obscured by an unnecessary passing of a function pointer. This patch removes the function pointer, and calls virNetDevVPortProfileGetLldpadPid() directly in virNetDevVPortProfileOpCommon - if it's doing this, it knows that it should also call virNetlinkEventServiceLocalPid() to set src_pid too; then it just passes src_pid and dst_pid down to virNetDevLinkDump. Since (src_pid == 0 && dst_pid == 0) implies that the kernel is the destination, there is no longer any need to send nltarget_kernel as an arg to virNetDevLinkDump, so it's been removed. The disparity between src_pid being int and dst_pid being uint32_t may be a bit disconcerting to some, but I didn't want to complicate virNetlinkEventServiceLocalPid() by having status returned separately from the value.
2012-05-04 17:48:20 +00:00
rc = virNetDevLinkDump(NULL, ifindex, tb, &recvbuf, 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;
}
usleep(STATUS_POLL_INTERVL_USEC);
VIR_FREE(recvbuf);
}
if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("port-profile setlink timed out"));
rc = -2;
}
cleanup:
VIR_FREE(recvbuf);
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 virMacAddrPtr macaddr,
int vf,
const virNetDevVPortProfilePtr virtPort,
enum virNetDevVPortProfileLinkOp virtPortOp,
bool setlink_only)
{
int rc = -1;
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) {
goto cleanup;
}
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_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);
goto cleanup;
}
rc = virNetDevVPortProfileOpCommon(physdev_ifname, physdev_ifindex,
nltarget_kernel,
macaddr,
vlanid,
NULL,
&portVsi,
virtPort->instanceID,
NULL,
vf,
op,
setlink_only);
cleanup:
return rc;
}
/* Returns 0 on success, -1 on general failure, and -2 on timeout */
static int
virNetDevVPortProfileOp8021Qbh(const char *ifname,
const virMacAddrPtr macaddr,
int32_t vf,
const virNetDevVPortProfilePtr 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 = strdup(ifname);
if (!physfndev) {
virReportOOMError();
rc = -1;
goto cleanup;
}
}
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;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("operation type %d not supported"), virtPortOp);
rc = -1;
}
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 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.
*/
int
virNetDevVPortProfileAssociate(const char *macvtap_ifname,
const virNetDevVPortProfilePtr virtPort,
const virMacAddrPtr macvtap_macaddr,
const char *linkdev,
int vf,
const unsigned char *vmuuid,
enum virNetDevVPortProfileOp vmOp,
bool setlink_only)
{
int rc = 0;
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, (macvtap_ifname ? macvtap_ifname : linkdev));
VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virNetDevVPortProfileOpTypeToString(vmOp));
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 virNetDevVPortProfilePtr virtPort,
const virMacAddrPtr macvtap_macaddr,
const char *linkdev,
int vf,
enum virNetDevVPortProfileOp vmOp)
{
int rc = 0;
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virNetDevVPortProfileOpTypeToString(vmOp));
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_VIRTUALPORT */
int virNetDevVPortProfileAssociate(const char *macvtap_ifname ATTRIBUTE_UNUSED,
const virNetDevVPortProfilePtr virtPort ATTRIBUTE_UNUSED,
const virMacAddrPtr macvtap_macaddr ATTRIBUTE_UNUSED,
const char *linkdev ATTRIBUTE_UNUSED,
int vf ATTRIBUTE_UNUSED,
const unsigned char *vmuuid ATTRIBUTE_UNUSED,
enum virNetDevVPortProfileOp vmOp ATTRIBUTE_UNUSED,
bool setlink_only ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Virtual port profile association not supported on this platform"));
return -1;
}
int virNetDevVPortProfileDisassociate(const char *macvtap_ifname ATTRIBUTE_UNUSED,
const virNetDevVPortProfilePtr virtPort ATTRIBUTE_UNUSED,
const virMacAddrPtr macvtap_macaddr ATTRIBUTE_UNUSED,
const char *linkdev ATTRIBUTE_UNUSED,
int vf ATTRIBUTE_UNUSED,
enum virNetDevVPortProfileOp vmOp ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Virtual port profile association not supported on this platform"));
return -1;
}
#endif /* ! WITH_VIRTUALPORT */