Devel
Threads by month
- ----- 2026 -----
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- 17 participants
- 40169 discussions
Otherwise, 'make check' in the python dir tries to reference a file in
docs that is built by 'make' but not by 'make check'.
* docs/Makefile.am (check-local): New rule.
Reported by Matthias Bolte.
---
Test with make && rm docs/libvirt-api.xml && make check
docs/Makefile.am | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 70b9e51..41068c6 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -143,6 +143,8 @@ html/%-%.html html/%-virterror.html %-api.xml %-refs.xml: \
$(srcdir)/../src/%.c \
$(srcdir)/../src/util/virterror.c
+check-local: all
+
clean-local:
rm -f *~ *.bak *.hierarchy *.signals *-unused.txt
--
1.7.0.1
2
2
27 May '10
Parse the BIOS UUID. This information may not be available, in that
case no host UUID is exposed in the capabilities XML.
---
src/esx/esx_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index 7c9c50e..b89de72 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -141,6 +141,69 @@ esxSupportsLongMode(esxPrivate *priv)
+static int
+esxLookupHostSystemBiosUuid(esxPrivate *priv, unsigned char *uuid)
+{
+ int result = -1;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *hostSystem = NULL;
+ esxVI_DynamicProperty *dynamicProperty = NULL;
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return -1;
+ }
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "hardware.systemInfo.uuid") < 0 ||
+ esxVI_LookupObjectContentByType(priv->host, priv->host->hostFolder,
+ "HostSystem", propertyNameList,
+ esxVI_Boolean_True, &hostSystem) < 0) {
+ goto cleanup;
+ }
+
+ if (hostSystem == NULL) {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Could not retrieve the HostSystem object"));
+ goto cleanup;
+ }
+
+ for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, "hardware.systemInfo.uuid")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_String) < 0) {
+ goto cleanup;
+ }
+
+ if (strlen(dynamicProperty->val->string) > 0) {
+ if (virUUIDParse(dynamicProperty->val->string, uuid) < 0) {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Could not parse UUID from string '%s'"),
+ dynamicProperty->val->string);
+ goto cleanup;
+ }
+ } else {
+ /* HostSystem has an empty UUID */
+ memset(uuid, 0, VIR_UUID_BUFLEN);
+ }
+
+ break;
+ } else {
+ VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
+ }
+ }
+
+ result = 0;
+
+ cleanup:
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&hostSystem);
+
+ return result;
+}
+
+
+
static virCapsPtr
esxCapsInit(esxPrivate *priv)
{
@@ -166,6 +229,10 @@ esxCapsInit(esxPrivate *priv)
virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x0c, 0x29 });
virCapabilitiesAddHostMigrateTransport(caps, "esx");
+ if (esxLookupHostSystemBiosUuid(priv, caps->host.host_uuid) < 0) {
+ goto failure;
+ }
+
/* i686 */
guest = virCapabilitiesAddGuest(caps, "hvm", "i686", 32, NULL, NULL, 0,
NULL);
--
1.7.0.4
2
2
A problem I am encountering is when trying to build after configuring
with --without-macvtap. [I apologize for the recent build errors...] In
that case the [dis|]associatePortProfileId functions become unused and
the compiler complains. I am trying to solve this now by making them
non-static functions that also later on, probably after some
changes/extensions to their passed parameters, can be used in the PCI
device passthrough case. I preprended 'vp' before their names, for
'virtualPort'. So the change in this version of the patch now is to not
define these two functions as static. However, I have not added them to
libvirt_private.syms.
In case of 802.1Qbg I am sending netlink messages to (future) lldpad,
another user space program. I used to use netlink multicast for doing
that, but now it's unicast. I am renaming the variable multicast to
nltarget_kernel and invert the logic for its testing. The two places
where the variable multicast received a boolean value has now the
opposite assigned to nltarget_kernel.
V8:
- To enable build with --without-macvtap rename the
[dis|]associatePortProfileId functions, prepend 'vp' before their
name and make them non-static functions.
- Renaming variable multicast to nltarget_kernel and inverting
the logic
V7:
- Addressing Jim Meyering's comments; this also touches existing
code for example for correcting indentation of break statements or
simplification of switch statements.
Changes from v5 to v6:
- Renamed occurrencvirVirtualPortProfileDef to
virVirtualPortProfileParamses
- 802.1Qbg part prepared for sending a RTM_SETLINK and getting
processing status back plus a subsequent RTM_GETLINK to
get IFLA_PORT_RESPONSE.
Note: This interface for 802.1Qbg may still change
Changes from v4 to v5:
- [David Allan] move getPhysfn inside IFLA_VF_PORT_MAX to avoid
compiler
warning when latest if_link.h isn't available
Changes from v3 to v4:
- move from Stefan's 802.1Qb{g|h} XML v8 to v9
- move hostuuid and vf index calcs to inside doPortProfileOp8021Qbh
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
configure.ac | 16
src/qemu/qemu_conf.c | 2
src/qemu/qemu_driver.c | 4
src/util/macvtap.c | 833 +++++++++++++++++++++++++++++++++++++++++++++----
src/util/macvtap.h | 1
5 files changed, 785 insertions(+), 71 deletions(-)
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || test "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1509,7 +1509,7 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
}
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3709,7 +3709,7 @@ static void qemudShutdownVMDaemon(struct
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
#endif
@@ -8514,7 +8514,7 @@ qemudDomainDetachNetDevice(struct qemud_
#if WITH_MACVTAP
if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(detach->ifname,
+ delMacvtap(detach->ifname, detach->data.direct.linkdev,
&detach->data.direct.virtPortProfile);
#endif
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,14 +61,22 @@
# 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)
+
+
-static int associatePortProfileId(const char *macvtap_ifname,
- const virVirtualPortProfileParamsPtr virtPort,
- int vf,
- const unsigned char *vmuuid);
-static int disassociatePortProfileId(const char *macvtap_ifname,
- const virVirtualPortProfileParamsPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
static int nlOpen(void)
@@ -97,7 +108,7 @@ static void nlClose(int fd)
*/
static
int nlComm(struct nlmsghdr *nlmsg,
- char **respbuf, int *respbuflen)
+ char **respbuf, unsigned int *respbuflen)
{
int rc = 0;
struct sockaddr_nl nladdr = {
@@ -159,6 +170,162 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @timeout_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, uint32_t nl_groups,
+ char **respbuf, unsigned int *respbuflen,
+ unsigned long long timeout_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ size_t rcv_offset = 0;
+ ssize_t nbytes;
+ struct timeval tv = {
+ .tv_sec = timeout_usecs / MICROSEC_PER_SEC,
+ .tv_usec = timeout_usecs % MICROSEC_PER_SEC,
+ };
+ bool got_valid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!got_valid) {
+
+ rcv_offset = 0;
+
+ while (1) {
+ int n;
+ fd_set rfds;
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcv_offset + rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n < 0)
+ virReportSystemError(errno, "%s",
+ _("error in select call"));
+ if (n == 0)
+ virReportSystemError(ETIMEDOUT, "%s",
+ _("no valid netlink response was received"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcv_offset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcv_offset += nbytes;
+ break;
+ }
+ *respbuflen = rcv_offset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (err->error != -EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ got_valid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ got_valid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+# endif /* IFLA_VF_PORT_MAX */
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +371,8 @@ nlAppend(struct nlmsghdr *nlm, int totle
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -213,15 +382,15 @@ link_add(const char *type,
int *retry)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta, *rta1, *li;
- struct ifinfomsg i = { .ifi_family = AF_UNSPEC };
+ struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
int ifindex;
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
if (ifaceGetIndex(true, srcdev, &ifindex) != 0)
return -1;
@@ -232,65 +401,46 @@ link_add(const char *type,
nlInit(nlm, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, RTM_NEWLINK);
- if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINK,
&ifindex, sizeof(ifindex));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_ADDRESS,
macaddress, macaddrsize);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (ifname) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
ifname, strlen(ifname) + 1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
}
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINKINFO, NULL, 0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ if (!rta ||
+ !(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_KIND,
type, strlen(type));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (macvlan_mode > 0) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_DATA,
NULL, 0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ if (!rta ||
+ !(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_MACVLAN_MODE,
&macvlan_mode, sizeof(macvlan_mode));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
rta1->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)rta1;
@@ -312,15 +462,15 @@ link_add(const char *type,
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
+ switch (err->error) {
case 0:
- break;
+ break;
- case EEXIST:
+ case -EEXIST:
*retry = 1;
rc = -1;
- break;
+ break;
default:
virReportSystemError(-err->error,
@@ -328,10 +478,10 @@ link_add(const char *type,
type);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -358,14 +508,14 @@ static int
link_del(const char *name)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta;
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
@@ -376,10 +526,7 @@ link_del(const char *name)
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
name, strlen(name)+1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
@@ -396,20 +543,16 @@ link_del(const char *name)
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
- case 0:
- break;
-
- default:
+ if (err->error) {
virReportSystemError(-err->error,
_("error destroying %s interface"),
name);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -509,11 +652,9 @@ macvtapModeFromInt(enum virDomainNetdevM
switch (mode) {
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_PRIVATE:
return MACVLAN_MODE_PRIVATE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_BRIDGE:
return MACVLAN_MODE_BRIDGE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA:
default:
@@ -654,10 +795,10 @@ create_name:
cr_ifname = ifname;
}
- if (associatePortProfileId(cr_ifname,
- virtPortProfile,
- -1,
- vmuuid) != 0) {
+ if (vpAssociatePortProfileId(cr_ifname,
+ linkdev,
+ virtPortProfile,
+ vmuuid) != 0) {
rc = -1;
goto link_del_exit;
}
@@ -688,8 +829,9 @@ create_name:
return rc;
disassociate_exit:
- disassociatePortProfileId(cr_ifname,
- virtPortProfile);
+ vpDisassociatePortProfileId(cr_ifname,
+ linkdev,
+ virtPortProfile);
link_del_exit:
link_del(cr_ifname);
@@ -701,6 +843,7 @@ link_del_exit:
/**
* 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
@@ -709,22 +852,564 @@ link_del_exit:
*/
void
delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile)
{
if (ifname) {
- disassociatePortProfileId(ifname,
- virtPortProfile);
+ vpDisassociatePortProfileId(ifname,
+ linkdev,
+ virtPortProfile);
link_del(ifname);
}
}
+# endif /* WITH_MACVTAP */
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(bool nltarget_kernel, int ifindex,
+ struct nlattr **tb, char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ unsigned int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (nltarget_kernel) {
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ 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
+doPortProfileOpSetLink(bool nltarget_kernel,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[RATTBUF_SIZE];
+ struct rtattr *rta, *vfports = NULL, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ unsigned int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta ||
+ !(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* begin nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta ||
+ !(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (nltarget_kernel) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of ifindex %d"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool nltarget_kernel,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = STATUS_POLL_TIMEOUT_USEC / STATUS_POLL_INTERVL_USEC;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(nltarget_kernel,
+ ifindex,
+ 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 = link_dump(nltarget_kernel, ifindex, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ 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 ifindex %d"),
+ status, ifindex);
+ rc = 1;
+ break;
+ }
+ } else
+ goto err_exit;
+
+ 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 = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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 ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ 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 ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(nltarget_kernel, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ PORT_SELF_VF,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+# ifdef IFLA_VF_PORT_MAX
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+ return rc;
+}
+# endif /* IFLA_VF_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool nltarget_kernel = true;
+ int ifindex;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(ifname, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
+ if (ifaceGetIndex(true, physfndev, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ 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"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(nltarget_kernel, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ hostuuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
- * associatePortProfile
+ * vpAssociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
*
* Associate a port on a swtich with a profile. This function
@@ -736,17 +1421,16 @@ delMacvtap(const char *ifname,
* Returns 0 in case of success, != 0 otherwise with error
* having been reported.
*/
-static int
-associatePortProfileId(const char *macvtap_ifname,
- const virVirtualPortProfileParamsPtr virtPort,
- int vf,
- const unsigned char *vmuuid)
+int
+vpAssociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
+ const virVirtualPortProfileParamsPtr virtPort,
+ const unsigned char *vmuuid)
{
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -754,11 +1438,14 @@ associatePortProfileId(const char *macvt
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ vmuuid,
+ ASSOCIATE);
break;
}
@@ -767,19 +1454,22 @@ associatePortProfileId(const char *macvt
/**
- * disassociatePortProfile
+ * vpDisassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @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.
*/
-static int
-disassociatePortProfileId(const char *macvtap_ifname,
- const virVirtualPortProfileParamsPtr virtPort)
+int
+vpDisassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
+ const virVirtualPortProfileParamsPtr virtPort)
{
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
@@ -789,15 +1479,18 @@ disassociatePortProfileId(const char *ma
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
-#endif /* WITH_MACVTAP */
+#endif /* WITH_MACVTAP || WITH_VIRTUALPORT */
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -72,6 +72,7 @@ int openMacvtapTap(const char *ifname,
char **res_ifname);
void delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile);
# endif /* WITH_MACVTAP */
@@ -80,6 +81,14 @@ void delMacvtap(const char *ifname,
# define MACVTAP_MODE_VEPA_STR "vepa"
# define MACVTAP_MODE_BRIDGE_STR "bridge"
+int vpAssociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
+ const virVirtualPortProfileParamsPtr virtPort,
+ const unsigned char *vmuuid);
+
+int vpDisassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
+ const virVirtualPortProfileParamsPtr virtPort);
VIR_ENUM_DECL(virVirtualPort)
2
3
With my recent addition of vnc_allow_host_audio to qemu.conf, the user
facing comment in /etc/sysconfig/libvirtd wasn't accurate. I've pushed
this small fix:
daemon: sysconf: Update comment about VNC audio
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
daemon/libvirtd.sysconf | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/daemon/libvirtd.sysconf b/daemon/libvirtd.sysconf
index 28080a0..b730c5e 100644
--- a/daemon/libvirtd.sysconf
+++ b/daemon/libvirtd.sysconf
@@ -11,7 +11,8 @@
# Override the QEMU/SDL default audio driver probing when
# starting virtual machines using SDL graphics
#
-# NB these have no effect for VMs using VNC
+# NB these have no effect for VMs using VNC, unless vnc_allow_host_audio
+# is enabled in /etc/libvirt/qemu.conf
#QEMU_AUDIO_DRV=sdl
#
#SDL_AUDIODRIVER=pulse
--
1.6.6.1
1
0
[libvirt] [PATCH 0/3 RFC] Demonstrating a new API for spawning processes
by Daniel P. Berrange 26 May '10
by Daniel P. Berrange 26 May '10
26 May '10
We have had great success with our APIs for memory allocation and
string buffer management, removing most of the potential for incorrect
API usage, thus avoiding many common errors. It is time todo the same
for command execution.
This patch series is a proof of concept for a set of APIs I've
been thinking about for many months. They are intended to replace
all current usage of virExec and virRun with a more flexible and
less error prone API. In particular they improve of OOM handling,
prevent more memory leaks & simplify alot of code that used horrible
macros
Patch 1 contains the actual implementation and comprehensive test
suite. Patch 2 contains HTML docs. Patch 3 ports two current methods
to the new APIs to demonstrate the improved code clarity.
What follows is a plain text rendering of the HTML docs from the
second patch describing these APIs.
Spawning processes / commands from libvirt drivers
This page describes the usage of libvirt APIs for spawning processes /
commands from libvirt drivers. All code is required to use these APIs
Problems with standard POSIX APIs
The POSIX specification includes a number of APIs for spawning processes /
commands, but they suffer from a number of flaws
*Â fork+exec: The lowest & most flexible level, but very hard to use
correctly / safely. It is easy to leak file descriptors, have
unexpected signal handler behaviour and not handle edge cases
*Â system: Convenient if you don't care about capturing command output,
but has the serious downside that the command string is interpreted by
the shell. This makes it very dangerous to use, because improperly
validated user input can lead to exploits via shell meta characters.
*Â posix_spawn: A half-way house between simplicity of system() and the
flexibility of fork+exec. It does not allow for a couple of important
features though, such as running a hook between the fork+exec stage,
or closing all open file descriptors.
Due to the problems mentioned with each of these, libvirt driver code must
not use any of the above APIs. Historically libvirt provided a higher
level API known as virExec. This was wrapper around fork+exec, in a
similar style to posix_spawn, but with a few more features.
This wrapper still suffered from a number of problems. Handling command
cleanup via waitpid() is overly complex & error prone for most usage.
Building up the argv[] + env[] string arrays is quite cumbersome and error
prone, particularly wrt memory leak / OOM handling.
The libvirt command execution API
There is now a high level API that provides a safe and flexible way to
spawn commands, which prevents the most common errors & is easy to code
against. This code is provided in the src/util/command.h header which can
be imported using #include "command.h"
Defining commands in libvirt
The first step is to declare what command is to be executed. The command
name can be either a fully qualified path, or a bare command name. In the
latter case it will be resolved wrt the $PATH environment variable.
virCommandPtr cmd = virCommandNew("/usr/bin/dnsmasq");
There is no need to check for allocation failure after virCommandNew. This
will be detected and reported at a later time.
Adding arguments to the command
There are a number of APIs for adding arguments to a command. To add a
direct string arg
virCommandAddArg(cmd, "-strict-order");
If an argument takes an attached value of the form -arg=val, then this can
be done using
virCommandAddArgPair(cmd, "--conf-file", "/etc/dnsmasq.conf");
To add a entire NULL terminated array of arguments in one go
const char *const args[] = {
"--strict-order", "--except-interface",
"lo", "--domain", "localdomain", NULL
};
virCommandAddArgSet(cmd, args);
This latter option can also be used at time of initial construction of the
virCommandPtr object
const char *const args[] = {
"--strict-order", "--except-interface",
"lo", "--domain", "localdomain", NULL
};
virCommandPtr cmd = virCommandNewArgs(cmd, args);
Setting up the environment
By default a command will inherit all environment variables from the
current process. Generally this is not desirable and a customized
environment will be more suitable. Any customization done via the
following APIs will prevent inheritance of any existing environment
variables unless explicitly allowed. The first step is usually to pass
through a small number of variables from the current process.
virCommandAddEnvPassCommon(cmd);
This has now set up a clean environment for the child, passing through
PATH, LD_PRELOAD, LD_LIBRARY_PATH, HOME, USER, LOGNAME and TMPDIR.
Furthermore it will explicitly set LC_ALL=C to avoid unexpected
localization of command output. Further variables can be passed through
from parent explicitly:
virCommandAddEnvPass(cmd, "DISPLAY");
virCommandAddEnvPass(cmd, "XAUTHORITY");
To define an environment variable in the child with an separate key /
value:
virCommandAddEnvPair(cmd, "TERM", "xterm");
If the key/value pair is pre-formatted in the right format, it can be set
directly
virCommandAddEnvString(cmd, "TERM=xterm");
Miscellaneous other options
Normally the spawned command will retain the current process as its
parent. If the current process dies, the child will then (usually) be
terminated too. If this cleanup is not desired, then the command should be
marked as daemonized:
virCommandDaemonize(cmd);
When daemonizing a command, the PID visible from the caller will be that
of the intermediate process, not the actual damonized command. If the PID
of the real command is required then a pidfile can be requested
virCommandSetPidFile(cmd, "/var/run/dnsmasq.pid");
This PID file is guaranteed to be written before the intermediate process
exits.
Reducing command privileges
Normally a command will inherit all privileges of the current process. To
restrict what a command can do, it is possible to request that all its
capabilities are cleared. With this done it will only be able to access
resources for which it has explicit DAC permissions
virCommandClearCaps(cmd);
Managing file handles
To prevent unintended resource leaks to child processes, all open file
handles will be closed in the child, and its stdin/out/err all connected
to /dev/null. It is possible to allow an open file handle to be passed
into the child:
virCommandPreserveFD(cmd, 5);
With this file descriptor 5 in the current process remains open as file
descriptor 5 in the child. For stdin/out/err it is usually neccessary to
map a file handle. To attach file descriptor 7 in the current process to
stdin in the child:
virCommandSetInputFD(cmd, 7);
Equivalently to redirect stdout or stderr in the child, pass in a pointer
to the desired handle
int outfd = open("out.log", "w+");
int errfd = open("err.log", "w+");
virCommandSetOutputFD(cmd, &outfd);
virCommandSetErrorFD(cmd, &errfd);
Alternatively it is possible to request that a pipe be created to fetch
stdout/err in the parent, by initializing the FD to -1.
int outfd = -1;
int errfd = -1
virCommandSetOutputFD(cmd, &outfd);
virCommandSetErrorFD(cmd, &errfd);
Once the command is running, outfd and errfd will be initialized with
valid file handles that can be read from.
Feeding & capturing strings to/from the child
Often dealing with file handles for stdin/out/err is unneccessarily
complex. It is possible to specify a string buffer to act as the data
source for the child's stdin
const char *input = "Hello World\n";
virCommandSetInputBuffer(cmd, input);
Similarly it is possible to request that the child's stdout/err be
redirected into a string buffer
char *output = NULL, *errors = NULL;
virCommandSetOutputBuffer(cmd, &output);
virCommandSetErrorBuffer(cmd, &errors);
Once the command has finished executing, these buffers will contain the
output. It is the callers responsibility to free these buffers.
Running commands synchronously
For most commands, the desired behaviour is to spawn the command, wait for
it to complete & exit and then check that its exit status is zero.
if (virCommandRun(cmd, NULL) < 0)
return -1;
Note: if the command has been daemonized this will only block & wait for
the intermediate process, not the real command. virCommandRun will report
on any errors that have occured upon this point with all previous API
calls. If the command fails to run, or exits with non-zero status an error
will be reported via normal libvirt error infrastructure. If a non-zero
exit status can represent a success condition, it is possible to request
the exit status and perform that check manually instead of letting
virCommandRun raise the error
int status;
if (virCommandRun(cmd, &status) < 0)
return -1;
if (WEXITSTATUS(status) ...) {
...do stuff...
}
Running commands asynchronously
In certain complex scenarios, particularly special I/O handling is
required for the child's stdin/err/out it will be neccessary to run the
command asynchronously and wait for completion separately.
pid_t pid;
if (virCommandRunAsync(cmd, &pid) < 0)
return -1;
... do something while pid is running ...
int status;
if (virCommandWait(cmd, &status) < 0)
return -1;
if (WEXITSTATUS(status)...) {
..do stuff..
}
As with virCommandRun, the status arg for virCommandWait can be omitted,
in which case it will validate that exit status is zero and raise an error
if not.
Releasing resources
Once the command has been executed, or if execution has been abandoned, it
is neccessary to release resources associated with the virCommandPtr
object. This is done with:
virCommandFree(cmd);
There is no need to check if cmd is NULL before calling virCommandFree.
This scenario is handled automatically. If the command is still running,
it will be forcably killed and cleaned up (via waitpid).
Complete examples
This shows a complete example usage of the APIs roughly using the libvirt
source src/util/hooks.c
int runhook(const char *drvstr, const char *id,
const char *opstr, const char *subopstr,
const char *extra) {
int ret;
char *path;
virCommandPtr cmd;
ret = virBuildPath(&path, LIBVIRT_HOOK_DIR, drvstr);
if ((ret < 0) || (path == NULL)) {
virHookReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to build path for %s hook"),
drvstr);
return -1;
}
cmd = virCommandNew(path);
VIR_FREE(path);
virCommandAddEnvPassCommon(cmd);
virCommandAddArg(cmd, id);
virCommandAddArg(cmd, opstr);
virCommandAddArg(cmd, subopstr);
virCommandAddArg(cmd, extra);
virCommandSetInputBuffer(cmd, input);
ret = virCommandRun(cmd, NULL);
virCommandFree(cmd);
return ret;
In this example, the command is being run synchronously. A pre-formatted
string is being fed to the command as its stdin. The command takes four
arguments, and has a minimal set of environment variables passed down. In
this example, the code does not require any error checking. All errors are
reported by the virCommandRun method, and the exit status from this is
returned to the caller to handle as desired.
3
13
A build on Ubuntu reported:
|| Generating formatnwfilter.html.tmp
/dados/develop/libvirt/docs/formatnwfilter.html.in|390| HTML parser error : Unexpected end tag : p
|| </p>
|| ^
/dados/develop/libvirt/docs/formatnwfilter.html.in|705| HTML parser error : Unexpected end tag : code
|| <td>End of range of valid source ports</code></td>
|| ^
/dados/develop/libvirt/docs/formatnwfilter.html.in|710| HTML parser error : Unexpected end tag : code
|| <td>Start of range of valid destination ports</code></td>
|| ^
* docs/formatnwfilter.html.in: Fix invalid HTML constructs.
Reported by Eduardo Otubo.
---
Don't know why a build on Fedora didn't catch these, so I
won't push until I get an ack that this fixes things on Ubuntu.
docs/formatnwfilter.html.in | 11 ++++++-----
1 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/docs/formatnwfilter.html.in b/docs/formatnwfilter.html.in
index f03e9ce..586a4fb 100644
--- a/docs/formatnwfilter.html.in
+++ b/docs/formatnwfilter.html.in
@@ -384,10 +384,11 @@
</table>
<p>
Valid Strings for <code>protocolid</code> are: arp, rarp, ipv4, ipv6
- <br><br>
- Example: <pre><mac match='no' srcmacaddr='$MAC'/></pre>
- <br><br>
</p>
+<pre>
+[...]
+<mac match='no' srcmacaddr='$MAC'/>
+</pre>
<h5><a name="nwfelemsRulesProtoARP">ARP/RARP</a></h5>
<p>
@@ -702,12 +703,12 @@
<tr>
<td>srcportend</td>
<td>UINT16</td>
- <td>End of range of valid source ports</code></td>
+ <td>End of range of valid source ports</td>
</tr>
<tr>
<td>dstportstart</td>
<td>UINT16</td>
- <td>Start of range of valid destination ports</code></td>
+ <td>Start of range of valid destination ports</td>
</tr>
<tr>
<td>dstportend</td>
--
1.7.0.1
3
4
Getting paranoid now... Clear possibly partially parsed UUIDs. Question
is whether to do this in the parsing function itself.
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
src/util/uuid.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
Index: libvirt-acl/src/util/uuid.c
===================================================================
--- libvirt-acl.orig/src/util/uuid.c
+++ libvirt-acl/src/util/uuid.c
@@ -289,14 +289,19 @@ virSetHostUUIDStr(const char *uuid)
if (!getDMISystemUUID(dmiuuid, sizeof(dmiuuid))) {
if (!virUUIDParse(dmiuuid, host_uuid))
return 0;
+ // clear partially parsed UUID
+ memset(host_uuid, 0x0, sizeof(host_uuid));
}
if (!virUUIDIsValid(host_uuid))
return virUUIDGenerate(host_uuid);
} else {
rc = virUUIDParse(uuid, host_uuid);
- if (rc)
+ if (rc) {
+ // clear partially parsed UUID
+ memset(host_uuid, 0x0, sizeof(host_uuid));
return rc;
+ }
if (!virUUIDIsValid(host_uuid))
return EINVAL;
}
2
1
26 May '10
The hotplug methods still had the qemuCmdFlags variable declared
as an int, instead of unsigned long long. This caused flag checks
to be incorrect for flags > 31
* src/qemu/qemu_driver.c: Fix integer overflow in hotplug
---
src/qemu/qemu_driver.c | 16 ++++++++--------
1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 64dbf4a..ad99d59 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7019,7 +7019,7 @@ error:
static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
int i, ret;
const char* type = virDomainDiskBusTypeToString(disk->bus);
@@ -7114,7 +7114,7 @@ error:
static int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainControllerDefPtr controller,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
int i;
int ret = -1;
@@ -7180,7 +7180,7 @@ static virDomainControllerDefPtr
qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver,
virDomainObjPtr vm,
int controller,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
int i;
virDomainControllerDefPtr cont;
@@ -7225,7 +7225,7 @@ qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver,
static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
int i;
qemuDomainObjPrivatePtr priv = vm->privateData;
@@ -7341,7 +7341,7 @@ error:
static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
int i, ret;
@@ -7652,7 +7652,7 @@ no_memory:
static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
pciDevice *pci;
@@ -7764,7 +7764,7 @@ error:
static int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
int ret;
qemuDomainObjPrivatePtr priv = vm->privateData;
@@ -7808,7 +7808,7 @@ error:
static int qemudDomainAttachHostDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev,
- int qemuCmdFlags)
+ unsigned long long qemuCmdFlags)
{
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
qemuReportError(VIR_ERR_NO_SUPPORT,
--
1.6.6.1
3
2
We've been running into a lot of situations where
virGetHostname() is returning "localhost", where a plain
gethostname() would have returned the correct thing. This
is because virGetHostname() is *always* trying to canonicalize
the name returned from gethostname(), even when it doesn't
have to.
This patch changes virGetHostname so that if the value returned
from gethostname() is already FQDN or localhost, it returns
that string directly. If the value returned from gethostname()
is a shortened hostname, then we try to canonicalize it. If
that succeeds, we returned the canonicalized hostname. If
that fails, and/or returns "localhost", then we just return
the original string we got from gethostname() and hope for
the best.
Note that after this patch it is up to clients to check whether
"localhost" is an allowed return value. The only place
where it's currently not is in qemu migration.
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
src/libvirt_private.syms | 1 -
src/qemu/qemu_driver.c | 15 +++++--
src/util/util.c | 94 ++++++++++++++++++++++++---------------------
src/util/util.h | 1 -
4 files changed, 60 insertions(+), 51 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 104278f..130a2a1 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -653,7 +653,6 @@ virExecDaemonize;
virSetCloseExec;
virSetNonBlock;
virFormatMacAddr;
-virGetHostnameLocalhost;
virGetHostname;
virParseMacAddr;
virFileDeletePid;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 0af64c7..cfa5964 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10017,7 +10017,7 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
virDomainDefPtr def = NULL;
virDomainObjPtr vm = NULL;
int this_port;
- char *hostname;
+ char *hostname = NULL;
char migrateFrom [64];
const char *p;
virDomainEventPtr event = NULL;
@@ -10057,9 +10057,15 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0;
/* Get hostname */
- if ((hostname = virGetHostnameLocalhost(0)) == NULL)
+ if ((hostname = virGetHostname(NULL)) == NULL)
goto cleanup;
+ if (STRPREFIX(hostname, "localhost")) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("hostname on destination resolved to localhost, but migration requires an FQDN"));
+ goto cleanup;
+ }
+
/* XXX this really should have been a properly well-formed
* URI, but we can't add in tcp:// now without breaking
* compatability with old targets. We at least make the
@@ -10067,7 +10073,6 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
*/
/* Caller frees */
internalret = virAsprintf(uri_out, "tcp:%s:%d", hostname, this_port);
- VIR_FREE(hostname);
if (internalret < 0) {
virReportOOMError();
goto cleanup;
@@ -10171,10 +10176,10 @@ endjob:
vm = NULL;
cleanup:
+ VIR_FREE(hostname);
virDomainDefFree(def);
- if (ret != 0) {
+ if (ret != 0)
VIR_FREE(*uri_out);
- }
if (vm)
virDomainObjUnlock(vm);
if (event)
diff --git a/src/util/util.c b/src/util/util.c
index e937d39..930bfac 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -2367,11 +2367,31 @@ char *virIndexToDiskName(int idx, const char *prefix)
# define AI_CANONIDN 0
#endif
-char *virGetHostnameLocalhost(int allow_localhost)
+/* Who knew getting a hostname could be so delicate. In Linux (and Unices
+ * in general), many things depend on "hostname" returning a value that will
+ * resolve one way or another. In the modern world where networks frequently
+ * come and go this is often being hard-coded to resolve to "localhost". If
+ * it *doesn't* resolve to localhost, then we would prefer to have the FQDN.
+ * That leads us to 3 possibilities:
+ *
+ * 1) gethostname() returns an FQDN (not localhost) - we return the string
+ * as-is, it's all of the information we want
+ * 2) gethostname() returns "localhost" - we return localhost; doing further
+ * work to try to resolve it is pointless
+ * 3) gethostname() returns a shortened hostname - in this case, we want to
+ * try to resolve this to a fully-qualified name. Therefore we pass it
+ * to getaddrinfo(). There are two possible responses:
+ * a) getaddrinfo() resolves to a FQDN - return the FQDN
+ * b) getaddrinfo() resolves to localhost - in this case, the data we got
+ * from gethostname() is actually more useful than what we got from
+ * getaddrinfo(). Return the value from gethostname() and hope for
+ * the best.
+ */
+char *virGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
{
int r;
char hostname[HOST_NAME_MAX+1], *result;
- struct addrinfo hints, *info, *res;
+ struct addrinfo hints, *info;
r = gethostname (hostname, sizeof(hostname));
if (r == -1) {
@@ -2381,6 +2401,21 @@ char *virGetHostnameLocalhost(int allow_localhost)
}
NUL_TERMINATE(hostname);
+ if (STRPREFIX(hostname, "localhost") || strchr(hostname, '.')) {
+ /* in this case, gethostname returned localhost (meaning we can't
+ * do any further canonicalization), or it returned an FQDN (and
+ * we don't need to do any further canonicalization). Return the
+ * string as-is; it's up to callers to check whether "localhost"
+ * is allowed.
+ */
+ result = strdup(hostname);
+ goto check_and_return;
+ }
+
+ /* otherwise, it's a shortened, non-localhost, hostname. Attempt to
+ * canonicalize the hostname by running it through getaddrinfo
+ */
+
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME|AI_CANONIDN;
hints.ai_family = AF_UNSPEC;
@@ -2395,54 +2430,25 @@ char *virGetHostnameLocalhost(int allow_localhost)
/* Tell static analyzers about getaddrinfo semantics. */
sa_assert (info);
- /* if we aren't allowing localhost, then we iterate through the
- * list and make sure none of the IPv4 addresses are 127.0.0.1 and
- * that none of the IPv6 addresses are ::1
- */
- if (!allow_localhost) {
- res = info;
- while (res) {
- if (res->ai_family == AF_INET) {
- if (htonl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr) == INADDR_LOOPBACK) {
- virUtilError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("canonical hostname pointed to localhost, but this is not allowed"));
- freeaddrinfo(info);
- return NULL;
- }
- }
- else if (res->ai_family == AF_INET6) {
- if (IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {
- virUtilError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("canonical hostname pointed to localhost, but this is not allowed"));
- freeaddrinfo(info);
- return NULL;
- }
- }
- res = res->ai_next;
- }
- }
+ if (info->ai_canonname == NULL ||
+ STRPREFIX(info->ai_canonname, "localhost"))
+ /* in this case, we tried to canonicalize and we ended up back with
+ * localhost. Ignore the canonicalized name and just return the
+ * original hostname
+ */
+ result = strdup(hostname);
+ else
+ /* Caller frees this string. */
+ result = strdup (info->ai_canonname);
- if (info->ai_canonname == NULL) {
- virUtilError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("could not determine canonical host name"));
- freeaddrinfo(info);
- return NULL;
- }
+ freeaddrinfo(info);
- /* Caller frees this string. */
- result = strdup (info->ai_canonname);
- if (!result)
+check_and_return:
+ if (result == NULL)
virReportOOMError();
-
- freeaddrinfo(info);
return result;
}
-char *virGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
-{
- return virGetHostnameLocalhost(1);
-}
-
/* send signal to a single process */
int virKillProcess(pid_t pid, int sig)
{
diff --git a/src/util/util.h b/src/util/util.h
index 6bf6bcc..f8b64c2 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -252,7 +252,6 @@ static inline int getuid (void) { return 0; }
static inline int getgid (void) { return 0; }
# endif
-char *virGetHostnameLocalhost(int allow_localhost);
char *virGetHostname(virConnectPtr conn);
int virKillProcess(pid_t pid, int sig);
--
1.6.6.1
5
18
Basic live migration was broken by the commit that added
non-shared block support in two ways:
1) It added a virCheckFlags() to doNativeMigrate(). Besides
the fact that typical usage of virCheckFlags() is in driver
entry points, and doNativeMigrate() is not an entry point,
it was missing important flags like VIR_MIGRATE_LIVE. Move
the virCheckFlags to the top-level qemuDomainMigratePrepare2
and friends.
2) It also added a memory leak in qemuMonitorTextMigrate()
by not freeing the memory used by virBufferContentAndReset().
This is fixed by storing the pointer in a temporary variable
and freeing it at the end.
With this patch in place, normal live migration works again.
v3: Instead of the churn for virCheckFlagsUI and UL, instead
always promote flags to an unsigned long and always use %lx
for the fprintf.
v2: Add back flags check, which required adding virCheckFlagsUI
and virCheckFlagsUL
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
src/internal.h | 7 ++++---
src/qemu/qemu_driver.c | 30 +++++++++++++++++++++++++++---
src/qemu/qemu_monitor_text.c | 10 +++++++---
3 files changed, 38 insertions(+), 9 deletions(-)
diff --git a/src/internal.h b/src/internal.h
index c3c5311..fab3e11 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -212,15 +212,16 @@
*/
# define virCheckFlags(supported, retval) \
do { \
- if ((flags & ~(supported))) { \
+ unsigned long __unsuppflags = flags & ~(supported); \
+ if (__unsuppflags) { \
virReportErrorHelper(NULL, \
VIR_FROM_THIS, \
VIR_ERR_INVALID_ARG, \
__FILE__, \
__FUNCTION__, \
__LINE__, \
- _("%s: unsupported flags (0x%x)"), \
- __FUNCTION__, flags & ~(supported)); \
+ _("%s: unsupported flags (0x%lx)"), \
+ __FUNCTION__, __unsuppflags); \
return retval; \
} \
} while (0)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 77e71cc..941b482 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10175,6 +10175,15 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
int ret = -1;
int internalret;
+ virCheckFlags(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
*uri_out = NULL;
qemuDriverLock(driver);
@@ -10356,9 +10365,6 @@ static int doNativeMigrate(struct qemud_driver *driver,
qemuDomainObjPrivatePtr priv = vm->privateData;
unsigned int background_flags = 0;
- virCheckFlags(VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC,
- -1);
-
/* Issue the migrate command. */
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) {
/* HACK: source host generates bogus URIs, so fix them up */
@@ -10779,6 +10785,15 @@ qemudDomainMigratePerform (virDomainPtr dom,
int resume = 0;
qemuDomainObjPrivatePtr priv;
+ virCheckFlags(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
if (!vm) {
@@ -10882,6 +10897,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
virErrorPtr orig_err;
int newVM = 1;
+ virCheckFlags(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, NULL);
+
/* Migration failed. Save the current error so nothing squashes it */
orig_err = virSaveLastError();
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 5511550..4b1e2ec 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1144,6 +1144,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
int ret = -1;
char *safedest = qemuMonitorEscapeArg(dest);
virBuffer extra = VIR_BUFFER_INITIALIZER;
+ char *extrastr = NULL;
if (!safedest) {
virReportOOMError();
@@ -1159,10 +1160,12 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
if (virBufferError(&extra)) {
virBufferFreeAndReset(&extra);
virReportOOMError();
- free(safedest);
- return -1;
+ goto cleanup;
}
- if (virAsprintf(&cmd, "migrate %s\"%s\"", virBufferContentAndReset(&extra), safedest) < 0) {
+
+ extrastr = virBufferContentAndReset(&extra);
+ if (virAsprintf(&cmd, "migrate %s\"%s\"", extrastr ? extrastr : "",
+ safedest) < 0) {
virReportOOMError();
goto cleanup;
}
@@ -1191,6 +1194,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
ret = 0;
cleanup:
+ VIR_FREE(extrastr);
VIR_FREE(safedest);
VIR_FREE(info);
VIR_FREE(cmd);
--
1.6.6.1
3
4
Didn't have time to fix this today; virVirtualPortProfileFormat is
conditionally implemented based on PROXY, but unconditionally called.
Throwing it out there in case someone else wants to beat me to the fix.
cc1: warnings being treated as errors
../src/conf/domain_conf.c: In function 'virDomainNetDefFormat':
../src/conf/domain_conf.c:5343: error: implicit declaration of function
'virVirtualPortProfileFormat' [-Wimplicit-function-declaration]
../src/conf/domain_conf.c:5343: error: nested extern declaration of
'virVirtualPortProfileFormat' [-Wnested-externs]
--
Eric Blake eblake(a)redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
3
3
Allows listing existing pools and requesting information about them.
Alter the esxVI_ProductVersion enum in a way that allows to check for
product type by masking.
Changes in v2:
- split not directly related parts into separate patches
- simplify goto usage: don't jump backwards
- rework format of esxVI_ProductVersion values and explain the format
- expand a FIXME in esxStoragePoolGetXMLDesc
---
po/POTFILES.in | 1 +
src/esx/esx_storage_driver.c | 597 +++++++++++++++++++++++++++++++++++++++-
src/esx/esx_vi.c | 126 ++++++++-
src/esx/esx_vi.h | 33 ++-
src/esx/esx_vi_generator.input | 59 ++++
src/esx/esx_vi_generator.py | 7 +-
6 files changed, 794 insertions(+), 29 deletions(-)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 88218bd..e047b1b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,7 @@ src/cpu/cpu_map.c
src/cpu/cpu_x86.c
src/datatypes.c
src/esx/esx_driver.c
+src/esx/esx_storage_driver.c
src/esx/esx_util.c
src/esx/esx_vi.c
src/esx/esx_vi_methods.c
diff --git a/src/esx/esx_storage_driver.c b/src/esx/esx_storage_driver.c
index 97b92a5..0e2e1a3 100644
--- a/src/esx/esx_storage_driver.c
+++ b/src/esx/esx_storage_driver.c
@@ -29,6 +29,7 @@
#include "memory.h"
#include "logging.h"
#include "uuid.h"
+#include "storage_conf.h"
#include "esx_private.h"
#include "esx_storage_driver.h"
#include "esx_vi.h"
@@ -65,17 +66,587 @@ esxStorageClose(virConnectPtr conn)
+static int
+esxNumberOfStoragePools(virConnectPtr conn)
+{
+ int count = 0;
+ esxPrivate *priv = conn->storagePrivateData;
+ esxVI_ObjectContent *datastoreList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return -1;
+ }
+
+ if (esxVI_LookupObjectContentByType(priv->host, priv->host->datacenter,
+ "Datastore", NULL, esxVI_Boolean_True,
+ &datastoreList) < 0) {
+ return -1;
+ }
+
+ for (datastore = datastoreList; datastore != NULL;
+ datastore = datastore->_next) {
+ ++count;
+ }
+
+ esxVI_ObjectContent_Free(&datastoreList);
+
+ return count;
+}
+
+
+
+static int
+esxListStoragePools(virConnectPtr conn, char **const names, int maxnames)
+{
+ bool success = false;
+ esxPrivate *priv = conn->storagePrivateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_DynamicProperty *dynamicProperty = NULL;
+ esxVI_ObjectContent *datastoreList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+ int count = 0;
+ int i;
+
+ if (names == NULL || maxnames < 0) {
+ ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ if (maxnames == 0) {
+ return 0;
+ }
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return -1;
+ }
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "summary.name") < 0 ||
+ esxVI_LookupObjectContentByType(priv->host, priv->host->datacenter,
+ "Datastore", propertyNameList,
+ esxVI_Boolean_True,
+ &datastoreList) < 0) {
+ goto cleanup;
+ }
+
+ for (datastore = datastoreList; datastore != NULL;
+ datastore = datastore->_next) {
+ for (dynamicProperty = datastore->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, "summary.name")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_String) < 0) {
+ goto cleanup;
+ }
+
+ names[count] = strdup(dynamicProperty->val->string);
+
+ if (names[count] == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ++count;
+ break;
+ } else {
+ VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
+ }
+ }
+ }
+
+ success = true;
+
+ cleanup:
+ if (! success) {
+ for (i = 0; i < count; ++i) {
+ VIR_FREE(names[i]);
+ }
+
+ count = -1;
+ }
+
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&datastoreList);
+
+ return count;
+}
+
+
+
+static int
+esxNumberOfDefinedStoragePools(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+ /* ESX storage pools are always active */
+ return 0;
+}
+
+
+
+static int
+esxListDefinedStoragePools(virConnectPtr conn ATTRIBUTE_UNUSED,
+ char **const names ATTRIBUTE_UNUSED,
+ int maxnames ATTRIBUTE_UNUSED)
+{
+ /* ESX storage pools are always active */
+ return 0;
+}
+
+
+
+static virStoragePoolPtr
+esxStoragePoolLookupByName(virConnectPtr conn, const char *name)
+{
+ esxPrivate *priv = conn->storagePrivateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+ esxVI_Boolean accessible = esxVI_Boolean_Undefined;
+ char *summaryUrl = NULL;
+ char *suffix = NULL;
+ int suffixLength;
+ char uuid_string[VIR_UUID_STRING_BUFLEN] = "00000000-00000000-0000-000000000000";
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ char *realName = NULL;
+ virStoragePoolPtr pool = NULL;
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return NULL;
+ }
+
+ if (esxVI_String_AppendValueListToList(&propertyNameList,
+ "summary.accessible\0"
+ "summary.name\0"
+ "summary.url\0") < 0 ||
+ esxVI_LookupDatastoreByName(priv->host, name,
+ propertyNameList, &datastore,
+ esxVI_Occurrence_RequiredItem) < 0 ||
+ esxVI_GetBoolean(datastore, "summary.accessible",
+ &accessible, esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * Datastores don't have a UUID. We can use the 'summary.url' property as
+ * source for a "UUID" on ESX, because the property value has this format:
+ *
+ * summary.url = /vmfs/volumes/4b0beca7-7fd401f3-1d7f-000ae484a6a3
+ * summary.url = /vmfs/volumes/b24b7a78-9d82b4f5 (short format)
+ *
+ * The 'summary.url' property comes in two forms, with a complete "UUID"
+ * and a short "UUID".
+ *
+ * But this trailing "UUID" is not guaranteed to be there. On the other
+ * hand we already rely on another implementation detail of the ESX server:
+ * The object name of virtual machine contains an integer, we use that as
+ * domain ID.
+ *
+ * The 'summary.url' property of an inaccessible datastore is invalid.
+ */
+ if (accessible == esxVI_Boolean_True &&
+ priv->host->productVersion & esxVI_ProductVersion_ESX) {
+ if (esxVI_GetStringValue(datastore, "summary.url", &summaryUrl,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ if ((suffix = STRSKIP(summaryUrl, "/vmfs/volumes/")) == NULL) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Datastore URL '%s' has unexpected prefix, "
+ "expecting '/vmfs/volumes/' prefix"), summaryUrl);
+ goto cleanup;
+ }
+
+ suffixLength = strlen(suffix);
+
+ if ((suffixLength == 35 && /* = strlen("4b0beca7-7fd401f3-1d7f-000ae484a6a3") */
+ suffix[8] == '-' && suffix[17] == '-' && suffix[22] == '-') ||
+ (suffixLength == 17 && /* = strlen("b24b7a78-9d82b4f5") */
+ suffix[8] == '-')) {
+ /*
+ * Intentionally use memcpy here, because we want to be able to
+ * replace a prefix of the initial Zero-UUID. virStrncpy would
+ * null-terminate the string in an unwanted place.
+ */
+ memcpy(uuid_string, suffix, suffixLength);
+ } else {
+ VIR_WARN("Datastore URL suffix '%s' has unexpected format, "
+ "cannot deduce a UUID from it", suffix);
+ }
+ }
+
+ if (virUUIDParse(uuid_string, uuid) < 0) {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Could not parse UUID from string '%s'"),
+ uuid_string);
+ goto cleanup;
+ }
+
+ if (esxVI_GetStringValue(datastore, "summary.name", &realName,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ pool = virGetStoragePool(conn, realName, uuid);
+
+ cleanup:
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&datastore);
+
+ return pool;
+}
+
+
+
+static virStoragePoolPtr
+esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
+{
+ esxPrivate *priv = conn->storagePrivateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+ char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
+ char *name = NULL;
+ virStoragePoolPtr pool = NULL;
+
+ if (! (priv->host->productVersion & esxVI_ProductVersion_ESX)) {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Lookup by UUID is supported on ESX only"));
+ return NULL;
+ }
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return NULL;
+ }
+
+ /*
+ * Convert from UUID to datastore URL form by stripping the second '-':
+ *
+ * <---- 14 ----><-------- 22 --------> <---- 13 ---><-------- 22 -------->
+ * 4b0beca7-7fd4-01f3-1d7f-000ae484a6a3 -> 4b0beca7-7fd401f3-1d7f-000ae484a6a3
+ */
+ virUUIDFormat(uuid, uuid_string);
+ memmove(uuid_string + 13, uuid_string + 14, 22 + 1);
+
+ /*
+ * Use esxVI_LookupDatastoreByName because it also does try to match "UUID"
+ * part of the 'summary.url' property if there is no name match.
+ */
+ if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 ||
+ esxVI_LookupDatastoreByName(priv->host, uuid_string,
+ propertyNameList, &datastore,
+ esxVI_Occurrence_OptionalItem) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * If the first try didn't succeed and the trailing 16 digits are zero then
+ * the "UUID" could be a short one. Strip the 16 zeros and try again:
+ *
+ * <------ 17 -----> <------ 17 ----->
+ * b24b7a78-9d82b4f5-0000-000000000000 -> b24b7a78-9d82b4f5
+ */
+ if (datastore == NULL && STREQ(uuid_string + 17, "-0000-000000000000")) {
+ uuid_string[17] = '\0';
+
+ if (esxVI_LookupDatastoreByName(priv->host, uuid_string,
+ propertyNameList, &datastore,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (datastore == NULL) {
+ virUUIDFormat(uuid, uuid_string);
+
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Could not find datastore with UUID '%s'"),
+ uuid_string);
+
+ goto cleanup;
+ }
+
+ if (esxVI_GetStringValue(datastore, "summary.name", &name,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ pool = virGetStoragePool(conn, name, uuid);
+
+ cleanup:
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&datastore);
+
+ return pool;
+}
+
+
+
+static int
+esxStoragePoolRefresh(virStoragePoolPtr pool, unsigned int flags)
+{
+ int result = -1;
+ esxPrivate *priv = pool->conn->storagePrivateData;
+ esxVI_ObjectContent *datastore = NULL;
+
+ virCheckFlags(0, -1);
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return -1;
+ }
+
+ if (esxVI_LookupDatastoreByName(priv->host, pool->name, NULL, &datastore,
+ esxVI_Occurrence_RequiredItem) < 0 ||
+ esxVI_RefreshDatastore(priv->host, datastore->obj) < 0) {
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ esxVI_ObjectContent_Free(&datastore);
+
+ return result;
+}
+
+
+
+static int
+esxStoragePoolGetInfo(virStoragePoolPtr pool, virStoragePoolInfoPtr info)
+{
+ int result = -1;
+ esxPrivate *priv = pool->conn->storagePrivateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+ esxVI_DynamicProperty *dynamicProperty = NULL;
+ esxVI_Boolean accessible = esxVI_Boolean_Undefined;
+
+ memset(info, 0, sizeof (*info));
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return -1;
+ }
+
+ if (esxVI_String_AppendValueListToList(&propertyNameList,
+ "summary.accessible\0"
+ "summary.capacity\0"
+ "summary.freeSpace\0") < 0 ||
+ esxVI_LookupDatastoreByName(priv->host, pool->name,
+ propertyNameList, &datastore,
+ esxVI_Occurrence_RequiredItem) < 0 ||
+ esxVI_GetBoolean(datastore, "summary.accessible",
+ &accessible, esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ if (accessible == esxVI_Boolean_True) {
+ info->state = VIR_STORAGE_POOL_RUNNING;
+
+ for (dynamicProperty = datastore->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, "summary.capacity")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_Long) < 0) {
+ goto cleanup;
+ }
+
+ info->capacity = dynamicProperty->val->int64;
+ } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_Long) < 0) {
+ goto cleanup;
+ }
+
+ info->available = dynamicProperty->val->int64;
+ }
+ }
+
+ info->allocation = info->capacity - info->available;
+ } else {
+ info->state = VIR_STORAGE_POOL_INACCESSIBLE;
+ }
+
+ result = 0;
+
+ cleanup:
+ if (result < 0) {
+ memset(info, 0, sizeof (*info));
+ }
+
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&datastore);
+
+ return result;
+}
+
+
+
+static char *
+esxStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags)
+{
+ esxPrivate *priv = pool->conn->storagePrivateData;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *datastore = NULL;
+ esxVI_DynamicProperty *dynamicProperty = NULL;
+ esxVI_Boolean accessible = esxVI_Boolean_Undefined;
+ virStoragePoolDef def;
+ esxVI_DatastoreInfo *info = NULL;
+ esxVI_LocalDatastoreInfo *localInfo = NULL;
+ esxVI_NasDatastoreInfo *nasInfo = NULL;
+ esxVI_VmfsDatastoreInfo *vmfsInfo = NULL;
+ char *xml = NULL;
+
+ virCheckFlags(0, NULL);
+
+ memset(&def, 0, sizeof (def));
+
+ if (esxVI_EnsureSession(priv->host) < 0) {
+ return NULL;
+ }
+
+ if (esxVI_String_AppendValueListToList(&propertyNameList,
+ "summary.accessible\0"
+ "summary.capacity\0"
+ "summary.freeSpace\0"
+ "info\0") < 0 ||
+ esxVI_LookupDatastoreByName(priv->host, pool->name,
+ propertyNameList, &datastore,
+ esxVI_Occurrence_RequiredItem) < 0 ||
+ esxVI_GetBoolean(datastore, "summary.accessible",
+ &accessible, esxVI_Occurrence_RequiredItem) < 0) {
+ goto cleanup;
+ }
+
+ def.name = pool->name;
+ memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN);
+
+ if (accessible == esxVI_Boolean_True) {
+ for (dynamicProperty = datastore->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, "summary.capacity")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_Long) < 0) {
+ goto cleanup;
+ }
+
+ def.capacity = dynamicProperty->val->int64;
+ } else if (STREQ(dynamicProperty->name, "summary.freeSpace")) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_Long) < 0) {
+ goto cleanup;
+ }
+
+ def.available = dynamicProperty->val->int64;
+ } else if (STREQ(dynamicProperty->name, "info")) {
+ if (esxVI_DatastoreInfo_CastFromAnyType(dynamicProperty->val,
+ &info) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+
+ def.allocation = def.capacity - def.available;
+
+ /* See vSphere API documentation about HostDatastoreSystem for details */
+ if ((localInfo = esxVI_LocalDatastoreInfo_DynamicCast(info)) != NULL) {
+ def.type = VIR_STORAGE_POOL_DIR;
+ def.target.path = localInfo->path;
+ } else if ((nasInfo = esxVI_NasDatastoreInfo_DynamicCast(info)) != NULL) {
+ def.type = VIR_STORAGE_POOL_NETFS;
+ def.source.host.name = nasInfo->nas->remoteHost;
+ def.source.dir = nasInfo->nas->remotePath;
+
+ if (STRCASEEQ(nasInfo->nas->type, "NFS")) {
+ def.source.format = VIR_STORAGE_POOL_NETFS_NFS;
+ } else if (STRCASEEQ(nasInfo->nas->type, "CIFS")) {
+ def.source.format = VIR_STORAGE_POOL_NETFS_CIFS;
+ } else {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Datastore has unexpected type '%s'"),
+ nasInfo->nas->type);
+ goto cleanup;
+ }
+ } else if ((vmfsInfo = esxVI_VmfsDatastoreInfo_DynamicCast(info)) != NULL) {
+ def.type = VIR_STORAGE_POOL_FS;
+ /*
+ * FIXME: I'm not sure how to represent the source and target of a
+ * VMFS based datastore in libvirt terms
+ */
+ } else {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("DatastoreInfo has unexpected type"));
+ goto cleanup;
+ }
+ }
+
+ xml = virStoragePoolDefFormat(&def);
+
+ cleanup:
+ esxVI_String_Free(&propertyNameList);
+ esxVI_ObjectContent_Free(&datastore);
+ esxVI_DatastoreInfo_Free(&info);
+
+ return xml;
+}
+
+
+
+static int
+esxStoragePoolGetAutostart(virStoragePoolPtr pool ATTRIBUTE_UNUSED,
+ int *autostart)
+{
+ /* ESX storage pools are always active */
+ *autostart = 1;
+
+ return 0;
+}
+
+
+
+static int
+esxStoragePoolSetAutostart(virStoragePoolPtr pool ATTRIBUTE_UNUSED,
+ int autostart)
+{
+ /* Just accept autostart activation, but fail on autostart deactivation */
+ autostart = (autostart != 0);
+
+ if (! autostart) {
+ ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot deactivate storage pool autostart"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static int
+esxStoragePoolIsActive(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
+{
+ /* ESX storage pools are always active */
+ return 1;
+}
+
+
+
+static int
+esxStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED)
+{
+ /* ESX has no concept of transient pools, so all of them are persistent */
+ return 1;
+}
+
+
static virStorageDriver esxStorageDriver = {
"ESX", /* name */
esxStorageOpen, /* open */
esxStorageClose, /* close */
- NULL, /* numOfPools */
- NULL, /* listPools */
- NULL, /* numOfDefinedPools */
- NULL, /* listDefinedPools */
+ esxNumberOfStoragePools, /* numOfPools */
+ esxListStoragePools, /* listPools */
+ esxNumberOfDefinedStoragePools, /* numOfDefinedPools */
+ esxListDefinedStoragePools, /* listDefinedPools */
NULL, /* findPoolSources */
- NULL, /* poolLookupByName */
- NULL, /* poolLookupByUUID */
+ esxStoragePoolLookupByName, /* poolLookupByName */
+ esxStoragePoolLookupByUUID, /* poolLookupByUUID */
NULL, /* poolLookupByVolume */
NULL, /* poolCreateXML */
NULL, /* poolDefineXML */
@@ -84,11 +655,11 @@ static virStorageDriver esxStorageDriver = {
NULL, /* poolCreate */
NULL, /* poolDestroy */
NULL, /* poolDelete */
- NULL, /* poolRefresh */
- NULL, /* poolGetInfo */
- NULL, /* poolGetXMLDesc */
- NULL, /* poolGetAutostart */
- NULL, /* poolSetAutostart */
+ esxStoragePoolRefresh, /* poolRefresh */
+ esxStoragePoolGetInfo, /* poolGetInfo */
+ esxStoragePoolGetXMLDesc, /* poolGetXMLDesc */
+ esxStoragePoolGetAutostart, /* poolGetAutostart */
+ esxStoragePoolSetAutostart, /* poolSetAutostart */
NULL, /* poolNumOfVolumes */
NULL, /* poolListVolumes */
NULL, /* volLookupByName */
@@ -101,8 +672,8 @@ static virStorageDriver esxStorageDriver = {
NULL, /* volGetInfo */
NULL, /* volGetXMLDesc */
NULL, /* volGetPath */
- NULL, /* poolIsActive */
- NULL, /* poolIsPersistent */
+ esxStoragePoolIsActive, /* poolIsActive */
+ esxStoragePoolIsPersistent, /* poolIsPersistent */
};
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c
index 966ef85..100b9d2 100644
--- a/src/esx/esx_vi.c
+++ b/src/esx/esx_vi.c
@@ -1530,6 +1530,114 @@ esxVI_GetVirtualMachineQuestionInfo
int
+esxVI_GetBoolean(esxVI_ObjectContent *objectContent, const char *propertyName,
+ esxVI_Boolean *value, esxVI_Occurrence occurence)
+{
+ esxVI_DynamicProperty *dynamicProperty;
+
+ if (value == NULL || *value != esxVI_Boolean_Undefined) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ for (dynamicProperty = objectContent->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, propertyName)) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_Boolean) < 0) {
+ return -1;
+ }
+
+ *value = dynamicProperty->val->boolean;
+ break;
+ }
+ }
+
+ if (*value == esxVI_Boolean_Undefined &&
+ occurence == esxVI_Occurrence_RequiredItem) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Missing '%s' property"), propertyName);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+int
+esxVI_GetStringValue(esxVI_ObjectContent *objectContent,
+ const char *propertyName,
+ char **value, esxVI_Occurrence occurence)
+{
+ esxVI_DynamicProperty *dynamicProperty;
+
+ if (value == NULL || *value != NULL) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ for (dynamicProperty = objectContent->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, propertyName)) {
+ if (esxVI_AnyType_ExpectType(dynamicProperty->val,
+ esxVI_Type_String) < 0) {
+ return -1;
+ }
+
+ *value = dynamicProperty->val->string;
+ break;
+ }
+ }
+
+ if (*value == NULL && occurence == esxVI_Occurrence_RequiredItem) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Missing '%s' property"), propertyName);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+int
+esxVI_GetManagedObjectReference(esxVI_ObjectContent *objectContent,
+ const char *propertyName,
+ esxVI_ManagedObjectReference **value,
+ esxVI_Occurrence occurence)
+{
+ esxVI_DynamicProperty *dynamicProperty;
+
+ if (value == NULL || *value != NULL) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ for (dynamicProperty = objectContent->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, propertyName)) {
+ if (esxVI_ManagedObjectReference_CastFromAnyType
+ (dynamicProperty->val, value) < 0) {
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ if (*value == NULL && occurence == esxVI_Occurrence_RequiredItem) {
+ ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+ _("Missing '%s' property"), propertyName);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+int
esxVI_LookupNumberOfDomainsByPowerState(esxVI_Context *ctx,
esxVI_VirtualMachinePowerState powerState,
esxVI_Boolean inverse)
@@ -2161,7 +2269,7 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
esxVI_ObjectContent *candidate = NULL;
esxVI_DynamicProperty *dynamicProperty = NULL;
esxVI_Boolean accessible = esxVI_Boolean_Undefined;
- size_t offset = strlen("/vmfs/volumes/");
+ size_t offset = 14; /* = strlen("/vmfs/volumes/") */
int numInaccessibleDatastores = 0;
if (datastore == NULL || *datastore != NULL) {
@@ -2227,9 +2335,7 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
- if (STREQ(dynamicProperty->name, "summary.accessible")) {
- /* Ignore it */
- } else if (STREQ(dynamicProperty->name, "summary.name")) {
+ if (STREQ(dynamicProperty->name, "summary.name")) {
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
esxVI_Type_String) < 0) {
goto failure;
@@ -2244,7 +2350,8 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
/* Found datastore with matching name */
goto cleanup;
}
- } else if (STREQ(dynamicProperty->name, "summary.url")) {
+ } else if (STREQ(dynamicProperty->name, "summary.url") &&
+ ctx->productVersion & esxVI_ProductVersion_ESX) {
if (accessible == esxVI_Boolean_False) {
/*
* The 'summary.url' property of an inaccessible datastore
@@ -2276,8 +2383,6 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
/* Found datastore with matching URL suffix */
goto cleanup;
}
- } else {
- VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
}
}
}
@@ -2309,9 +2414,10 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
-int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
- esxVI_ManagedObjectReference *task,
- esxVI_TaskInfo **taskInfo)
+int
+esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
+ esxVI_ManagedObjectReference *task,
+ esxVI_TaskInfo **taskInfo)
{
int result = 0;
esxVI_String *propertyNameList = NULL;
diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h
index d581a59..e2687c4 100644
--- a/src/esx/esx_vi.h
+++ b/src/esx/esx_vi.h
@@ -96,13 +96,23 @@ enum _esxVI_APIVersion {
esxVI_APIVersion_40
};
+/*
+ * AAAABBBB: where AAAA0000 is the product and BBBB the version. this format
+ * allows simple bitmask testing for a product independent of the version
+ */
enum _esxVI_ProductVersion {
esxVI_ProductVersion_Undefined = 0,
- esxVI_ProductVersion_GSX20,
- esxVI_ProductVersion_ESX35,
- esxVI_ProductVersion_ESX40,
- esxVI_ProductVersion_VPX25,
- esxVI_ProductVersion_VPX40
+
+ esxVI_ProductVersion_GSX = (1 << 0) << 16,
+ esxVI_ProductVersion_GSX20 = esxVI_ProductVersion_GSX | 1,
+
+ esxVI_ProductVersion_ESX = (1 << 1) << 16,
+ esxVI_ProductVersion_ESX35 = esxVI_ProductVersion_ESX | 1,
+ esxVI_ProductVersion_ESX40 = esxVI_ProductVersion_ESX | 2,
+
+ esxVI_ProductVersion_VPX = (1 << 2) << 16,
+ esxVI_ProductVersion_VPX25 = esxVI_ProductVersion_VPX | 1,
+ esxVI_ProductVersion_VPX40 = esxVI_ProductVersion_VPX | 2
};
enum _esxVI_Occurrence {
@@ -272,6 +282,19 @@ int esxVI_GetVirtualMachineQuestionInfo
(esxVI_ObjectContent *virtualMachine,
esxVI_VirtualMachineQuestionInfo **questionInfo);
+int esxVI_GetBoolean(esxVI_ObjectContent *objectContent,
+ const char *propertyName,
+ esxVI_Boolean *value, esxVI_Occurrence occurence);
+
+int esxVI_GetStringValue(esxVI_ObjectContent *objectContent,
+ const char *propertyName,
+ char **value, esxVI_Occurrence occurence);
+
+int esxVI_GetManagedObjectReference(esxVI_ObjectContent *objectContent,
+ const char *propertyName,
+ esxVI_ManagedObjectReference **value,
+ esxVI_Occurrence occurence);
+
int esxVI_LookupNumberOfDomainsByPowerState
(esxVI_Context *ctx, esxVI_VirtualMachinePowerState powerState,
esxVI_Boolean inverse);
diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input
index 5e5e6ba..ff65178 100644
--- a/src/esx/esx_vi_generator.input
+++ b/src/esx/esx_vi_generator.input
@@ -146,6 +146,14 @@ object ChoiceOption extends OptionType
end
+object DatastoreInfo
+ String name r
+ String url r
+ Long freeSpace r
+ Long maxFileSize r
+end
+
+
object Description
String label r
String summary r
@@ -186,6 +194,47 @@ object HostCpuIdInfo
end
+object HostFileSystemVolume
+ String type r
+ String name r
+ Long capacity r
+end
+
+
+object HostNasVolume extends HostFileSystemVolume
+ String remoteHost r
+ String remotePath r
+ String userName o
+end
+
+
+object HostScsiDiskPartition
+ String diskName r
+ Int partition r
+end
+
+
+object HostVmfsVolume extends HostFileSystemVolume
+ Int blockSizeMb r
+ Int maxBlocks r
+ Int majorVersion r
+ String version r
+ String uuid r
+ HostScsiDiskPartition extent rl
+ Boolean vmfsUpgradable r
+end
+
+
+object LocalDatastoreInfo extends DatastoreInfo
+ String path o
+end
+
+
+object NasDatastoreInfo extends DatastoreInfo
+ HostNasVolume nas o
+end
+
+
object ObjectContent
ManagedObjectReference obj r
DynamicProperty propSet ol
@@ -453,6 +502,11 @@ object VirtualMachineSnapshotTree
end
+object VmfsDatastoreInfo extends DatastoreInfo
+ HostVmfsVolume vmfs o
+end
+
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Methods
#
@@ -571,6 +625,11 @@ method ReconfigVM_Task returns ManagedObjectReference r
end
+method RefreshDatastore
+ ManagedObjectReference _this r
+end
+
+
method RegisterVM_Task returns ManagedObjectReference r
ManagedObjectReference _this r
String path r
diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py
index 6ae13c0..8df0e80 100755
--- a/src/esx/esx_vi_generator.py
+++ b/src/esx/esx_vi_generator.py
@@ -382,6 +382,9 @@ class Object:
self.properties = properties
self.extended_by = extended_by
+ if self.extended_by is not None:
+ self.extended_by.sort();
+
def generate_struct_members(self, add_banner = False, struct_gap = False):
global objects_by_name
@@ -1095,7 +1098,8 @@ additional_enum_features = { "ManagedEntityStatus" : Enum.FEATURE__ANY_TYPE
"VirtualMachinePowerState" : Enum.FEATURE__ANY_TYPE }
-additional_object_features = { "Event" : Object.FEATURE__LIST,
+additional_object_features = { "DatastoreInfo" : Object.FEATURE__ANY_TYPE | Object.FEATURE__DYNAMIC_CAST,
+ "Event" : Object.FEATURE__LIST,
"HostCpuIdInfo" : Object.FEATURE__ANY_TYPE | Object.FEATURE__LIST,
"ManagedObjectReference" : Object.FEATURE__ANY_TYPE,
"ObjectContent" : Object.FEATURE__DEEP_COPY | Object.FEATURE__LIST,
@@ -1235,6 +1239,7 @@ for obj in objects_by_name.values():
extended_obj.extended_by = [obj.name]
else:
extended_obj.extended_by.append(obj.name)
+ extended_obj.extended_by.sort()
--
1.7.0.4
2
2
Hey folks,
I am poking at Sys::Virt a little. Here's a patch to help with
dependencies.
Cheers,
C.J.
$ diff -u Sys-Virt-0.2.4/Makefile.PL Sys-Virt-0.2.4-cjac/Makefile.PL
--- Sys-Virt-0.2.4/Makefile.PL 2010-05-19 11:56:18.000000000 +0000
+++ Sys-Virt-0.2.4-cjac/Makefile.PL 2010-05-26 00:55:05.000000000 +0000
@@ -16,6 +16,7 @@
'VERSION_FROM' => 'lib/Sys/Virt.pm',
'PREREQ_PM' => {
'Test::More' => 0,
+ 'XML::XPath' => 0,
},
'AUTHOR' => 'Daniel Berrange <dan(a)berrange.com>',
'LIBS' => [$LIBVIRT_LIBS],
2
1
Below is David Alan's original patch with lots of changes.
In particular, it now parses the following two XML descriptions, one
for 802.1Qbg and 802.1Qbh and stored the data internally. The actual
triggering of the switch setup protocol has not been implemented
here but the relevant code to do that should go into the functions
associatePortProfileId() and disassociatePortProfileId().
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbg'>
<parameters managerid='12' typeid='0x123456' typeidversion='1'
instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
</virtualport>
<filterref filter='clean-traffic'/>
</interface>
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbh'>
<parameters profileid='my_profile'/>
</virtualport>
</interface>
I'd suggest to use this patch as a base for triggering the setup
protocol with the 802.1Qb{g|h} switch.
V10:
- Renamed structure virVirtualPortProfileDef to virVirtualPortProfileParams
as per Daniel Berrange's request
V9:
-Addressing Daniel Berrange's comments:
- removing macvtap.h's dependency on domain_conf.h by
moving the virVirtualPortProfileDef structure into macvtap.h
and not passing virtDomainNetDefPtr to any functions in
macvtap.c
Changes from V7 to V8:
- Addressed most of Chris Wright's comments:
- indicating error in case virtualport XML node cannot be parsed
properly
- parsing hex and decimal numbers using virStrToLong_ui() with
parameter '0' for base
- tgifname (target interface name) variable wasn't necessary
to pass to openMacvtapTap function anymore
- assigning the virtual port data structure to the virDomainNetDef
only if it was previously parsed
-> still leaving possibility to start a domain with macvtap but no profile
Changes from V6 to V7:
- make sure that the error code returned by openMacvtapTap() is a negative number
in case the associatePortProfileId() function failed.
Changes from V5 to V6:
- renaming vsi in the XML to virtualport
- replace all occurrences of vsi in the source as well
Changes from V4 to V5:
- removing mode and MAC address parameters from the functions that
will communicate with the hareware diretctly or indirectly
Changes from V3 to V4:
- moving the associate and disassociate functions to the end of the
file for subsequent patches to easier make them generally available
for export
- passing the macvtap interface name rather than the link device since
this otherwise gives funny side effects when using netlink messages
where IFLA_IFNAME and IFLA_ADDRESS are specified and the link dev
all of a sudden gets the MAC address of the macvtap interface.
- Removing rc = -1 error indications in the case of 802.1Qbg|h setup in case
we wanted to use hook scripts for the setup and so the setup doesn't fail
here.
Changes from V2 to V3:
- if instance ID UUID is not supplied it will automatically be generated
- adapted schema to make instance ID UUID optional
- added test case
Some of the changes from V1 to V2:
- parser and XML generator have been separated into their own
functions so they can be re-used elsewhere (passthrough case
for example)
- Adapted XML parser and generator support the above shown type
(802.1Qbg, 802.1Qbh).
- Adapted schema to above XML
- Adapted test XML to above XML
- Passing through the VM's UUID which seems to be necessary for
802.1Qbh -- sorry no host UUID
- adding virtual function ID to association function, in case it's
necessary to use (for SR-IOV)
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
>From a945107f047c7cd71f9c1b74fd74c47d8cdc3670 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Fri, 12 Mar 2010 13:25:04 -0500
Subject: [PATCH 1/1] POC of port profile id support
* Modified schema per DanPB's feedback
* Added test for modified schema
---
docs/schemas/domain.rng | 69 +++++++++++
src/conf/domain_conf.c | 202 +++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 2
src/qemu/qemu_conf.c | 21 +--
src/qemu/qemu_conf.h | 5
src/qemu/qemu_driver.c | 19 +--
src/util/macvtap.c | 137 ++++++++++++++++++++--
src/util/macvtap.h | 45 ++++++-
tests/domainschemadata/portprofile.xml | 36 +++++
9 files changed, 498 insertions(+), 38 deletions(-)
create mode 100644 tests/domainschemadata/portprofile.xml
Index: libvirt-acl/docs/schemas/domain.rng
===================================================================
--- libvirt-acl.orig/docs/schemas/domain.rng
+++ libvirt-acl/docs/schemas/domain.rng
@@ -817,6 +817,9 @@
</optional>
<empty/>
</element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
<ref name="interface-options"/>
</interleave>
</group>
@@ -902,6 +905,45 @@
</optional>
</interleave>
</define>
+ <define name="virtualPortProfile">
+ <choice>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbg</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="managerid">
+ <ref name="uint8range"/>
+ </attribute>
+ <attribute name="typeid">
+ <ref name="uint24range"/>
+ </attribute>
+ <attribute name="typeidversion">
+ <ref name="uint8range"/>
+ </attribute>
+ <optional>
+ <attribute name="instanceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbh</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </element>
+ </element>
+ </group>
+ </choice>
+ </define>
<!--
An emulator description is just a path to the binary used for the task
-->
@@ -1769,4 +1811,31 @@
<param name="pattern">[a-zA-Z0-9_\.:]+</param>
</data>
</define>
+ <define name="uint8range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,2}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint24range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,6}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">16777215</param>
+ </data>
+ </choice>
+ </define>
+ <define name="virtualPortProfileID">
+ <data type="string">
+ <param name="maxLength">39</param>
+ </data>
+ </define>
</grammar>
Index: libvirt-acl/src/conf/domain_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.c
+++ libvirt-acl/src/conf/domain_conf.c
@@ -242,6 +242,11 @@ VIR_ENUM_IMPL(virDomainNetdevMacvtap, VI
"private",
"bridge")
+VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST,
+ "none",
+ "802.1Qbg",
+ "802.1Qbh")
+
VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
"utc",
"localtime",
@@ -1807,6 +1812,190 @@ cleanup:
}
+static int
+virVirtualPortProfileParamsParseXML(xmlNodePtr node,
+ virVirtualPortProfileParamsPtr virtPort)
+{
+ int ret = -1;
+ char *virtPortType;
+ char *virtPortManagerID = NULL;
+ char *virtPortTypeID = NULL;
+ char *virtPortTypeIDVersion = NULL;
+ char *virtPortInstanceID = NULL;
+ char *virtPortProfileID = NULL;
+ xmlNodePtr cur = node->children;
+ const char *msg = NULL;
+
+ virtPortType = virXMLPropString(node, "type");
+ if (!virtPortType)
+ return -1;
+
+ while (cur != NULL) {
+ if (xmlStrEqual(cur->name, BAD_CAST "parameters")) {
+
+ virtPortManagerID = virXMLPropString(cur, "managerid");
+ virtPortTypeID = virXMLPropString(cur, "typeid");
+ virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion");
+ virtPortInstanceID = virXMLPropString(cur, "instanceid");
+ virtPortProfileID = virXMLPropString(cur, "profileid");
+
+ break;
+ }
+
+ cur = cur->next;
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_NONE;
+
+ switch (virVirtualPortTypeFromString(virtPortType)) {
+
+ case VIR_VIRTUALPORT_8021QBG:
+ if (virtPortManagerID != NULL && virtPortTypeID != NULL &&
+ virtPortTypeIDVersion != NULL) {
+ unsigned int val;
+
+ if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) {
+ msg = _("cannot parse value of managerid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of managerid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
+
+ if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xffffff) {
+ msg = _("value for typeid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
+
+ if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeidversion parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of typeidversion out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (virtPortInstanceID != NULL) {
+ if (virUUIDParse(virtPortInstanceID,
+ virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot parse instanceid parameter as a uuid");
+ goto err_exit;
+ }
+ } else {
+ if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot generate a random uuid for instanceid");
+ goto err_exit;
+ }
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
+ ret = 0;
+ } else {
+ msg = _("a parameter is missing for 802.1Qbg description");
+ goto err_exit;
+ }
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ if (virtPortProfileID != NULL) {
+ if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
+ virtPortProfileID) != NULL) {
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
+ ret = 0;
+ } else {
+ msg = _("profileid parameter too long");
+ goto err_exit;
+ }
+ } else {
+ msg = _("profileid parameter is missing for 802.1Qbh descripion");
+ goto err_exit;
+ }
+ break;
+
+
+ default:
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ msg = _("unknown virtualport type");
+ goto err_exit;
+ break;
+ }
+
+err_exit:
+
+ if (msg)
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ VIR_FREE(virtPortManagerID);
+ VIR_FREE(virtPortTypeID);
+ VIR_FREE(virtPortTypeIDVersion);
+ VIR_FREE(virtPortInstanceID);
+ VIR_FREE(virtPortProfileID);
+ VIR_FREE(virtPortType);
+
+ return ret;
+}
+
+
+static void
+virVirtualPortProfileFormat(virBufferPtr buf,
+ virVirtualPortProfileParamsPtr virtPort,
+ const char *indent)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE)
+ return;
+
+ virBufferVSprintf(buf, "%s<virtualport type='%s'>\n",
+ indent,
+ virVirtualPortTypeToString(virtPort->virtPortType));
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+ virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID,
+ uuidstr);
+ virBufferVSprintf(buf,
+ "%s <parameters managerid='%d' typeid='%d' "
+ "typeidversion='%d' instanceid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbg.managerID,
+ virtPort->u.virtPort8021Qbg.typeID,
+ virtPort->u.virtPort8021Qbg.typeIDVersion,
+ uuidstr);
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ virBufferVSprintf(buf,
+ "%s <parameters profileid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbh.profileID);
+ break;
+ }
+
+ virBufferVSprintf(buf, "%s</virtualport>\n", indent);
+}
+
+
/* Parse the XML definition for a network interface
* @param node XML nodeset to parse for net definition
* @return 0 on success, -1 on failure
@@ -1832,6 +2021,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *devaddr = NULL;
char *mode = NULL;
virNWFilterHashTablePtr filterparams = NULL;
+ virVirtualPortProfileParams virtPort;
+ bool virtPortParsed = false;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
@@ -1873,6 +2064,12 @@ virDomainNetDefParseXML(virCapsPtr caps,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ } else if ((virtPortParsed == false) &&
+ (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
+ xmlStrEqual(cur->name, BAD_CAST "virtualport")) {
+ if (virVirtualPortProfileParamsParseXML(cur, &virtPort))
+ goto error;
+ virtPortParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2048,6 +2245,9 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ if (virtPortParsed)
+ def->data.direct.virtPortProfile = virtPort;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5140,6 +5340,8 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferVSprintf(buf, " mode='%s'",
virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
virBufferAddLit(buf, "/>\n");
+ virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile,
+ " ");
break;
case VIR_DOMAIN_NET_TYPE_USER:
Index: libvirt-acl/src/conf/domain_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.h
+++ libvirt-acl/src/conf/domain_conf.h
@@ -38,6 +38,7 @@
# include "network.h"
# include "nwfilter_params.h"
# include "nwfilter_conf.h"
+# include "macvtap.h"
/* Private component of virDomainXMLFlags */
typedef enum {
@@ -290,6 +291,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVirtualPortProfileParams virtPortProfile;
} direct;
} data;
char *ifname;
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -43,6 +43,7 @@
# include "util.h"
# include "memory.h"
+# include "logging.h"
# include "macvtap.h"
# include "interface.h"
# include "conf/domain_conf.h"
@@ -57,6 +58,16 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+
+static int associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid);
+
+static int disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileParamsPtr virtPort);
+
+
static int nlOpen(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@@ -577,8 +588,10 @@ configMacvtapTap(int tapfd, int vnet_hdr
* be NULL if this function is supposed to choose a name
* @macaddress: The MAC address for the macvtap device
* @linkdev: The interface name of the NIC to connect to the external bridge
- * @mode_str: String describing the mode. Valid are 'bridge', 'vepa' and
- * 'private'.
+ * @mode: int describing the mode for 'bridge', 'vepa' or 'private'.
+ * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it
+ * @vmuuid: The UUID of the VM the macvtap belongs to
+ * @virtPortProfile: pointer to object holding the virtual port profile data
* @res_ifname: Pointer to a string pointer where the actual name of the
* interface will be stored into if everything succeeded. It is up
* to the caller to free the string.
@@ -592,8 +605,10 @@ openMacvtapTap(const char *tgifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileParamsPtr virtPortProfile,
+ char **res_ifname)
{
const char *type = "macvtap";
int c, rc;
@@ -639,6 +654,14 @@ create_name:
cr_ifname = ifname;
}
+ if (associatePortProfileId(cr_ifname,
+ virtPortProfile,
+ -1,
+ vmuuid) != 0) {
+ rc = -1;
+ goto link_del_exit;
+ }
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +670,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +679,18 @@ create_name:
if (configMacvtapTap(rc, vnet_hdr) < 0) {
close(rc);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
*res_ifname = strdup(cr_ifname);
} else
- goto link_del_exit;
+ goto disassociate_exit;
return rc;
+disassociate_exit:
+ disassociatePortProfileId(cr_ifname,
+ virtPortProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -674,13 +701,103 @@ link_del_exit:
/**
* delMacvtap:
* @ifname : The name of the macvtap interface
+ * @virtPortProfile: pointer to object holding the virtual port profile data
*
- * Delete an interface given its name.
+ * Delete an interface given its name. Disassociate
+ * it with the switch if port profile parameters
+ * were provided.
*/
void
-delMacvtap(const char *ifname)
+delMacvtap(const char *ifname,
+ virVirtualPortProfileParamsPtr virtPortProfile)
{
- link_del(ifname);
+ if (ifname) {
+ disassociatePortProfileId(ifname,
+ virtPortProfile);
+ link_del(ifname);
+ }
}
#endif
+
+
+/**
+ * associatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: pointer to the object holding port profile parameters
+ * @vf: virtual function number, -1 if to be ignored
+ * @vmuuid : the UUID of the virtual machine
+ *
+ * Associate a port on a swtich with a profile. This function
+ * may notify a kernel driver or an external daemon to run
+ * the setup protocol. If profile parameters were not supplied
+ * by the user, then this function returns without doing
+ * anything.
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid)
+{
+ int rc = 0;
+ VIR_DEBUG("Associating port profile '%p' on link device '%s'",
+ virtPort, macvtap_ifname);
+ (void)vf;
+ (void)vmuuid;
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * disassociatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: point to object holding port profile parameters
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileParamsPtr virtPort)
+{
+ int rc = 0;
+ VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
+ virtPort, macvtap_ifname);
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -24,6 +24,40 @@
# include <config.h>
+
+enum virVirtualPortType {
+ VIR_VIRTUALPORT_NONE,
+ VIR_VIRTUALPORT_8021QBG,
+ VIR_VIRTUALPORT_8021QBH,
+
+ VIR_VIRTUALPORT_TYPE_LAST,
+};
+
+# ifdef IFLA_VF_PORT_PROFILE_MAX
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX
+# else
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40
+# endif
+
+/* profile data for macvtap (VEPA) */
+typedef struct _virVirtualPortProfileParams virVirtualPortProfileParams;
+typedef virVirtualPortProfileParams *virVirtualPortProfileParamsPtr;
+struct _virVirtualPortProfileParams {
+ enum virVirtualPortType virtPortType;
+ union {
+ struct {
+ uint8_t managerID;
+ uint32_t typeID; // 24 bit valid
+ uint8_t typeIDVersion;
+ unsigned char instanceID[VIR_UUID_BUFLEN];
+ } virtPort8021Qbg;
+ struct {
+ char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX];
+ } virtPort8021Qbh;
+ } u;
+};
+
+
# if defined(WITH_MACVTAP)
# include "internal.h"
@@ -32,10 +66,13 @@ int openMacvtapTap(const char *ifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileParamsPtr virtPortProfile,
+ char **res_ifname);
-void delMacvtap(const char *ifname);
+void delMacvtap(const char *ifname,
+ virVirtualPortProfileParamsPtr virtPortProfile);
# endif /* WITH_MACVTAP */
@@ -44,4 +81,6 @@ void delMacvtap(const char *ifname);
# define MACVTAP_MODE_BRIDGE_STR "bridge"
+VIR_ENUM_DECL(virVirtualPort)
+
#endif /* __UTIL_MACVTAP_H__ */
Index: libvirt-acl/tests/domainschemadata/portprofile.xml
===================================================================
--- /dev/null
+++ libvirt-acl/tests/domainschemadata/portprofile.xml
@@ -0,0 +1,36 @@
+<domain type='lxc'>
+ <name>portprofile</name>
+ <uuid>00000000-0000-0000-0000-000000000000</uuid>
+ <memory>1048576</memory>
+ <os>
+ <type>exe</type>
+ <init>/sh</init>
+ </os>
+ <devices>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'
+ instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbh'>
+ <parameters profileid='my_profile'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ </devices>
+</domain>
Index: libvirt-acl/src/qemu/qemu_conf.h
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.h
+++ libvirt-acl/src/qemu/qemu_conf.h
@@ -274,9 +274,8 @@ qemudOpenVhostNet(virDomainNetDefPtr net
int qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags);
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid);
int qemudProbeMachineTypes (const char *binary,
virCapsGuestMachinePtr **machines,
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3708,10 +3708,9 @@ static void qemudShutdownVMDaemon(struct
def = vm->def;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
- if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (net->ifname)
- delMacvtap(net->ifname);
- }
+ if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
#endif
@@ -7466,9 +7465,8 @@ static int qemudDomainAttachNetDevice(vi
}
if ((tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags)) < 0)
+ qemuCmdFlags,
+ vm->def->uuid)) < 0)
return -1;
}
@@ -8514,10 +8512,9 @@ qemudDomainDetachNetDevice(struct qemud_
virNWFilterTearNWFilter(detach);
#if WITH_MACVTAP
- if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (detach->ifname)
- delMacvtap(detach->ifname);
- }
+ if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(detach->ifname,
+ &detach->data.direct.virtPortProfile);
#endif
if ((driver->macFilter) && (detach->ifname != NULL)) {
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1470,9 +1470,8 @@ int
qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags)
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid)
{
int rc;
#if WITH_MACVTAP
@@ -1484,8 +1483,9 @@ qemudPhysIfaceConnect(virConnectPtr conn
net->model && STREQ(net->model, "virtio"))
vnet_hdr = 1;
- rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode,
- &res_ifname, vnet_hdr);
+ rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
+ net->data.direct.mode, vnet_hdr, vmuuid,
+ &net->data.direct.virtPortProfile, &res_ifname);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1505,17 +1505,17 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
}
}
#else
(void)conn;
(void)net;
- (void)linkdev;
- (void)brmode;
(void)qemuCmdFlags;
(void)driver;
+ (void)vmuuid;
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No support for macvtap device"));
rc = -1;
@@ -4135,9 +4135,8 @@ int qemudBuildCommandLine(virConnectPtr
goto no_memory;
} else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
int tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags);
+ qemuCmdFlags,
+ def->uuid);
if (tapfd < 0)
goto error;
4
4
26 May '10
Hello all,
This is the first patch about storage management driver on IBM Power
Hypervisor. I reviewd a couple of times but perhaps I might be missing
something. Any comments are welcome.
[]'s
--
Eduardo Otubo
Software Engineer
Linux Technology Center
IBM Systems & Technology Group
Mobile: +55 19 8135 0885
eotubo(a)linux.vnet.ibm.com
2
4
* src/util/macvtap.c: (associatePortProfileId)
(disassociatePortProfileId): Move inside HAVE_MACVTAP
conditional.
Reported by Eduardo Otubo
---
Pushing as trivial, to fix the build.
src/util/macvtap.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 715f329..4f8c95a 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -718,8 +718,6 @@ delMacvtap(const char *ifname,
}
}
-#endif
-
/**
* associatePortProfile
@@ -801,3 +799,5 @@ disassociatePortProfileId(const char *macvtap_ifname,
return rc;
}
+
+#endif /* WITH_MACVTAP */
--
1.7.0.1
1
0
26 May '10
In reviewing/testing some incoming patches, I noticed a new
"make check" failure.
There were two problems:
- not diagnosing an invalid host UUID. The lack of this diagnostic
made it slightly more challenging to track down the next problem:
- not accommodating the fact that our template now contains
an invalid line; before the addition of "host_uuid", each
commented-out sample line was valid.
Here are the fixes.
>From 11228f4b2dba1b43a9de97a575d1121eba2e8cf4 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Tue, 25 May 2010 20:56:18 +0200
Subject: [PATCH 1/2] libvirtd: diagnose invalid host UUID
* daemon/libvirtd.c (remoteReadConfigFile): Diagnose an invalid
host UUID rather than silently exiting with status 7.
---
daemon/libvirtd.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 8fa78b8..e86f78d 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -2843,8 +2843,10 @@ remoteReadConfigFile (struct qemud_server *server, const char *filename)
GET_CONF_INT (conf, filename, max_client_requests);
GET_CONF_STR (conf, filename, host_uuid);
- if (virSetHostUUIDStr(host_uuid))
+ if (virSetHostUUIDStr(host_uuid)) {
+ VIR_ERROR(_("invalid host UUID: %s"), host_uuid);
goto free_and_fail;
+ }
VIR_FREE(host_uuid);
--
1.7.1.342.g1c280
>From 6d20ae23fe5a5e64a0cf1be1f0b9cebce49625c1 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Tue, 25 May 2010 21:08:37 +0200
Subject: [PATCH 2/2] tests: avoid new failure of the daemon-conf test
* tests/daemon-conf: Accommodate the fact that out template,
daemon/libvirtd.conf now contains an invalid host_uuid.
Convert it to a valid one before the final libvirtd-running
test that must terminate normally.
---
tests/daemon-conf | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/tests/daemon-conf b/tests/daemon-conf
index 14d4ced..0e756d4 100755
--- a/tests/daemon-conf
+++ b/tests/daemon-conf
@@ -83,7 +83,11 @@ if test 108 -lt `echo $SOCKPATH | wc -c`; then
skip_test_ "CWD too long"
fi
-$abs_top_builddir/daemon/libvirtd --pid-file=pid-file --config=tmp.conf > log 2>&1 & pid=$!
+# Replace the invalid host_uuid with one that is valid:
+sed 's/^\(host_uuid =.*\)0"$/\11"/' tmp.conf > k; mv k tmp.conf
+
+$abs_top_builddir/daemon/libvirtd --pid-file=pid-file --config=tmp.conf \
+ > log 2>&1 & pid=$!
sleep $sleep_secs
kill $pid
--
1.7.1.342.g1c280
2
2
25 May '10
Reposting due to malformatted patch.
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's three posted patches:
[PATCH v10] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
[PATCH v3] Add host UUID (to libvirt capabilities)
Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.
V7:
- Addressing Jim Meyering's comments; this also touches existing
code for example for correcting indentation of break statements or
simplification of switch statements.
Changes from v5 to v6:
- Renamed occurrencvirVirtualPortProfileDef to
virVirtualPortProfileParamses
- 802.1Qbg part prepared for sending a RTM_SETLINK and getting
processing status back plus a subsequent RTM_GETLINK to
get IFLA_PORT_RESPONSE.
Note: This interface for 802.1Qbg may still change
Changes from v4 to v5:
- [David Allan] move getPhysfn inside IFLA_VF_PORT_MAX to avoid
compiler
warning when latest if_link.h isn't available
Changes from v3 to v4:
- move from Stefan's 802.1Qb{g|h} XML v8 to v9
- move hostuuid and vf index calcs to inside doPortProfileOp8021Qbh
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
configure.ac | 16
src/qemu/qemu_conf.c | 2
src/qemu/qemu_driver.c | 4
src/util/macvtap.c | 837 +++++++++++++++++++++++++++++++++++++++++++++----
src/util/macvtap.h | 1
5 files changed, 788 insertions(+), 72 deletions(-)
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || test "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1509,7 +1509,7 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
}
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3709,7 +3709,7 @@ static void qemudShutdownVMDaemon(struct
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
#endif
@@ -8514,7 +8514,7 @@ qemudDomainDetachNetDevice(struct qemud_
#if WITH_MACVTAP
if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(detach->ifname,
+ delMacvtap(detach->ifname, detach->data.direct.linkdev,
&detach->data.direct.virtPortProfile);
#endif
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,15 +61,30 @@
# 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)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr virtPort,
- int vf,
const unsigned char *vmuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -97,7 +115,7 @@ static void nlClose(int fd)
*/
static
int nlComm(struct nlmsghdr *nlmsg,
- char **respbuf, int *respbuflen)
+ char **respbuf, unsigned int *respbuflen)
{
int rc = 0;
struct sockaddr_nl nladdr = {
@@ -159,6 +177,162 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @timeout_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, uint32_t nl_groups,
+ char **respbuf, unsigned int *respbuflen,
+ unsigned long long timeout_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ size_t rcv_offset = 0;
+ ssize_t nbytes;
+ struct timeval tv = {
+ .tv_sec = timeout_usecs / MICROSEC_PER_SEC,
+ .tv_usec = timeout_usecs % MICROSEC_PER_SEC,
+ };
+ bool got_valid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!got_valid) {
+
+ rcv_offset = 0;
+
+ while (1) {
+ int n;
+ fd_set rfds;
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcv_offset + rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n < 0)
+ virReportSystemError(errno, "%s",
+ _("error in select call"));
+ if (n == 0)
+ virReportSystemError(ETIMEDOUT, "%s",
+ _("no valid netlink response was received"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcv_offset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcv_offset += nbytes;
+ break;
+ }
+ *respbuflen = rcv_offset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (err->error != -EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ got_valid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ got_valid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+# endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +378,8 @@ nlAppend(struct nlmsghdr *nlm, int totle
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -213,15 +389,15 @@ link_add(const char *type,
int *retry)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta, *rta1, *li;
- struct ifinfomsg i = { .ifi_family = AF_UNSPEC };
+ struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
int ifindex;
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
if (ifaceGetIndex(true, srcdev, &ifindex) != 0)
return -1;
@@ -232,65 +408,46 @@ link_add(const char *type,
nlInit(nlm, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, RTM_NEWLINK);
- if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINK,
&ifindex, sizeof(ifindex));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_ADDRESS,
macaddress, macaddrsize);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (ifname) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
ifname, strlen(ifname) + 1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
}
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINKINFO, NULL, 0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ if (!rta ||
+ !(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_KIND,
type, strlen(type));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (macvlan_mode > 0) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_DATA,
NULL, 0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ if (!rta ||
+ !(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_MACVLAN_MODE,
&macvlan_mode, sizeof(macvlan_mode));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
rta1->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)rta1;
@@ -312,15 +469,15 @@ link_add(const char *type,
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
+ switch (err->error) {
case 0:
- break;
+ break;
- case EEXIST:
+ case -EEXIST:
*retry = 1;
rc = -1;
- break;
+ break;
default:
virReportSystemError(-err->error,
@@ -328,10 +485,10 @@ link_add(const char *type,
type);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -358,14 +515,14 @@ static int
link_del(const char *name)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta;
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
@@ -376,10 +533,7 @@ link_del(const char *name)
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
name, strlen(name)+1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
@@ -396,20 +550,16 @@ link_del(const char *name)
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
- case 0:
- break;
-
- default:
+ if (err->error) {
virReportSystemError(-err->error,
_("error destroying %s interface"),
name);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -509,11 +659,9 @@ macvtapModeFromInt(enum virDomainNetdevM
switch (mode) {
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_PRIVATE:
return MACVLAN_MODE_PRIVATE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_BRIDGE:
return MACVLAN_MODE_BRIDGE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA:
default:
@@ -655,8 +803,8 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile,
- -1,
vmuuid) != 0) {
rc = -1;
goto link_del_exit;
@@ -689,6 +837,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile);
link_del_exit:
@@ -701,6 +850,7 @@ link_del_exit:
/**
* 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
@@ -709,24 +859,565 @@ link_del_exit:
*/
void
delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile)
{
if (ifname) {
disassociatePortProfileId(ifname,
+ linkdev,
virtPortProfile);
link_del(ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(bool multicast, int ifindex, struct nlattr **tb, char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ unsigned int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (!multicast) {
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ 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
+doPortProfileOpSetLink(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[RATTBUF_SIZE];
+ struct rtattr *rta, *vfports = NULL, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ unsigned int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta ||
+ !(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* begin nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta ||
+ !(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of ifindex %d"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = STATUS_POLL_TIMEOUT_USEC / STATUS_POLL_INTERVL_USEC;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifindex,
+ 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 = link_dump(multicast, ifindex, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ 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 ifindex %d"),
+ status, ifindex);
+ rc = 1;
+ break;
+ }
+ } else
+ goto err_exit;
+ 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 = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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 multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ 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 ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ PORT_SELF_VF,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+# ifdef IFLA_VF_PORT_MAX
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+ return rc;
+}
+# endif /* IFLA_VF_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(ifname, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
+ if (ifaceGetIndex(true, physfndev, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ 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"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ hostuuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
*
* Associate a port on a swtich with a profile. This function
@@ -740,15 +1431,14 @@ delMacvtap(const char *ifname,
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr virtPort,
- int vf,
const unsigned char *vmuuid)
{
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -756,11 +1446,14 @@ associatePortProfileId(const char *macvt
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ vmuuid,
+ ASSOCIATE);
break;
}
@@ -772,6 +1465,7 @@ associatePortProfileId(const char *macvt
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @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
@@ -779,9 +1473,11 @@ associatePortProfileId(const char *macvt
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr virtPort)
{
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
@@ -791,13 +1487,18 @@ disassociatePortProfileId(const char *ma
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -72,6 +72,7 @@ int openMacvtapTap(const char *ifname,
char **res_ifname);
void delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile);
# endif /* WITH_MACVTAP */
3
2
25 May '10
Currently all host audio backends are disabled if a VM is using VNC, in
favor of the QEMU VNC audio extension. Unfortunately no released VNC
client supports this extension, so users have no way of getting audio
to work if using VNC.
Add a new config option in qemu.conf which allows changing libvirt's
behavior, but keep the default intact.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/qemu/qemu.conf | 10 ++++++++++
src/qemu/qemu_conf.c | 17 ++++++++++++-----
src/qemu/qemu_conf.h | 2 ++
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
index 3da332f..fec946d 100644
--- a/src/qemu/qemu.conf
+++ b/src/qemu/qemu.conf
@@ -168,3 +168,13 @@
# be assigned to guests.
#
# relaxed_acs_check = 1
+
+
+# QEMU implements anextension for providing audio over a VNC connection,
+# though if your VNC client does not support it, your only chance for getting
+# sound output is through regular audio backends. By default, libvirt will
+# disable all QEMU sound backends if using VNC, since they can cause
+# permissions issues. Enabling this option will make libvirtd honor the
+# QEMU_AUDIO_DRV environment variable when using VNC.
+#
+# vnc_enable_audio_backend = 0
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index d7bc798..5c30f48 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -351,6 +351,10 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
CHECK_TYPE ("relaxed_acs_check", VIR_CONF_LONG);
if (p) driver->relaxedACS = p->l;
+ p = virConfGetValue (conf, "vnc_enable_audio_backend");
+ CHECK_TYPE ("vnc_enable_audio_backend", VIR_CONF_LONG);
+ if (p) driver->vncEnableAudioBackend = p->l;
+
virConfFree (conf);
return 0;
}
@@ -4394,12 +4398,15 @@ int qemudBuildCommandLine(virConnectPtr conn,
ADD_ARG_LIT(def->graphics[0]->data.vnc.keymap);
}
- /* QEMU implements a VNC extension for providing audio, so we
- * set the audio backend to none, to prevent it opening the
- * host OS audio devices since that causes security issues
- * and is non-sensical when using VNC.
+ /* Unless user requested it, set the audio backend to none, to
+ * prevent it opening the host OS audio devices since that causes
+ * security issues and is may not work when using VNC.
*/
- ADD_ENV_LIT("QEMU_AUDIO_DRV=none");
+ if (driver->vncEnableAudioBackend) {
+ ADD_ENV_COPY("QEMU_AUDIO_DRV");
+ } else {
+ ADD_ENV_LIT("QEMU_AUDIO_DRV=none");
+ }
} else if ((def->ngraphics == 1) &&
def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
char *xauth = NULL;
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 11f8dcd..aa6ed61 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -137,6 +137,8 @@ struct qemud_driver {
unsigned int relaxedACS : 1;
+ unsigned int vncEnableAudioBackend : 1;
+
virCapsPtr caps;
/* An array of callbacks */
--
1.6.6.1
3
7
25 May '10
This patch introduces a dependency on libnl, which subsequent patches
will then use.
Changes from V1 to V2:
- added diffstats
- following changes in tree
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
configure.ac | 24 ++++++++++++++++++++++++
libvirt.spec.in | 14 ++++++++++++++
src/Makefile.am | 4 ++--
3 files changed, 40 insertions(+), 2 deletions(-)
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -42,6 +42,7 @@ HAL_REQUIRED=0.5.0
DEVMAPPER_REQUIRED=1.0.0
LIBCURL_REQUIRED="7.18.0"
LIBPCAP_REQUIRED="1.0.0"
+LIBNL_REQUIRED="1.1"
dnl Checks for C compiler.
AC_PROG_CC
@@ -2005,6 +2006,24 @@ fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+dnl netlink library
+
+LIBNL_CFLAGS=""
+LIBNL_LIBS=""
+
+if test "$with_macvtap" = "yes"; then
+ PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
+ ], [
+ AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
+ ])
+fi
+
+AC_SUBST([LIBNL_CFLAGS])
+AC_SUBST([LIBNL_LIBS])
+
+
+
+
# Only COPYING.LIB is under version control, yet COPYING
# is included as part of the distribution tarball.
# Copy one to the other, but only if this is a srcdir-build.
@@ -2183,6 +2202,11 @@ AC_MSG_NOTICE([ pcap: $LIBPCAP_CFLAGS
else
AC_MSG_NOTICE([ pcap: no])
fi
+if test "$with_macvtap" = "yes" ; then
+AC_MSG_NOTICE([ nl: $LIBNL_CFLAGS $LIBNL_LIBS])
+else
+AC_MSG_NOTICE([ nl: no])
+fi
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Test suite])
AC_MSG_NOTICE([])
Index: libvirt-acl/libvirt.spec.in
===================================================================
--- libvirt-acl.orig/libvirt.spec.in
+++ libvirt-acl/libvirt.spec.in
@@ -63,6 +63,7 @@
%define with_yajl 0%{!?_without_yajl:0}
%define with_nwfilter 0%{!?_without_nwfilter:0}
%define with_libpcap 0%{!?_without_libpcap:0}
+%define with_macvtap 0%{!?_without_macvtap:0}
# Non-server/HV driver defaults which are always enabled
%define with_python 0%{!?_without_python:1}
@@ -153,6 +154,11 @@
%if %{with_qemu}
%define with_nwfilter 0%{!?_without_nwfilter:%{server_drivers}}
%define with_libpcap 0%{!?_without_libpcap:%{server_drivers}}
+%define with_macvtap 0%{!?_without_macvtap:%{server_drivers}}
+%endif
+
+%if %{with_macvtap}
+%define with_libnl 1
%endif
# Force QEMU to run as non-root
@@ -282,6 +288,9 @@ BuildRequires: yajl-devel
%if %{with_libpcap}
BuildRequires: libpcap-devel
%endif
+%if %{with_libnl}
+BuildRequires: libnl-devel
+%endif
%if %{with_avahi}
BuildRequires: avahi-devel
%endif
@@ -531,6 +540,10 @@ of recent versions of Linux (and other O
%define _without_libpcap --without-libpcap
%endif
+%if ! %{with_macvtap}
+%define _without_macvtap --without-macvtap
+%endif
+
%configure %{?_without_xen} \
%{?_without_qemu} \
%{?_without_openvz} \
@@ -560,6 +573,7 @@ of recent versions of Linux (and other O
%{?_without_udev} \
%{?_without_yajl} \
%{?_without_libpcap} \
+ %{?_without_macvtap} \
--with-qemu-user=%{qemu_user} \
--with-qemu-group=%{qemu_group} \
--with-init-script=redhat \
Index: libvirt-acl/src/Makefile.am
===================================================================
--- libvirt-acl.orig/src/Makefile.am
+++ libvirt-acl/src/Makefile.am
@@ -998,7 +998,7 @@ libvirt_la_LDFLAGS = $(VERSION_SCRIPT_FL
$(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS)
libvirt_la_BUILT_LIBADD += ../gnulib/lib/libgnu.la
libvirt_la_LIBADD += $(LIBXML_LIBS) \
- $(LIBPCAP_LIBS) \
+ $(LIBPCAP_LIBS) $(LIBNL_LIBS) \
$(DRIVER_MODULE_LIBS) \
$(CYGWIN_EXTRA_LIBADD)
libvirt_la_CFLAGS = $(COVERAGE_CFLAGS) -DIN_LIBVIRT
@@ -1057,7 +1057,7 @@ libvirt_lxc_SOURCES = \
libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDFLAGS)
libvirt_lxc_LDADD = $(CAPNG_LIBS) $(YAJL_LIBS) \
$(LIBXML_LIBS) $(NUMACTL_LIBS) $(LIB_PTHREAD) \
- ../gnulib/lib/libgnu.la
+ $(LIBNL_LIBS) ../gnulib/lib/libgnu.la
libvirt_lxc_CFLAGS = \
$(LIBPARTED_CFLAGS) \
$(NUMACTL_CFLAGS) \
4
7
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's three posted patches:
[PATCH v10] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
[PATCH v3] Add host UUID (to libvirt capabilities)
Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.
V7:
- Addressing Jim Meyering's comments; this also touches existing
code for example for correcting indentation of break statements or
simplification of switch statements.
Changes from v5 to v6:
- Renamed occurrencvirVirtualPortProfileDef to
virVirtualPortProfileParamses
- 802.1Qbg part prepared for sending a RTM_SETLINK and getting
processing status back plus a subsequent RTM_GETLINK to
get IFLA_PORT_RESPONSE.
Note: This interface for 802.1Qbg may still change
Changes from v4 to v5:
- [David Allan] move getPhysfn inside IFLA_VF_PORT_MAX to avoid
compiler
warning when latest if_link.h isn't available
Changes from v3 to v4:
- move from Stefan's 802.1Qb{g|h} XML v8 to v9
- move hostuuid and vf index calcs to inside doPortProfileOp8021Qbh
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
configure.ac | 16
src/qemu/qemu_conf.c | 2
src/qemu/qemu_driver.c | 4
src/util/macvtap.c | 837
+++++++++++++++++++++++++++++++++++++++++++++----
src/util/macvtap.h | 1
5 files changed, 788 insertions(+), 72 deletions(-)
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support
is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || test "$with_virtualport" = "yes";
then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap
support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1509,7 +1509,7 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
}
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3709,7 +3709,7 @@ static void qemudShutdownVMDaemon(struct
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
#endif
@@ -8514,7 +8514,7 @@ qemudDomainDetachNetDevice(struct qemud_
#if WITH_MACVTAP
if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(detach->ifname,
+ delMacvtap(detach->ifname, detach->data.direct.linkdev,
&detach->data.direct.virtPortProfile);
#endif
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,15 +61,30 @@
# 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)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr
virtPort,
- int vf,
const unsigned char *vmuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const
virVirtualPortProfileParamsPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -97,7 +115,7 @@ static void nlClose(int fd)
*/
static
int nlComm(struct nlmsghdr *nlmsg,
- char **respbuf, int *respbuflen)
+ char **respbuf, unsigned int *respbuflen)
{
int rc = 0;
struct sockaddr_nl nladdr = {
@@ -159,6 +177,162 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response
buffer
+ * on return of the function.
+ * @timeout_usecs: timeout in microseconds to wait for a success
message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, uint32_t nl_groups,
+ char **respbuf, unsigned int *respbuflen,
+ unsigned long long timeout_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ size_t rcv_offset = 0;
+ ssize_t nbytes;
+ struct timeval tv = {
+ .tv_sec = timeout_usecs / MICROSEC_PER_SEC,
+ .tv_usec = timeout_usecs % MICROSEC_PER_SEC,
+ };
+ bool got_valid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!got_valid) {
+
+ rcv_offset = 0;
+
+ while (1) {
+ int n;
+ fd_set rfds;
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcv_offset + rcvChunkSize) < 0)
{
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n < 0)
+ virReportSystemError(errno, "%s",
+ _("error in select call"));
+ if (n == 0)
+ virReportSystemError(ETIMEDOUT, "%s",
+ _("no valid netlink response was
received"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcv_offset]),
rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink
socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcv_offset += nbytes;
+ break;
+ }
+ *respbuflen = rcv_offset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (err->error != -EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ got_valid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ got_valid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+# endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +378,8 @@ nlAppend(struct nlmsghdr *nlm, int totle
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -213,15 +389,15 @@ link_add(const char *type,
int *retry)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta, *rta1, *li;
- struct ifinfomsg i = { .ifi_family = AF_UNSPEC };
+ struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
int ifindex;
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
if (ifaceGetIndex(true, srcdev, &ifindex) != 0)
return -1;
@@ -232,65 +408,46 @@ link_add(const char *type,
nlInit(nlm, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
RTM_NEWLINK);
- if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINK,
&ifindex, sizeof(ifindex));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_ADDRESS,
macaddress, macaddrsize);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
if (ifname) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
ifname, strlen(ifname) + 1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
}
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINKINFO, NULL,
0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
+ if (!rta ||
+ !(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_KIND,
type, strlen(type));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
if (macvlan_mode > 0) {
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_DATA,
NULL, 0);
- if (!rta)
- goto buffer_too_small;
-
- if (!(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
+ if (!rta ||
+ !(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
goto buffer_too_small;
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
IFLA_MACVLAN_MODE,
&macvlan_mode, sizeof(macvlan_mode));
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
rta1->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)rta1;
@@ -312,15 +469,15 @@ link_add(const char *type,
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
+ switch (err->error) {
case 0:
- break;
+ break;
- case EEXIST:
+ case -EEXIST:
*retry = 1;
rc = -1;
- break;
+ break;
default:
virReportSystemError(-err->error,
@@ -328,10 +485,10 @@ link_add(const char *type,
type);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -358,14 +515,14 @@ static int
link_del(const char *name)
{
int rc = 0;
- char nlmsgbuf[256];
+ char nlmsgbuf[NLMSGBUF_SIZE];
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
struct nlmsgerr *err;
- char rtattbuf[64];
+ char rtattbuf[RATTBUF_SIZE];
struct rtattr *rta;
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
char *recvbuf = NULL;
- int recvbuflen;
+ unsigned int recvbuflen;
memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
@@ -376,10 +533,7 @@ link_del(const char *name)
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
name, strlen(name)+1);
- if (!rta)
- goto buffer_too_small;
-
- if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
goto buffer_too_small;
if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
@@ -396,20 +550,16 @@ link_del(const char *name)
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
- switch (-err->error) {
- case 0:
- break;
-
- default:
+ if (err->error) {
virReportSystemError(-err->error,
_("error destroying %s interface"),
name);
rc = -1;
}
- break;
+ break;
case NLMSG_DONE:
- break;
+ break;
default:
goto malformed_resp;
@@ -509,11 +659,9 @@ macvtapModeFromInt(enum virDomainNetdevM
switch (mode) {
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_PRIVATE:
return MACVLAN_MODE_PRIVATE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_BRIDGE:
return MACVLAN_MODE_BRIDGE;
- break;
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA:
default:
@@ -655,8 +803,8 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile,
- -1,
vmuuid) != 0) {
rc = -1;
goto link_del_exit;
@@ -689,6 +837,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile);
link_del_exit:
@@ -701,6 +850,7 @@ link_del_exit:
/**
* 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
@@ -709,24 +859,565 @@ link_del_exit:
*/
void
delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile)
{
if (ifname) {
disassociatePortProfileId(ifname,
+ linkdev,
virtPortProfile);
link_del(ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(bool multicast, int ifindex, struct nlattr **tb, char
**recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ unsigned int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (!multicast) {
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX,
tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX,
tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX,
tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ 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
+doPortProfileOpSetLink(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[NLMSGBUF_SIZE];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[RATTBUF_SIZE];
+ struct rtattr *rta, *vfports = NULL, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ unsigned int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF,
NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS,
NULL, 0);
+ if (!rta ||
+ !(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* begin nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT,
NULL, 0);
+ }
+
+ if (!rta ||
+ !(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta || !nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char
*)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of ifindex %
d"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = STATUS_POLL_TIMEOUT_USEC / STATUS_POLL_INTERVL_USEC;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifindex,
+ 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 = link_dump(multicast, ifindex, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ 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 ifindex
%d"),
+ status, ifindex);
+ rc = 1;
+ break;
+ }
+ } else
+ goto err_exit;
+ 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 = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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 multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ 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 ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ PORT_SELF_VF,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+# ifdef IFLA_VF_PORT_MAX
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+ return rc;
+}
+# endif /* IFLA_VF_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileParamsPtr virtPort,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(ifname, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
+ if (ifaceGetIndex(true, physfndev, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ 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"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ hostuuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
*
* Associate a port on a swtich with a profile. This function
@@ -740,15 +1431,14 @@ delMacvtap(const char *ifname,
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr virtPort,
- int vf,
const unsigned char *vmuuid)
{
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -756,11 +1446,14 @@ associatePortProfileId(const char *macvt
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ vmuuid,
+ ASSOCIATE);
break;
}
@@ -772,6 +1465,7 @@ associatePortProfileId(const char *macvt
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @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
@@ -779,9 +1473,11 @@ associatePortProfileId(const char *macvt
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileParamsPtr
virtPort)
{
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s'
",
virtPort, macvtap_ifname);
@@ -791,13 +1487,18 @@ disassociatePortProfileId(const char *ma
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -72,6 +72,7 @@ int openMacvtapTap(const char *ifname,
char **res_ifname);
void delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileParamsPtr virtPortProfile);
# endif /* WITH_MACVTAP */
3
2
25 May '10
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/storage/storage_backend_mpath.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c
index 8d0a92a..3a137eb 100644
--- a/src/storage/storage_backend_mpath.c
+++ b/src/storage/storage_backend_mpath.c
@@ -302,7 +302,7 @@ virStorageBackendMpathRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
{
int retval = 0;
- VIR_ERROR(_("in %s"), __func__);
+ VIR_DEBUG("in %s", __func__);
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
--
1.6.6.1
2
2
Spurious / in a pool target path makes life difficult for apps using the
GetVolByPath, and doing other path based comparisons with pools. This
has caused a few issues for virt-manager users:
https://bugzilla.redhat.com/show_bug.cgi?id=494005
https://bugzilla.redhat.com/show_bug.cgi?id=593565
Add a new util API which removes spurious /, virFileSanitizePath. Sanitize
target paths when parsing pool XML, and for paths passed to GetVolByPath.
v2: Leading // must be preserved, properly sanitize path=/, sanitize
away /./ -> /
v3: Properly handle starting ./ and ending /.
v4: Drop all '.' handling, just sanitize / for now.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/conf/storage_conf.c | 8 ++++++-
src/libvirt_private.syms | 1 +
src/storage/storage_driver.c | 8 ++++++-
src/util/util.c | 49 ++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 2 +
5 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 9aad081..422e76a 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -602,6 +602,7 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
xmlNodePtr source_node;
char *type = NULL;
char *uuid = NULL;
+ char *tmppath;
if (VIR_ALLOC(ret) < 0) {
virReportOOMError();
@@ -699,11 +700,16 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
- if ((ret->target.path = virXPathString("string(./target/path)", ctxt)) == NULL) {
+ if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
virStorageReportError(VIR_ERR_XML_ERROR,
"%s", _("missing storage pool target path"));
goto cleanup;
}
+ ret->target.path = virFileSanitizePath(tmppath);
+ VIR_FREE(tmppath);
+ if (!ret->target.path)
+ goto cleanup;
+
if (virStorageDefParsePerms(ctxt, &ret->target.perms,
"./target/permissions", 0700) < 0)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1e4bfd0..8b397bf 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -675,6 +675,7 @@ virFileReadLimFD;
virFilePid;
virFileReadPid;
virFileLinkPointsTo;
+virFileSanitizePath;
virParseNumber;
virParseVersionString;
virPipeReadUntilEOF;
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index b148e39..0870f74 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1204,6 +1204,11 @@ storageVolumeLookupByPath(virConnectPtr conn,
virStorageDriverStatePtr driver = conn->storagePrivateData;
unsigned int i;
virStorageVolPtr ret = NULL;
+ char *cleanpath;
+
+ cleanpath = virFileSanitizePath(path);
+ if (!cleanpath)
+ return NULL;
storageDriverLock(driver);
for (i = 0 ; i < driver->pools.count && !ret ; i++) {
@@ -1213,7 +1218,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
const char *stable_path;
stable_path = virStorageBackendStablePath(driver->pools.objs[i],
- path);
+ cleanpath);
/*
* virStorageBackendStablePath already does
* virStorageReportError if it fails; we just need to keep
@@ -1242,6 +1247,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
"%s", _("no storage vol with matching path"));
cleanup:
+ VIR_FREE(cleanpath);
storageDriverUnlock(driver);
return ret;
}
diff --git a/src/util/util.c b/src/util/util.c
index 92b9a0f..0642858 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1921,6 +1921,55 @@ int virFileAbsPath(const char *path, char **abspath)
return 0;
}
+/* Remove spurious / characters from a path. The result must be freed */
+char *
+virFileSanitizePath(const char *path)
+{
+ const char *cur = path;
+ char *cleanpath;
+ int idx = 0;
+
+ cleanpath = strdup(path);
+ if (!cleanpath) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ /* Need to sanitize:
+ * // -> //
+ * /// -> /
+ * /../foo -> /../foo
+ * /foo///bar/ -> /foo/bar
+ */
+
+ /* Starting with // is valid posix, but ///foo == /foo */
+ if (cur[0] == '/' && cur[1] == '/' && cur[2] != '/') {
+ idx = 2;
+ cur += 2;
+ }
+
+ /* Sanitize path in place */
+ while (*cur != '\0') {
+ if (*cur != '/') {
+ cleanpath[idx++] = *cur++;
+ continue;
+ }
+
+ /* Skip all extra / */
+ while (*++cur == '/')
+ continue;
+
+ /* Don't add a trailing / */
+ if (idx != 0 && *cur == '\0')
+ break;
+
+ cleanpath[idx++] = '/';
+ }
+ cleanpath[idx] = '\0';
+
+ return cleanpath;
+}
+
/* Like strtol, but produce an "int" result, and check more carefully.
Return 0 upon success; return -1 to indicate failure.
When END_PTR is NULL, the byte after the final valid digit must be NUL.
diff --git a/src/util/util.h b/src/util/util.h
index 6bf6bcc..abc2688 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -118,6 +118,8 @@ char *virFindFileInPath(const char *file);
int virFileExists(const char *path);
+char *virFileSanitizePath(const char *path);
+
enum {
VIR_FILE_OP_NONE = 0,
VIR_FILE_OP_AS_UID = (1 << 0),
--
1.6.6.1
2
2
25 May '10
Currently all host audio backends are disabled if a VM is using VNC, in
favor of the QEMU VNC audio extension. Unfortunately no released VNC
client supports this extension, so users have no way of getting audio
to work if using VNC.
Add a new config option in qemu.conf which allows changing libvirt's
behavior, but keep the default intact.
v2: Fix doc typos, change name to vnc_allow_host_audio
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/qemu/qemu.conf | 10 ++++++++++
src/qemu/qemu_conf.c | 17 ++++++++++++-----
src/qemu/qemu_conf.h | 2 ++
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
index 3da332f..98a1176 100644
--- a/src/qemu/qemu.conf
+++ b/src/qemu/qemu.conf
@@ -168,3 +168,13 @@
# be assigned to guests.
#
# relaxed_acs_check = 1
+
+
+# QEMU implements an extension for providing audio over a VNC connection,
+# though if your VNC client does not support it, your only chance for getting
+# sound output is through regular audio backends. By default, libvirt will
+# disable all QEMU sound backends if using VNC, since they can cause
+# permissions issues. Enabling this option will make libvirtd honor the
+# QEMU_AUDIO_DRV environment variable when using VNC.
+#
+# vnc_allow_host_audio = 0
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 2755545..b4d8e74 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -351,6 +351,10 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
CHECK_TYPE ("relaxed_acs_check", VIR_CONF_LONG);
if (p) driver->relaxedACS = p->l;
+ p = virConfGetValue (conf, "vnc_allow_host_audio");
+ CHECK_TYPE ("vnc_allow_host_audio", VIR_CONF_LONG);
+ if (p) driver->vncAllowHostAudio = p->l;
+
virConfFree (conf);
return 0;
}
@@ -4399,12 +4403,15 @@ int qemudBuildCommandLine(virConnectPtr conn,
ADD_ARG_LIT(def->graphics[0]->data.vnc.keymap);
}
- /* QEMU implements a VNC extension for providing audio, so we
- * set the audio backend to none, to prevent it opening the
- * host OS audio devices since that causes security issues
- * and is non-sensical when using VNC.
+ /* Unless user requested it, set the audio backend to none, to
+ * prevent it opening the host OS audio devices, since that causes
+ * security issues and might not work when using VNC.
*/
- ADD_ENV_LIT("QEMU_AUDIO_DRV=none");
+ if (driver->vncAllowHostAudio) {
+ ADD_ENV_COPY("QEMU_AUDIO_DRV");
+ } else {
+ ADD_ENV_LIT("QEMU_AUDIO_DRV=none");
+ }
} else if ((def->ngraphics == 1) &&
def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
char *xauth = NULL;
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 8fd8d79..7fb4de5 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -138,6 +138,8 @@ struct qemud_driver {
unsigned int relaxedACS : 1;
+ unsigned int vncAllowHostAudio : 1;
+
virCapsPtr caps;
/* An array of callbacks */
--
1.6.6.1
2
2
[libvirt] [v5 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman 25 May '10
by Scott Feldman 25 May '10
25 May '10
From: Scott Feldman <scofeldm(a)cisco.com>
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's three posted patches:
[PATCH v9] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
[PATCH v3] Add host UUID (to libvirt capabilities)
Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.
Changes from v4 to v5:
- [David Allan] move getPhysfn inside IFLA_VF_PORT_MAX to avoid compiler
warning when latest if_link.h isn't available
Changes from v3 to v4:
- move from Stafan's 802.1Qb{g|h} XML v8 to v9
- move hostuuid and vf index calcs to inside doPortProfileOp8021Qbh
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
---
configure.ac | 16 +
src/qemu/qemu_conf.c | 2
src/qemu/qemu_driver.c | 4
src/util/macvtap.c | 764 +++++++++++++++++++++++++++++++++++++++++++++++-
src/util/macvtap.h | 1
5 files changed, 771 insertions(+), 16 deletions(-)
diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 111fa6e..95d4c1a 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1505,7 +1505,7 @@ qemudPhysIfaceConnect(virConnectPtr conn,
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
}
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f02bf3b..f1a0d0e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3679,7 +3679,7 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver,
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(net->ifname,
+ delMacvtap(net->ifname, net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
}
#endif
@@ -8369,7 +8369,7 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
#if WITH_MACVTAP
if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
- delMacvtap(detach->ifname,
+ delMacvtap(detach->ifname, detach->data.direct.linkdev,
&detach->data.direct.virtPortProfile);
#endif
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 5cbd02b..8e4bea3 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,15 +61,23 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,156 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+ char **respbuf, int *respbuflen, long to_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ int rcvoffset = 0;
+ ssize_t nbytes;
+ int n;
+ struct timeval tv = {
+ .tv_sec = to_usecs / MICROSEC_PER_SEC,
+ .tv_usec = to_usecs % MICROSEC_PER_SEC,
+ };
+ fd_set rfds;
+ bool gotvalid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!gotvalid) {
+ rcvoffset = 0;
+ while (1) {
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcvoffset += nbytes;
+ break;
+ }
+ *respbuflen = rcvoffset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (-err->error != EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ gotvalid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ gotvalid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+# endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +365,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -655,8 +818,8 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile,
- -1,
vmuuid) != 0) {
rc = -1;
goto link_del_exit;
@@ -689,6 +852,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ linkdev,
virtPortProfile);
link_del_exit:
@@ -701,6 +865,7 @@ link_del_exit:
/**
* 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
@@ -709,24 +874,589 @@ link_del_exit:
*/
void
delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileDefPtr virtPortProfile)
{
if (ifname) {
disassociatePortProfileId(ifname,
+ linkdev,
virtPortProfile);
link_del(ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, struct nlattr **tb, char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[256] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ struct ifinfomsg i = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ goto buffer_too_small;
+
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ 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
+doPortProfileOpSetLink(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[256];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta, *vfports, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* beging nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of ifindex %d"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 80;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifindex,
+ profileId,
+ portVsi,
+ instanceId,
+ hostUUID,
+ vf,
+ op);
+
+ if (rc != 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed."));
+ return rc;
+ }
+
+ if (!multicast) {
+ while (--repeats) {
+ rc = link_dump(ifindex, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ 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 ifindex %d"),
+ status, ifindex);
+ rc = 1;
+ break;
+ }
+ }
+ usleep(125000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+ if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("port-profile setlink timed out"));
+ rc = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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 multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ 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 ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ PORT_SELF_VF,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+# ifdef IFLA_VF_PORT_MAX
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+ return rc;
+}
+# endif /* IFLA_VF_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (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;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(ifname, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
+ if (ifaceGetIndex(true, physfndev, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ 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"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ hostuuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
*
* Associate a port on a swtich with a profile. This function
@@ -740,15 +1470,14 @@ delMacvtap(const char *ifname,
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid)
{
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -756,11 +1485,14 @@ associatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ vmuuid,
+ ASSOCIATE);
break;
}
@@ -772,6 +1504,7 @@ associatePortProfileId(const char *macvtap_ifname,
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @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
@@ -779,9 +1512,11 @@ associatePortProfileId(const char *macvtap_ifname,
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort)
{
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
@@ -791,13 +1526,18 @@ disassociatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+ NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index ae11c5c..35db31c 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -72,6 +72,7 @@ int openMacvtapTap(const char *ifname,
char **res_ifname);
void delMacvtap(const char *ifname,
+ const char *linkdev,
virVirtualPortProfileDefPtr virtPortProfile);
# endif /* WITH_MACVTAP */
4
4