From: "Daniel P. Berrange" <berrange(a)redhat.com>
Rename the macvtap.c file to virnetdevmacvlan.c to reflect its
functionality. Move the port profile association code out into
virnetdevvportprofile.c. Make the APIs available unconditionally
to callers
* src/util/macvtap.h: rename to src/util/virnetdevmacvlan.h,
* src/util/macvtap.c: rename to src/util/virnetdevmacvlan.c
* src/util/virnetdevvportprofile.c, src/util/virnetdevvportprofile.h:
Pull in vport association code
* src/Makefile.am, src/conf/domain_conf.h, src/qemu/qemu_conf.c,
src/qemu/qemu_conf.h, src/qemu/qemu_driver.c: Update include
paths & remove conditional compilation
---
po/POTFILES.in | 3 +-
src/Makefile.am | 2 +-
src/conf/domain_conf.h | 2 +-
src/qemu/qemu_command.c | 12 -
src/qemu/qemu_conf.c | 1 -
src/qemu/qemu_conf.h | 1 -
src/qemu/qemu_driver.c | 1 -
src/qemu/qemu_hotplug.c | 2 -
src/qemu/qemu_migration.c | 5 -
src/qemu/qemu_process.c | 2 -
src/util/macvtap.c | 1208 ----------------------------
src/util/virnetdevmacvlan.c | 423 ++++++++++
src/util/{macvtap.h => virnetdevmacvlan.h} | 38 +-
src/util/virnetdevvportprofile.c | 820 +++++++++++++++++++
src/util/virnetdevvportprofile.h | 33 +-
15 files changed, 1280 insertions(+), 1273 deletions(-)
delete mode 100644 src/util/macvtap.c
create mode 100644 src/util/virnetdevmacvlan.c
rename src/util/{macvtap.h => virnetdevmacvlan.h} (63%)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 810cf68..6797298 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -115,7 +115,6 @@ src/util/iohelper.c
src/util/interface.c
src/util/iptables.c
src/util/json.c
-src/util/macvtap.c
src/util/netlink.c
src/util/pci.c
src/util/processinfo.c
@@ -128,7 +127,9 @@ src/util/viraudit.c
src/util/virfile.c
src/util/virnetdev.c
src/util/virnetdevbridge.c
+src/util/virnetdevmacvlan.c
src/util/virnetdevtap.c
+src/util/virnetdevvportprofile.c
src/util/virpidfile.c
src/util/virsocketaddr.c
src/util/virterror.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 6ffa8c6..6703bc1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,7 +65,6 @@ UTIL_SOURCES = \
util/dnsmasq.c util/dnsmasq.h \
util/json.c util/json.h \
util/logging.c util/logging.h \
- util/macvtap.c util/macvtap.h \
util/memory.c util/memory.h \
util/netlink.c util/netlink.h \
util/pci.c util/pci.h \
@@ -93,6 +92,7 @@ UTIL_SOURCES = \
util/virnetdev.h util/virnetdev.c \
util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
util/virnetdevbridge.h util/virnetdevbridge.c \
+ util/virnetdevmacvlan.c util/virnetdevmacvlan.h \
util/virnetdevtap.h util/virnetdevtap.c \
util/virnetdevveth.h util/virnetdevveth.c \
util/virnetdevvportprofile.h util/virnetdevvportprofile.c \
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index c360674..9528199 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -38,7 +38,7 @@
# include "virsocketaddr.h"
# include "nwfilter_params.h"
# include "nwfilter_conf.h"
-# include "macvtap.h"
+# include "virnetdevmacvlan.h"
# include "sysinfo.h"
# include "virnetdevvportprofile.h"
# include "virnetdevbandwidth.h"
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 6d19f95..687d284 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -142,7 +142,6 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
enum virNetDevVPortProfileOp vmop)
{
int rc;
-#if WITH_MACVTAP
char *res_ifname = NULL;
int vnet_hdr = 0;
int err;
@@ -187,17 +186,6 @@ qemuPhysIfaceConnect(virDomainDefPtr def,
}
}
}
-#else
- (void)def;
- (void)conn;
- (void)net;
- (void)qemuCaps;
- (void)driver;
- (void)vmop;
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("No support for macvtap device"));
- rc = -1;
-#endif
return rc;
}
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 4c20d17..f6c04ae 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -50,7 +50,6 @@
#include "xml.h"
#include "nodeinfo.h"
#include "logging.h"
-#include "macvtap.h"
#include "cpu/cpu.h"
#include "domain_nwfilter.h"
#include "virfile.h"
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index bbe7e74..e13cd08 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -39,7 +39,6 @@
# include "cpu_conf.h"
# include "driver.h"
# include "bitmap.h"
-# include "macvtap.h"
# include "command.h"
# include "threadpool.h"
# include "locking/lock_manager.h"
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 465640f..0c885f1 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -77,7 +77,6 @@
#include "libvirt_internal.h"
#include "xml.h"
#include "cpu/cpu.h"
-#include "macvtap.h"
#include "sysinfo.h"
#include "domain_nwfilter.h"
#include "hooks.h"
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index a9d9a32..f9365fe 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1909,7 +1909,6 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
virDomainConfNWFilterTeardown(detach);
-#if WITH_MACVTAP
if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_DIRECT) {
ignore_value(virNetDevMacVLanDelete(detach->ifname, detach->mac,
virDomainNetGetActualDirectDev(detach),
@@ -1918,7 +1917,6 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
driver->stateDir));
VIR_FREE(detach->ifname);
}
-#endif
if ((driver->macFilter) && (detach->ifname != NULL)) {
if ((errno = networkDisallowMacOnPort(driver,
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index fbff29b..8925660 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -2512,7 +2512,6 @@ qemuMigrationPerform(struct qemud_driver *driver,
}
}
-#if WITH_MACVTAP
static void
qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) {
int i;
@@ -2547,10 +2546,6 @@ err_exit:
}
}
}
-#else /* !WITH_MACVTAP */
-static void
-qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def ATTRIBUTE_UNUSED) { }
-#endif /* WITH_MACVTAP */
virDomainPtr
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 4dc9357..f4096f8 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3410,7 +3410,6 @@ void qemuProcessStop(struct qemud_driver *driver,
def = vm->def;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
-#if WITH_MACVTAP
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) {
ignore_value(virNetDevMacVLanDelete(net->ifname, net->mac,
virDomainNetGetActualDirectDev(net),
@@ -3419,7 +3418,6 @@ void qemuProcessStop(struct qemud_driver *driver,
driver->stateDir));
VIR_FREE(net->ifname);
}
-#endif
/* release the physical device (or any other resources used by
* this interface in the network driver
*/
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
deleted file mode 100644
index 79bdcaa..0000000
--- a/src/util/macvtap.c
+++ /dev/null
@@ -1,1208 +0,0 @@
-/*
- * Copyright (C) 2010-2011 Red Hat, Inc.
- * Copyright (C) 2010 IBM Corporation
- *
- * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Authors:
- * Stefan Berger <stefanb(a)us.ibm.com>
- *
- * Notes:
- * netlink:
http://lovezutto.googlepages.com/netlink.pdf
- * iproute2 package
- *
- */
-
-#include <config.h>
-
-#include <stdint.h>
-
-#if WITH_MACVTAP || WITH_VIRTUALPORT
-
-# 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>
-
-/* Older kernels lacked this enum value. */
-# if !HAVE_DECL_MACVLAN_MODE_PASSTHRU
-# define MACVLAN_MODE_PASSTHRU 8
-# endif
-
-#endif /* WITH_MACVTAP || WITH_VIRTUALPORT */
-
-#include "util.h"
-#include "macvtap.h"
-#include "virnetdev.h"
-
-VIR_ENUM_IMPL(virNetDevMacVLanMode, VIR_NETDEV_MACVLAN_MODE_LAST,
- "vepa",
- "private",
- "bridge",
- "passthrough")
-
-#if WITH_MACVTAP || WITH_VIRTUALPORT
-
-# include "memory.h"
-# include "logging.h"
-# include "interface.h"
-# include "virterror_internal.h"
-# include "uuid.h"
-# include "virfile.h"
-# include "netlink.h"
-
-# define VIR_FROM_THIS VIR_FROM_NET
-
-# define macvtapError(code, ...) \
- virReportErrorHelper(VIR_FROM_NET, code, __FILE__, \
- __FUNCTION__, __LINE__, __VA_ARGS__)
-
-# define MACVTAP_NAME_PREFIX "macvtap"
-# define MACVTAP_NAME_PATTERN "macvtap%d"
-
-# 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 virNetDevVPortOp {
- ASSOCIATE = 0x1,
- DISASSOCIATE = 0x2,
- PREASSOCIATE = 0x3,
- PREASSOCIATE_RR = 0x4,
-};
-
-
-
-
-# if WITH_MACVTAP
-
-/**
- * virNetDevMacVLanTapOpen:
- * Open the macvtap's tap device.
- * @ifname: Name of the macvtap interface
- * @retries : Number of retries in case udev for example may need to be
- * waited for to create the tap chardev
- * Returns negative value in case of error, the file descriptor otherwise.
- */
-static
-int virNetDevMacVLanTapOpen(const char *ifname,
- int retries)
-{
- FILE *file;
- char path[64];
- int ifindex;
- char tapname[50];
- int tapfd;
-
- if (snprintf(path, sizeof(path),
- "/sys/class/net/%s/ifindex", ifname) >= sizeof(path)) {
- virReportSystemError(errno,
- "%s",
- _("buffer for ifindex path is too small"));
- return -1;
- }
-
- file = fopen(path, "r");
-
- if (!file) {
- virReportSystemError(errno,
- _("cannot open macvtap file %s to determine "
- "interface index"), path);
- return -1;
- }
-
- if (fscanf(file, "%d", &ifindex) != 1) {
- virReportSystemError(errno,
- "%s",_("cannot determine macvtap's tap
device "
- "interface index"));
- VIR_FORCE_FCLOSE(file);
- return -1;
- }
-
- VIR_FORCE_FCLOSE(file);
-
- if (snprintf(tapname, sizeof(tapname),
- "/dev/tap%d", ifindex) >= sizeof(tapname)) {
- virReportSystemError(errno,
- "%s",
- _("internal buffer for tap device is too
small"));
- return -1;
- }
-
- while (1) {
- /* may need to wait for udev to be done */
- tapfd = open(tapname, O_RDWR);
- if (tapfd < 0 && retries > 0) {
- retries--;
- usleep(20000);
- continue;
- }
- break;
- }
-
- if (tapfd < 0)
- virReportSystemError(errno,
- _("cannot open macvtap tap device %s"),
- tapname);
-
- return tapfd;
-}
-
-
-/**
- * virNetDevMacVLanTapSetup:
- * @tapfd: file descriptor of the macvtap tap
- * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it
- *
- * Returns 0 on success, -1 in case of fatal error, error code otherwise.
- *
- * Turn the IFF_VNET_HDR flag, if requested and available, make sure
- * it's off in the other cases.
- * A fatal error is defined as the VNET_HDR flag being set but it cannot
- * be turned off for some reason. This is reported with -1. Other fatal
- * error is not being able to read the interface flags. In that case the
- * macvtap device should not be used.
- */
-static int
-virNetDevMacVLanTapSetup(int tapfd, int vnet_hdr)
-{
- unsigned int features;
- struct ifreq ifreq;
- short new_flags = 0;
- int rc_on_fail = 0;
- const char *errmsg = NULL;
-
- memset(&ifreq, 0, sizeof(ifreq));
-
- if (ioctl(tapfd, TUNGETIFF, &ifreq) < 0) {
- virReportSystemError(errno, "%s",
- _("cannot get interface flags on macvtap tap"));
- return -1;
- }
-
- new_flags = ifreq.ifr_flags;
-
- if ((ifreq.ifr_flags & IFF_VNET_HDR) && !vnet_hdr) {
- new_flags = ifreq.ifr_flags & ~IFF_VNET_HDR;
- rc_on_fail = -1;
- errmsg = _("cannot clean IFF_VNET_HDR flag on macvtap tap");
- } else if ((ifreq.ifr_flags & IFF_VNET_HDR) == 0 && vnet_hdr) {
- if (ioctl(tapfd, TUNGETFEATURES, &features) < 0) {
- virReportSystemError(errno, "%s",
- _("cannot get feature flags on macvtap tap"));
- return -1;
- }
- if ((features & IFF_VNET_HDR)) {
- new_flags = ifreq.ifr_flags | IFF_VNET_HDR;
- errmsg = _("cannot set IFF_VNET_HDR flag on macvtap tap");
- }
- }
-
- if (new_flags != ifreq.ifr_flags) {
- ifreq.ifr_flags = new_flags;
- if (ioctl(tapfd, TUNSETIFF, &ifreq) < 0) {
- virReportSystemError(errno, "%s", errmsg);
- return rc_on_fail;
- }
- }
-
- return 0;
-}
-
-
-static const uint32_t modeMap[VIR_NETDEV_MACVLAN_MODE_LAST] = {
- [VIR_NETDEV_MACVLAN_MODE_VEPA] = MACVLAN_MODE_VEPA,
- [VIR_NETDEV_MACVLAN_MODE_PRIVATE] = MACVLAN_MODE_PRIVATE,
- [VIR_NETDEV_MACVLAN_MODE_BRIDGE] = MACVLAN_MODE_BRIDGE,
- [VIR_NETDEV_MACVLAN_MODE_PASSTHRU] = MACVLAN_MODE_PASSTHRU,
-};
-
-/**
- * virNetDevMacVLanCreate:
- * Create an instance of a macvtap device and open its tap character
- * device.
- * @tgifname: Interface name that the macvtap is supposed to have. May
- * 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: int describing the mode for 'bridge', 'vepa', 'private'
or 'passthru'.
- * @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.
- *
- * Returns file descriptor of the tap device in case of success,
- * negative value otherwise with error reported.
- *
- */
-int virNetDevMacVLanCreate(const char *tgifname,
- const unsigned char *macaddress,
- const char *linkdev,
- enum virNetDevMacVLanMode mode,
- int vnet_hdr,
- const unsigned char *vmuuid,
- virNetDevVPortProfilePtr virtPortProfile,
- char **res_ifname,
- enum virNetDevVPortProfileOp vmOp,
- char *stateDir,
- virNetDevBandwidthPtr bandwidth)
-{
- const char *type = "macvtap";
- int c, rc;
- char ifname[IFNAMSIZ];
- int retries, do_retry = 0;
- uint32_t macvtapMode;
- const char *cr_ifname;
- int ifindex;
-
- macvtapMode = modeMap[mode];
-
- *res_ifname = NULL;
-
- VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__,
virNetDevVPortProfileOpTypeToString(vmOp));
-
- /** Note: When using PASSTHROUGH mode with MACVTAP devices the link
- * device's MAC address must be set to the VMs MAC address. In
- * order to not confuse the first switch or bridge in line this MAC
- * address must be reset when the VM is shut down.
- * This is especially important when using SRIOV capable cards that
- * emulate their switch in firmware.
- */
- if (mode == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) {
- if (ifaceReplaceMacAddress(macaddress, linkdev, stateDir) < 0) {
- return -1;
- }
- }
-
- if (tgifname) {
- if(ifaceGetIndex(false, tgifname, &ifindex) == 0) {
- if (STRPREFIX(tgifname,
- MACVTAP_NAME_PREFIX)) {
- goto create_name;
- }
- virReportSystemError(errno,
- _("Interface %s already exists"), tgifname);
- return -1;
- }
- cr_ifname = tgifname;
- rc = ifaceMacvtapLinkAdd(type, macaddress, 6, tgifname, linkdev,
- macvtapMode, &do_retry);
- if (rc < 0)
- return -1;
- } else {
-create_name:
- retries = 5;
- for (c = 0; c < 8192; c++) {
- snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
- if (ifaceGetIndex(false, ifname, &ifindex) == -ENODEV) {
- rc = ifaceMacvtapLinkAdd(type, macaddress, 6, ifname, linkdev,
- macvtapMode, &do_retry);
- if (rc == 0)
- break;
-
- if (do_retry && --retries)
- continue;
- return -1;
- }
- }
- cr_ifname = ifname;
- }
-
- if (virNetDevVPortProfileAssociate(cr_ifname,
- virtPortProfile,
- macaddress,
- linkdev,
- vmuuid, vmOp) < 0) {
- rc = -1;
- goto link_del_exit;
- }
-
- if (virNetDevSetOnline(cr_ifname, true) < 0) {
- rc = -1;
- goto disassociate_exit;
- }
-
- rc = virNetDevMacVLanTapOpen(cr_ifname, 10);
- if (rc >= 0) {
- if (virNetDevMacVLanTapSetup(rc, vnet_hdr) < 0) {
- VIR_FORCE_CLOSE(rc); /* sets rc to -1 */
- goto disassociate_exit;
- }
- *res_ifname = strdup(cr_ifname);
- } else
- goto disassociate_exit;
-
- if (virNetDevBandwidthSet(cr_ifname, bandwidth) < 0) {
- macvtapError(VIR_ERR_INTERNAL_ERROR,
- _("cannot set bandwidth limits on %s"),
- cr_ifname);
- rc = -1;
- goto disassociate_exit;
- }
-
-
- return rc;
-
-disassociate_exit:
- ignore_value(virNetDevVPortProfileDisassociate(cr_ifname,
- virtPortProfile,
- macaddress,
- linkdev,
- vmOp));
-
-link_del_exit:
- ifaceLinkDel(cr_ifname);
-
- return rc;
-}
-
-
-/**
- * delMacvtap:
- * @ifname : The name of the macvtap interface
- * @linkdev: The interface name of the NIC to connect to the external bridge
- * @virtPortProfile: pointer to object holding the virtual port profile data
- *
- * Delete an interface given its name. Disassociate
- * it with the switch if port profile parameters
- * were provided.
- */
-int virNetDevMacVLanDelete(const char *ifname,
- const unsigned char *macaddr,
- const char *linkdev,
- int mode,
- virNetDevVPortProfilePtr virtPortProfile,
- char *stateDir)
-{
- int ret = 0;
- if (mode == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) {
- ifaceRestoreMacAddress(linkdev, stateDir);
- }
-
- if (ifname) {
- if (virNetDevVPortProfileDisassociate(ifname,
- virtPortProfile,
- macaddr,
- linkdev,
- VIR_NETDEV_VPORT_PROFILE_OP_DESTROY) <
0)
- ret = -1;
- if (ifaceLinkDel(ifname) < 0)
- ret = -1;
- }
- return ret;
-}
-
-# endif /* WITH_MACVTAP */
-
-# ifdef IFLA_PORT_MAX
-
-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 {
- macvtapError(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;
- const char *msg = NULL;
- 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)) {
- msg = _("error parsing IFLA_PORT_SELF part");
- goto err_exit;
- }
- } else {
- msg = _("IFLA_PORT_SELF is missing");
- goto err_exit;
- }
- } 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) {
- msg = _("error while iterating over IFLA_VF_PORTS part");
- goto err_exit;
- }
-
- if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
- ifla_port_policy)) {
- msg = _("error parsing IFLA_VF_PORT part");
- goto err_exit;
- }
-
- 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) {
- msg = _("Could not find netlink response with "
- "expected parameters");
- goto err_exit;
- }
- } else {
- msg = _("IFLA_VF_PORTS is missing");
- goto err_exit;
- }
- }
-
- 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 {
- msg = _("no IFLA_PORT_RESPONSE found in netlink message");
- goto err_exit;
- }
- }
-
-err_exit:
- if (msg)
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
-
- return rc;
-}
-
-
-static int
-virNetDevVPortProfileOpSetLink(const char *ifname, int ifindex,
- bool nltarget_kernel,
- const unsigned char *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;
- uint32_t 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, },
- };
-
- memcpy(ifla_vf_mac.mac, macaddr, 6);
-
- 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) {
- pid = virNetDevVPortProfileGetLldpadPid();
- if (pid == 0)
- goto err_exit;
- }
-
- if (nlComm(nl_msg, &recvbuf, &recvbuflen, pid) < 0)
- goto err_exit;
-
- 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 err_exit;
- }
- break;
-
- case NLMSG_DONE:
- break;
-
- default:
- goto malformed_resp;
- }
-
- rc = 0;
-
-err_exit:
- nlmsg_free(nl_msg);
-
- VIR_FREE(recvbuf);
-
- return rc;
-
-malformed_resp:
- nlmsg_free(nl_msg);
-
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("malformed netlink response message"));
- VIR_FREE(recvbuf);
- return rc;
-
-buffer_too_small:
- nlmsg_free(nl_msg);
-
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("allocated netlink buffer is too small"));
- 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 unsigned char *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;
- 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) {
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("sending of PortProfileRequest failed."));
- return rc;
- }
-
- while (--repeats >= 0) {
- rc = ifaceMacvtapLinkDump(nltarget_kernel, NULL, ifindex, tb,
- &recvbuf, virNetDevVPortProfileGetLldpadPid);
- if (rc < 0)
- goto err_exit;
-
- rc = virNetDevVPortProfileGetStatus(tb, vf, instanceId, nltarget_kernel,
- is8021Qbg, &status);
- if (rc < 0)
- goto err_exit;
- 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) {
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("port-profile setlink timed out"));
- rc = -2;
- }
-
-err_exit:
- VIR_FREE(recvbuf);
-
- return rc;
-}
-
-# endif /* IFLA_PORT_MAX */
-
-
-# ifdef IFLA_VF_PORT_MAX
-
-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 = ifaceGetNthParent(ifindex, ifname, 1,
- root_ifindex, root_ifname, &nth)) < 0)
- return ret;
- if (nth == 0)
- break;
- if (*vlanid == -1) {
- if (ifaceGetVlanID(root_ifname, vlanid) < 0)
- *vlanid = -1;
- }
-
- ifindex = *root_ifindex;
- ifname = NULL;
- }
-
- return 0;
-}
-
-# endif
-
-/* Returns 0 on success, -1 on general failure, and -2 on timeout */
-static int
-virNetDevVPortProfileOp8021Qbg(const char *ifname,
- const unsigned char *macaddr,
- const virNetDevVPortProfilePtr virtPort,
- enum virNetDevVPortOp virtPortOp)
-{
- int rc = 0;
-
-# ifndef IFLA_VF_PORT_MAX
-
- (void)ifname;
- (void)macaddr;
- (void)virtPort;
- (void)virtPortOp;
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Kernel VF Port support was missing at compile time."));
- rc = -1;
-
-# else /* IFLA_VF_PORT_MAX */
-
- int op = PORT_REQUEST_ASSOCIATE;
- struct ifla_port_vsi portVsi = {
- .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
- .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
- };
- bool nltarget_kernel = false;
- int vlanid;
- int physdev_ifindex = 0;
- char physdev_ifname[IFNAMSIZ] = { 0, };
- int vf = PORT_SELF_VF;
-
- if (virNetDevVPortProfileGetPhysdevAndVlan(ifname, &physdev_ifindex,
physdev_ifname,
- &vlanid) < 0) {
- rc = -1;
- goto err_exit;
- }
-
- if (vlanid < 0)
- vlanid = 0;
-
- portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
- portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
- portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
-
- switch (virtPortOp) {
- case PREASSOCIATE:
- op = PORT_REQUEST_PREASSOCIATE;
- break;
- case ASSOCIATE:
- op = PORT_REQUEST_ASSOCIATE;
- break;
- case DISASSOCIATE:
- op = PORT_REQUEST_DISASSOCIATE;
- break;
- default:
- macvtapError(VIR_ERR_INTERNAL_ERROR,
- _("operation type %d not supported"), virtPortOp);
- rc = -1;
- goto err_exit;
- }
-
- rc = virNetDevVPortProfileOpCommon(physdev_ifname, physdev_ifindex,
- nltarget_kernel,
- macaddr,
- vlanid,
- NULL,
- &portVsi,
- virtPort->u.virtPort8021Qbg.instanceID,
- NULL,
- vf,
- op);
-
-err_exit:
-
-# endif /* IFLA_VF_PORT_MAX */
-
- return rc;
-}
-
-
-# ifdef IFLA_VF_PORT_MAX
-static int
-virNetDevVPortProfileGetPhysfnDev(const char *linkdev,
- int32_t *vf,
- char **physfndev)
-{
- int rc = -1;
-
- if (ifaceIsVirtualFunction(linkdev) == 1) {
- /* if linkdev is SR-IOV VF, then set vf = VF index */
- /* and set linkdev = PF device */
-
- rc = ifaceGetPhysicalFunction(linkdev, physfndev);
- if (!rc)
- rc = ifaceGetVirtualFunctionIndex(*physfndev, linkdev, vf);
- } else {
-
- /* Not SR-IOV VF: physfndev is linkdev and VF index
- * refers to linkdev self
- */
-
- *vf = PORT_SELF_VF;
- *physfndev = strdup(linkdev);
- if (!*physfndev) {
- virReportOOMError();
- goto err_exit;
- }
- rc = 0;
- }
-
-err_exit:
-
- return rc;
-}
-# endif /* IFLA_VF_PORT_MAX */
-
-/* Returns 0 on success, -1 on general failure, and -2 on timeout */
-static int
-virNetDevVPortProfileOp8021Qbh(const char *ifname,
- const unsigned char *macaddr,
- const virNetDevVPortProfilePtr virtPort,
- const unsigned char *vm_uuid,
- enum virNetDevVPortOp virtPortOp)
-{
- int rc = 0;
-
-# ifndef IFLA_VF_PORT_MAX
-
- (void)ifname;
- (void)macaddr;
- (void)virtPort;
- (void)vm_uuid;
- (void)virtPortOp;
- macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Kernel VF Port support was missing at compile time."));
- rc = -1;
-
-# else /* IFLA_VF_PORT_MAX */
-
- char *physfndev = NULL;
- unsigned char hostuuid[VIR_UUID_BUFLEN];
- int32_t vf;
- bool nltarget_kernel = true;
- int ifindex;
- int vlanid = -1;
-
- rc = virNetDevVPortProfileGetPhysfnDev(ifname, &vf, &physfndev);
- if (rc < 0)
- goto err_exit;
-
- rc = ifaceGetIndex(true, physfndev, &ifindex);
- if (rc < 0)
- goto err_exit;
-
- switch (virtPortOp) {
- case PREASSOCIATE_RR:
- case ASSOCIATE:
- errno = virGetHostUUID(hostuuid);
- if (errno) {
- rc = -1;
- goto err_exit;
- }
-
- rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
- nltarget_kernel,
- macaddr,
- vlanid,
- virtPort->u.virtPort8021Qbh.profileID,
- NULL,
- vm_uuid,
- hostuuid,
- vf,
- (virtPortOp == PREASSOCIATE_RR) ?
- PORT_REQUEST_PREASSOCIATE_RR
- : PORT_REQUEST_ASSOCIATE);
- if (rc == -2)
- /* Association timed out, disassociate */
- virNetDevVPortProfileOpCommon(NULL, ifindex,
- nltarget_kernel,
- NULL,
- vlanid,
- NULL,
- NULL,
- NULL,
- NULL,
- vf,
- PORT_REQUEST_DISASSOCIATE);
- break;
-
- case DISASSOCIATE:
- rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
- nltarget_kernel,
- NULL,
- vlanid,
- NULL,
- NULL,
- NULL,
- NULL,
- vf,
- PORT_REQUEST_DISASSOCIATE);
- break;
-
- default:
- macvtapError(VIR_ERR_INTERNAL_ERROR,
- _("operation type %d not supported"), virtPortOp);
- rc = -1;
- }
-
-err_exit:
- VIR_FREE(physfndev);
-
-# endif /* IFLA_VF_PORT_MAX */
-
- 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)
- *
- * 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 unsigned char *macvtap_macaddr,
- const char *linkdev,
- const unsigned char *vmuuid,
- enum virNetDevVPortProfileOp vmOp)
-{
- int rc = 0;
-
- VIR_DEBUG("Associating port profile '%p' on link device
'%s'",
- virtPort, macvtap_ifname);
-
- 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_LAST:
- break;
-
- case VIR_NETDEV_VPORT_PROFILE_8021QBG:
- rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
- virtPort,
- (vmOp ==
VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
- ? PREASSOCIATE
- : ASSOCIATE);
- break;
-
- case VIR_NETDEV_VPORT_PROFILE_8021QBH:
- rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr,
- virtPort, vmuuid,
- (vmOp ==
VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
- ? PREASSOCIATE_RR
- : 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 unsigned char *macvtap_macaddr,
- const char *linkdev,
- 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_LAST:
- break;
-
- case VIR_NETDEV_VPORT_PROFILE_8021QBG:
- rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
- virtPort, DISASSOCIATE);
- break;
-
- case VIR_NETDEV_VPORT_PROFILE_8021QBH:
- /* avoid disassociating twice */
- if (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH)
- break;
- ignore_value(virNetDevSetOnline(linkdev, false));
- rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr,
- virtPort, NULL, DISASSOCIATE);
- break;
- }
-
- return rc;
-}
-
-#endif /* WITH_MACVTAP || WITH_NETDEV_VPORT_PROFILE */
-
-VIR_ENUM_IMPL(virNetDevVPortProfileOp, VIR_NETDEV_VPORT_PROFILE_OP_LAST,
- "create",
- "save",
- "restore",
- "destroy",
- "migrate out",
- "migrate in start",
- "migrate in finish",
- "no-op")
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
new file mode 100644
index 0000000..ab111f0
--- /dev/null
+++ b/src/util/virnetdevmacvlan.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Stefan Berger <stefanb(a)us.ibm.com>
+ *
+ * Notes:
+ * netlink:
http://lovezutto.googlepages.com/netlink.pdf
+ * iproute2 package
+ *
+ */
+
+#include <config.h>
+
+
+#include "virnetdevmacvlan.h"
+#include "util.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_NET
+
+#define macvtapError(code, ...) \
+ virReportErrorHelper(VIR_FROM_NET, code, __FILE__, \
+ __FUNCTION__, __LINE__, __VA_ARGS__)
+
+
+VIR_ENUM_IMPL(virNetDevMacVLanMode, VIR_NETDEV_MACVLAN_MODE_LAST,
+ "vepa",
+ "private",
+ "bridge",
+ "passthrough")
+
+#if WITH_MACVTAP
+
+# include <stdint.h>
+# include <stdio.h>
+# include <errno.h>
+# include <fcntl.h>
+# include <sys/socket.h>
+# include <sys/ioctl.h>
+
+# include <linux/if.h>
+# include <linux/if_tun.h>
+
+/* Older kernels lacked this enum value. */
+# if !HAVE_DECL_MACVLAN_MODE_PASSTHRU
+# define MACVLAN_MODE_PASSTHRU 8
+# endif
+
+# include "memory.h"
+# include "logging.h"
+# include "interface.h"
+# include "uuid.h"
+# include "virfile.h"
+# include "netlink.h"
+# include "virnetdev.h"
+
+# define MACVTAP_NAME_PREFIX "macvtap"
+# define MACVTAP_NAME_PATTERN "macvtap%d"
+
+/**
+ * virNetDevMacVLanTapOpen:
+ * Open the macvtap's tap device.
+ * @ifname: Name of the macvtap interface
+ * @retries : Number of retries in case udev for example may need to be
+ * waited for to create the tap chardev
+ * Returns negative value in case of error, the file descriptor otherwise.
+ */
+static
+int virNetDevMacVLanTapOpen(const char *ifname,
+ int retries)
+{
+ FILE *file;
+ char path[64];
+ int ifindex;
+ char tapname[50];
+ int tapfd;
+
+ if (snprintf(path, sizeof(path),
+ "/sys/class/net/%s/ifindex", ifname) >= sizeof(path)) {
+ virReportSystemError(errno,
+ "%s",
+ _("buffer for ifindex path is too small"));
+ return -1;
+ }
+
+ file = fopen(path, "r");
+
+ if (!file) {
+ virReportSystemError(errno,
+ _("cannot open macvtap file %s to determine "
+ "interface index"), path);
+ return -1;
+ }
+
+ if (fscanf(file, "%d", &ifindex) != 1) {
+ virReportSystemError(errno,
+ "%s",_("cannot determine macvtap's tap
device "
+ "interface index"));
+ VIR_FORCE_FCLOSE(file);
+ return -1;
+ }
+
+ VIR_FORCE_FCLOSE(file);
+
+ if (snprintf(tapname, sizeof(tapname),
+ "/dev/tap%d", ifindex) >= sizeof(tapname)) {
+ virReportSystemError(errno,
+ "%s",
+ _("internal buffer for tap device is too
small"));
+ return -1;
+ }
+
+ while (1) {
+ /* may need to wait for udev to be done */
+ tapfd = open(tapname, O_RDWR);
+ if (tapfd < 0 && retries > 0) {
+ retries--;
+ usleep(20000);
+ continue;
+ }
+ break;
+ }
+
+ if (tapfd < 0)
+ virReportSystemError(errno,
+ _("cannot open macvtap tap device %s"),
+ tapname);
+
+ return tapfd;
+}
+
+
+/**
+ * virNetDevMacVLanTapSetup:
+ * @tapfd: file descriptor of the macvtap tap
+ * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it
+ *
+ * Returns 0 on success, -1 in case of fatal error, error code otherwise.
+ *
+ * Turn the IFF_VNET_HDR flag, if requested and available, make sure
+ * it's off in the other cases.
+ * A fatal error is defined as the VNET_HDR flag being set but it cannot
+ * be turned off for some reason. This is reported with -1. Other fatal
+ * error is not being able to read the interface flags. In that case the
+ * macvtap device should not be used.
+ */
+static int
+virNetDevMacVLanTapSetup(int tapfd, int vnet_hdr)
+{
+ unsigned int features;
+ struct ifreq ifreq;
+ short new_flags = 0;
+ int rc_on_fail = 0;
+ const char *errmsg = NULL;
+
+ memset(&ifreq, 0, sizeof(ifreq));
+
+ if (ioctl(tapfd, TUNGETIFF, &ifreq) < 0) {
+ virReportSystemError(errno, "%s",
+ _("cannot get interface flags on macvtap tap"));
+ return -1;
+ }
+
+ new_flags = ifreq.ifr_flags;
+
+ if ((ifreq.ifr_flags & IFF_VNET_HDR) && !vnet_hdr) {
+ new_flags = ifreq.ifr_flags & ~IFF_VNET_HDR;
+ rc_on_fail = -1;
+ errmsg = _("cannot clean IFF_VNET_HDR flag on macvtap tap");
+ } else if ((ifreq.ifr_flags & IFF_VNET_HDR) == 0 && vnet_hdr) {
+ if (ioctl(tapfd, TUNGETFEATURES, &features) < 0) {
+ virReportSystemError(errno, "%s",
+ _("cannot get feature flags on macvtap tap"));
+ return -1;
+ }
+ if ((features & IFF_VNET_HDR)) {
+ new_flags = ifreq.ifr_flags | IFF_VNET_HDR;
+ errmsg = _("cannot set IFF_VNET_HDR flag on macvtap tap");
+ }
+ }
+
+ if (new_flags != ifreq.ifr_flags) {
+ ifreq.ifr_flags = new_flags;
+ if (ioctl(tapfd, TUNSETIFF, &ifreq) < 0) {
+ virReportSystemError(errno, "%s", errmsg);
+ return rc_on_fail;
+ }
+ }
+
+ return 0;
+}
+
+
+static const uint32_t modeMap[VIR_NETDEV_MACVLAN_MODE_LAST] = {
+ [VIR_NETDEV_MACVLAN_MODE_VEPA] = MACVLAN_MODE_VEPA,
+ [VIR_NETDEV_MACVLAN_MODE_PRIVATE] = MACVLAN_MODE_PRIVATE,
+ [VIR_NETDEV_MACVLAN_MODE_BRIDGE] = MACVLAN_MODE_BRIDGE,
+ [VIR_NETDEV_MACVLAN_MODE_PASSTHRU] = MACVLAN_MODE_PASSTHRU,
+};
+
+/**
+ * virNetDevMacVLanCreate:
+ * Create an instance of a macvtap device and open its tap character
+ * device.
+ * @tgifname: Interface name that the macvtap is supposed to have. May
+ * 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: int describing the mode for 'bridge', 'vepa', 'private'
or 'passthru'.
+ * @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.
+ *
+ * Returns file descriptor of the tap device in case of success,
+ * negative value otherwise with error reported.
+ *
+ */
+int virNetDevMacVLanCreate(const char *tgifname,
+ const unsigned char *macaddress,
+ const char *linkdev,
+ enum virNetDevMacVLanMode mode,
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ char **res_ifname,
+ enum virNetDevVPortProfileOp vmOp,
+ char *stateDir,
+ virNetDevBandwidthPtr bandwidth)
+{
+ const char *type = "macvtap";
+ int c, rc;
+ char ifname[IFNAMSIZ];
+ int retries, do_retry = 0;
+ uint32_t macvtapMode;
+ const char *cr_ifname;
+ int ifindex;
+
+ macvtapMode = modeMap[mode];
+
+ *res_ifname = NULL;
+
+ VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__,
virNetDevVPortProfileOpTypeToString(vmOp));
+
+ /** Note: When using PASSTHROUGH mode with MACVTAP devices the link
+ * device's MAC address must be set to the VMs MAC address. In
+ * order to not confuse the first switch or bridge in line this MAC
+ * address must be reset when the VM is shut down.
+ * This is especially important when using SRIOV capable cards that
+ * emulate their switch in firmware.
+ */
+ if (mode == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) {
+ if (ifaceReplaceMacAddress(macaddress, linkdev, stateDir) < 0) {
+ return -1;
+ }
+ }
+
+ if (tgifname) {
+ if(ifaceGetIndex(false, tgifname, &ifindex) == 0) {
+ if (STRPREFIX(tgifname,
+ MACVTAP_NAME_PREFIX)) {
+ goto create_name;
+ }
+ virReportSystemError(errno,
+ _("Interface %s already exists"), tgifname);
+ return -1;
+ }
+ cr_ifname = tgifname;
+ rc = ifaceMacvtapLinkAdd(type, macaddress, 6, tgifname, linkdev,
+ macvtapMode, &do_retry);
+ if (rc < 0)
+ return -1;
+ } else {
+create_name:
+ retries = 5;
+ for (c = 0; c < 8192; c++) {
+ snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
+ if (ifaceGetIndex(false, ifname, &ifindex) == -ENODEV) {
+ rc = ifaceMacvtapLinkAdd(type, macaddress, 6, ifname, linkdev,
+ macvtapMode, &do_retry);
+ if (rc == 0)
+ break;
+
+ if (do_retry && --retries)
+ continue;
+ return -1;
+ }
+ }
+ cr_ifname = ifname;
+ }
+
+ if (virNetDevVPortProfileAssociate(cr_ifname,
+ virtPortProfile,
+ macaddress,
+ linkdev,
+ vmuuid, vmOp) < 0) {
+ rc = -1;
+ goto link_del_exit;
+ }
+
+ if (virNetDevSetOnline(cr_ifname, true) < 0) {
+ rc = -1;
+ goto disassociate_exit;
+ }
+
+ rc = virNetDevMacVLanTapOpen(cr_ifname, 10);
+ if (rc >= 0) {
+ if (virNetDevMacVLanTapSetup(rc, vnet_hdr) < 0) {
+ VIR_FORCE_CLOSE(rc); /* sets rc to -1 */
+ goto disassociate_exit;
+ }
+ *res_ifname = strdup(cr_ifname);
+ } else
+ goto disassociate_exit;
+
+ if (virNetDevBandwidthSet(cr_ifname, bandwidth) < 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot set bandwidth limits on %s"),
+ cr_ifname);
+ rc = -1;
+ goto disassociate_exit;
+ }
+
+
+ return rc;
+
+disassociate_exit:
+ ignore_value(virNetDevVPortProfileDisassociate(cr_ifname,
+ virtPortProfile,
+ macaddress,
+ linkdev,
+ vmOp));
+
+link_del_exit:
+ ifaceLinkDel(cr_ifname);
+
+ return rc;
+}
+
+
+/**
+ * delMacvtap:
+ * @ifname : The name of the macvtap interface
+ * @linkdev: The interface name of the NIC to connect to the external bridge
+ * @virtPortProfile: pointer to object holding the virtual port profile data
+ *
+ * Delete an interface given its name. Disassociate
+ * it with the switch if port profile parameters
+ * were provided.
+ */
+int virNetDevMacVLanDelete(const char *ifname,
+ const unsigned char *macaddr,
+ const char *linkdev,
+ int mode,
+ virNetDevVPortProfilePtr virtPortProfile,
+ char *stateDir)
+{
+ int ret = 0;
+ if (mode == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) {
+ ifaceRestoreMacAddress(linkdev, stateDir);
+ }
+
+ if (ifname) {
+ if (virNetDevVPortProfileDisassociate(ifname,
+ virtPortProfile,
+ macaddr,
+ linkdev,
+ VIR_NETDEV_VPORT_PROFILE_OP_DESTROY) <
0)
+ ret = -1;
+ if (ifaceLinkDel(ifname) < 0)
+ ret = -1;
+ }
+ return ret;
+}
+
+#else /* ! WITH_MACVTAP */
+int virNetDevMacVLanCreate(const char *ifname ATTRIBUTE_UNUSED,
+ const unsigned char *macaddress ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ enum virNetDevMacVLanMode mode ATTRIBUTE_UNUSED,
+ int vnet_hdr ATTRIBUTE_UNUSED,
+ const unsigned char *vmuuid ATTRIBUTE_UNUSED,
+ virNetDevVPortProfilePtr virtPortProfile ATTRIBUTE_UNUSED,
+ char **res_ifname ATTRIBUTE_UNUSED,
+ enum virNetDevVPortProfileOp vmop ATTRIBUTE_UNUSED,
+ char *stateDir ATTRIBUTE_UNUSED,
+ virNetDevBandwidthPtr bandwidth ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Cannot create macvlan devices on this platform"));
+ return -1;
+}
+
+int virNetDevMacVLanDelete(const char *ifname ATTRIBUTE_UNUSED,
+ const unsigned char *macaddress ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ int mode ATTRIBUTE_UNUSED,
+ virNetDevVPortProfilePtr virtPortProfile ATTRIBUTE_UNUSED,
+ char *stateDir ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Cannot create macvlan devices on this platform"));
+ return -1;
+}
+#endif /* ! WITH_MACVTAP */
diff --git a/src/util/macvtap.h b/src/util/virnetdevmacvlan.h
similarity index 63%
rename from src/util/macvtap.h
rename to src/util/virnetdevmacvlan.h
index 5c5d84a..f3277f9 100644
--- a/src/util/macvtap.h
+++ b/src/util/virnetdevmacvlan.h
@@ -37,21 +37,7 @@ enum virNetDevMacVLanMode {
VIR_NETDEV_MACVLAN_MODE_LAST,
};
-
-enum virNetDevVPortProfileOp {
- VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
- VIR_NETDEV_VPORT_PROFILE_OP_SAVE,
- VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
- VIR_NETDEV_VPORT_PROFILE_OP_DESTROY,
- VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_OUT,
- VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START,
- VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH,
- VIR_NETDEV_VPORT_PROFILE_OP_NO_OP,
-
- VIR_NETDEV_VPORT_PROFILE_OP_LAST
-};
-
-# if WITH_MACVTAP
+VIR_ENUM_DECL(virNetDevMacVLanMode)
int virNetDevMacVLanCreate(const char *ifname,
const unsigned char *macaddress,
@@ -76,26 +62,4 @@ int virNetDevMacVLanDelete(const char *ifname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(6) ATTRIBUTE_RETURN_CHECK;
-int virNetDevVPortProfileAssociate(const char *ifname,
- const virNetDevVPortProfilePtr virtPort,
- const unsigned char *macaddr,
- const char *linkdev,
- const unsigned char *vmuuid,
- enum virNetDevVPortProfileOp vmOp)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
- ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
-
-int virNetDevVPortProfileDisassociate(const char *ifname,
- const virNetDevVPortProfilePtr virtPort,
- const unsigned char *macaddr,
- const char *linkdev,
- enum virNetDevVPortProfileOp vmOp)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
- ATTRIBUTE_RETURN_CHECK;
-
-# endif /* WITH_MACVTAP */
-
-VIR_ENUM_DECL(virNetDevVPortProfileOp)
-VIR_ENUM_DECL(virNetDevMacVLanMode)
-
#endif /* __UTIL_MACVTAP_H__ */
diff --git a/src/util/virnetdevvportprofile.c b/src/util/virnetdevvportprofile.c
index 29abce6..1e826db 100644
--- a/src/util/virnetdevvportprofile.c
+++ b/src/util/virnetdevvportprofile.c
@@ -23,6 +23,65 @@
#include <config.h>
#include "virnetdevvportprofile.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_NET
+
+#define virNetDevError(code, ...) \
+ virReportErrorHelper(VIR_FROM_NET, code, __FILE__, \
+ __FUNCTION__, __LINE__, __VA_ARGS__)
+
+
+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 "netlink.h"
+# include "virfile.h"
+# include "memory.h"
+# include "interface.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 {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+ PREASSOCIATE = 0x3,
+ PREASSOCIATE_RR = 0x4,
+};
+
+#endif
bool
virNetDevVPortProfileEqual(virNetDevVPortProfilePtr a, virNetDevVPortProfilePtr b)
@@ -60,3 +119,764 @@ virNetDevVPortProfileEqual(virNetDevVPortProfilePtr a,
virNetDevVPortProfilePtr
return true;
}
+
+
+#ifdef 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 {
+ virNetDevError(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;
+ const char *msg = NULL;
+ 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)) {
+ msg = _("error parsing IFLA_PORT_SELF part");
+ goto err_exit;
+ }
+ } else {
+ msg = _("IFLA_PORT_SELF is missing");
+ goto err_exit;
+ }
+ } 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) {
+ msg = _("error while iterating over IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+
+ if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
+ ifla_port_policy)) {
+ msg = _("error parsing IFLA_VF_PORT part");
+ goto err_exit;
+ }
+
+ 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) {
+ msg = _("Could not find netlink response with "
+ "expected parameters");
+ goto err_exit;
+ }
+ } else {
+ msg = _("IFLA_VF_PORTS is missing");
+ goto err_exit;
+ }
+ }
+
+ 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 {
+ msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+ goto err_exit;
+ }
+ }
+
+err_exit:
+ if (msg)
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+
+static int
+virNetDevVPortProfileOpSetLink(const char *ifname, int ifindex,
+ bool nltarget_kernel,
+ const unsigned char *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;
+ uint32_t 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, },
+ };
+
+ memcpy(ifla_vf_mac.mac, macaddr, 6);
+
+ 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) {
+ pid = virNetDevVPortProfileGetLldpadPid();
+ if (pid == 0)
+ goto err_exit;
+ }
+
+ if (nlComm(nl_msg, &recvbuf, &recvbuflen, pid) < 0)
+ goto err_exit;
+
+ 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 err_exit;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ rc = 0;
+
+err_exit:
+ nlmsg_free(nl_msg);
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return rc;
+
+buffer_too_small:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("allocated netlink buffer is too small"));
+ 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 unsigned char *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;
+ 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) {
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed."));
+ return rc;
+ }
+
+ while (--repeats >= 0) {
+ rc = ifaceMacvtapLinkDump(nltarget_kernel, NULL, ifindex, tb,
+ &recvbuf, virNetDevVPortProfileGetLldpadPid);
+ if (rc < 0)
+ goto err_exit;
+
+ rc = virNetDevVPortProfileGetStatus(tb, vf, instanceId, nltarget_kernel,
+ is8021Qbg, &status);
+ if (rc < 0)
+ goto err_exit;
+ 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) {
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("port-profile setlink timed out"));
+ rc = -2;
+ }
+
+err_exit:
+ 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 = ifaceGetNthParent(ifindex, ifname, 1,
+ root_ifindex, root_ifname, &nth)) < 0)
+ return ret;
+ if (nth == 0)
+ break;
+ if (*vlanid == -1) {
+ if (ifaceGetVlanID(root_ifname, vlanid) < 0)
+ *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 unsigned char *macaddr,
+ const virNetDevVPortProfilePtr virtPort,
+ enum virNetDevVPortProfileLinkOp virtPortOp)
+{
+ int rc = 0;
+ int op = PORT_REQUEST_ASSOCIATE;
+ struct ifla_port_vsi portVsi = {
+ .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
+ .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+ };
+ bool nltarget_kernel = false;
+ int vlanid;
+ int physdev_ifindex = 0;
+ char physdev_ifname[IFNAMSIZ] = { 0, };
+ int vf = PORT_SELF_VF;
+
+ if (virNetDevVPortProfileGetPhysdevAndVlan(ifname, &physdev_ifindex,
physdev_ifname,
+ &vlanid) < 0) {
+ rc = -1;
+ goto err_exit;
+ }
+
+ if (vlanid < 0)
+ vlanid = 0;
+
+ portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+ portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+ portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+ switch (virtPortOp) {
+ case PREASSOCIATE:
+ op = PORT_REQUEST_PREASSOCIATE;
+ break;
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ virNetDevError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), virtPortOp);
+ rc = -1;
+ goto err_exit;
+ }
+
+ rc = virNetDevVPortProfileOpCommon(physdev_ifname, physdev_ifindex,
+ nltarget_kernel,
+ macaddr,
+ vlanid,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ vf,
+ op);
+
+err_exit:
+
+ return rc;
+}
+
+
+static int
+virNetDevVPortProfileGetPhysfnDev(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = -1;
+
+ if (ifaceIsVirtualFunction(linkdev) == 1) {
+ /* if linkdev is SR-IOV VF, then set vf = VF index */
+ /* and set linkdev = PF device */
+
+ rc = ifaceGetPhysicalFunction(linkdev, physfndev);
+ if (!rc)
+ rc = ifaceGetVirtualFunctionIndex(*physfndev, linkdev, vf);
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = strdup(linkdev);
+ if (!*physfndev) {
+ virReportOOMError();
+ goto err_exit;
+ }
+ rc = 0;
+ }
+
+err_exit:
+
+ return rc;
+}
+
+
+/* Returns 0 on success, -1 on general failure, and -2 on timeout */
+static int
+virNetDevVPortProfileOp8021Qbh(const char *ifname,
+ const unsigned char *macaddr,
+ const virNetDevVPortProfilePtr virtPort,
+ const unsigned char *vm_uuid,
+ enum virNetDevVPortProfileLinkOp virtPortOp)
+{
+ int rc = 0;
+ char *physfndev = NULL;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
+ bool nltarget_kernel = true;
+ int ifindex;
+ int vlanid = -1;
+
+ rc = virNetDevVPortProfileGetPhysfnDev(ifname, &vf, &physfndev);
+ if (rc < 0)
+ goto err_exit;
+
+ rc = ifaceGetIndex(true, physfndev, &ifindex);
+ if (rc < 0)
+ goto err_exit;
+
+ switch (virtPortOp) {
+ case PREASSOCIATE_RR:
+ case ASSOCIATE:
+ errno = virGetHostUUID(hostuuid);
+ if (errno) {
+ rc = -1;
+ goto err_exit;
+ }
+
+ rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
+ nltarget_kernel,
+ macaddr,
+ vlanid,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ hostuuid,
+ vf,
+ (virtPortOp == PREASSOCIATE_RR) ?
+ PORT_REQUEST_PREASSOCIATE_RR
+ : PORT_REQUEST_ASSOCIATE);
+ if (rc == -2)
+ /* Association timed out, disassociate */
+ virNetDevVPortProfileOpCommon(NULL, ifindex,
+ nltarget_kernel,
+ NULL,
+ vlanid,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vf,
+ PORT_REQUEST_DISASSOCIATE);
+ break;
+
+ case DISASSOCIATE:
+ rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
+ nltarget_kernel,
+ NULL,
+ vlanid,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vf,
+ PORT_REQUEST_DISASSOCIATE);
+ break;
+
+ default:
+ virNetDevError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), virtPortOp);
+ rc = -1;
+ }
+
+err_exit:
+ 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)
+ *
+ * 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 unsigned char *macvtap_macaddr,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ enum virNetDevVPortProfileOp vmOp)
+{
+ int rc = 0;
+
+ VIR_DEBUG("Associating port profile '%p' on link device
'%s'",
+ virtPort, macvtap_ifname);
+
+ 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_LAST:
+ break;
+
+ case VIR_NETDEV_VPORT_PROFILE_8021QBG:
+ rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
+ virtPort,
+ (vmOp ==
VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
+ ? PREASSOCIATE
+ : ASSOCIATE);
+ break;
+
+ case VIR_NETDEV_VPORT_PROFILE_8021QBH:
+ rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr,
+ virtPort, vmuuid,
+ (vmOp ==
VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
+ ? PREASSOCIATE_RR
+ : 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 unsigned char *macvtap_macaddr,
+ const char *linkdev,
+ 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_LAST:
+ break;
+
+ case VIR_NETDEV_VPORT_PROFILE_8021QBG:
+ rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
+ virtPort, DISASSOCIATE);
+ break;
+
+ case VIR_NETDEV_VPORT_PROFILE_8021QBH:
+ /* avoid disassociating twice */
+ if (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH)
+ break;
+ ignore_value(virNetDevSetOnline(linkdev, false));
+ rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr,
+ virtPort, NULL, DISASSOCIATE);
+ break;
+ }
+
+ return rc;
+}
+
+#else /* ! WITH_VIRTUALPORT */
+int virNetDevVPortProfileAssociate(const char *macvtap_ifname ATTRIBUTE_UNUSED,
+ const virNetDevVPortProfilePtr virtPort ATTRIBUTE_UNUSED,
+ const unsigned char *macvtap_macaddr ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ const unsigned char *vmuuid ATTRIBUTE_UNUSED,
+ enum virNetDevVPortProfileOp vmOp 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 unsigned char *macvtap_macaddr
ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ enum virNetDevVPortProfileOp vmOp
ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Virtual port profile association not supported on this
platform"));
+ return -1;
+}
+#endif /* ! WITH_VIRTUALPORT */
diff --git a/src/util/virnetdevvportprofile.h b/src/util/virnetdevvportprofile.h
index 3e6887e..19f6db5 100644
--- a/src/util/virnetdevvportprofile.h
+++ b/src/util/virnetdevvportprofile.h
@@ -36,9 +36,22 @@ enum virNetDevVPortProfile {
VIR_NETDEV_VPORT_PROFILE_LAST,
};
-
VIR_ENUM_DECL(virNetDevVPort)
+enum virNetDevVPortProfileOp {
+ VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
+ VIR_NETDEV_VPORT_PROFILE_OP_SAVE,
+ VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
+ VIR_NETDEV_VPORT_PROFILE_OP_DESTROY,
+ VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_OUT,
+ VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START,
+ VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH,
+ VIR_NETDEV_VPORT_PROFILE_OP_NO_OP,
+
+ VIR_NETDEV_VPORT_PROFILE_OP_LAST
+};
+VIR_ENUM_DECL(virNetDevVPortProfileOp)
+
/* profile data for macvtap (VEPA) */
typedef struct _virNetDevVPortProfile virNetDevVPortProfile;
typedef virNetDevVPortProfile *virNetDevVPortProfilePtr;
@@ -61,4 +74,22 @@ struct _virNetDevVPortProfile {
bool virNetDevVPortProfileEqual(virNetDevVPortProfilePtr a,
virNetDevVPortProfilePtr b);
+int virNetDevVPortProfileAssociate(const char *ifname,
+ const virNetDevVPortProfilePtr virtPort,
+ const unsigned char *macaddr,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ enum virNetDevVPortProfileOp vmOp)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
+ ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
+
+int virNetDevVPortProfileDisassociate(const char *ifname,
+ const virNetDevVPortProfilePtr virtPort,
+ const unsigned char *macaddr,
+ const char *linkdev,
+ enum virNetDevVPortProfileOp vmOp)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
+ ATTRIBUTE_RETURN_CHECK;
+
+
#endif /* __VIR_NETDEV_VPORT_PROFILE_H__ */
--
1.7.6.4