[libvirt] [PATCHv7 0/2] util: Add netlink event handling code

This is hopefully the final spin of these patches, It squashes in all the changes from: https://www.redhat.com/archives/libvir-list/2012-February/msg01103.html as well as: https://www.redhat.com/archives/libvir-list/2012-February/msg01173.html Dirk, please test these out and let me know if they're okay to push. Thanks!

From: "D. Herrendoerfer" <d.herrendoerfer@herrendoerfer.name> This code adds a netlink event interface to libvirt. It is based upon the event_poll code and makes use of it. An event is generated for each netlink message sent to the libvirt pid. Signed-off-by: D. Herrendoerfer <d.herrendoerfer@herrendoerfer.name> --- daemon/libvirtd.c | 8 + src/libvirt_private.syms | 6 + src/util/virnetlink.c | 464 +++++++++++++++++++++++++++++++++++++++++++++- src/util/virnetlink.h | 33 ++++ 4 files changed, 506 insertions(+), 5 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index b1b542b..ca8074d 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -47,6 +47,7 @@ #include "conf.h" #include "memory.h" #include "conf.h" +#include "virnetlink.h" #include "virnetserver.h" #include "threads.h" #include "remote.h" @@ -1598,6 +1599,12 @@ int main(int argc, char **argv) { goto cleanup; } + /* Register the netlink event service */ + if (virNetlinkEventServiceStart() < 0) { + ret = VIR_DAEMON_ERR_NETWORK; + goto cleanup; + } + /* Run event loop. */ virNetServerRun(srv); @@ -1607,6 +1614,7 @@ int main(int argc, char **argv) { 0, "shutdown", NULL); cleanup: + virNetlinkEventServiceStop(); virNetServerProgramFree(remoteProgram); virNetServerProgramFree(qemuProgram); virNetServerClose(srv); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b9baf9a..ce4b7f7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1271,6 +1271,12 @@ virNetDevVPortProfileOpTypeToString; #virnetlink.h virNetlinkCommand; +virNetlinkEventServiceIsRunning; +virNetlinkEventServiceStop; +virNetlinkEventServiceStart; +virNetlinkEventAddClient; +virNetlinkEventRemoveClient; + # virnetmessage.h diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c index d03d171..fd6f751 100644 --- a/src/util/virnetlink.c +++ b/src/util/virnetlink.c @@ -35,7 +35,10 @@ #include <sys/types.h> #include "virnetlink.h" +#include "logging.h" #include "memory.h" +#include "threads.h" +#include "virmacaddr.h" #include "virterror_internal.h" #define VIR_FROM_THIS VIR_FROM_NET @@ -46,6 +49,48 @@ #define NETLINK_ACK_TIMEOUT_S 2 +#if defined(__linux__) && defined(HAVE_LIBNL) +/* State for a single netlink event handle */ +struct virNetlinkEventHandle { + int watch; + virNetlinkEventHandleCallback handleCB; + virNetlinkEventRemoveCallback removeCB; + void *opaque; + unsigned char macaddr[VIR_MAC_BUFLEN]; + int deleted; +}; + +typedef struct _virNetlinkEventSrvPrivate virNetlinkEventSrvPrivate; +typedef virNetlinkEventSrvPrivate *virNetlinkEventSrvPrivatePtr; +struct _virNetlinkEventSrvPrivate { + /*Server*/ + virMutex lock; + int eventwatch; + int netlinkfd; + struct nl_handle *netlinknh; + /*Events*/ + int handled; + size_t handlesCount; + size_t handlesAlloc; + struct virNetlinkEventHandle *handles; +}; + +enum virNetlinkDeleteMode { + VIR_NETLINK_HANDLE_VALID, + VIR_NETLINK_HANDLE_DELETED, +}; + +/* Unique ID for the next netlink watch to be registered */ +static int nextWatch = 1; + +/* Allocate extra slots for virEventPollHandle/virEventPollTimeout + records in this multiple */ +# define NETLINK_EVENT_ALLOC_EXTENT 10 + +static virNetlinkEventSrvPrivatePtr server = NULL; + +/* Function definitions */ + /** * virNetlinkCommand: * @nlmsg: pointer to netlink message @@ -58,7 +103,6 @@ * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ -#if defined(__linux__) && defined(HAVE_LIBNL) int virNetlinkCommand(struct nl_msg *nl_msg, unsigned char **respbuf, unsigned int *respbuflen, int nl_pid) @@ -89,7 +133,7 @@ int virNetlinkCommand(struct nl_msg *nl_msg, virReportSystemError(errno, "%s", _("cannot connect to netlink socket")); rc = -1; - goto err_exit; + goto error; } nlmsg_set_dst(nl_msg, &nladdr); @@ -101,7 +145,7 @@ int virNetlinkCommand(struct nl_msg *nl_msg, virReportSystemError(errno, "%s", _("cannot send to netlink socket")); rc = -1; - goto err_exit; + goto error; } fd = nl_socket_get_fd(nlhandle); @@ -118,7 +162,7 @@ int virNetlinkCommand(struct nl_msg *nl_msg, virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); rc = -1; - goto err_exit; + goto error; } *respbuflen = nl_recv(nlhandle, &nladdr, respbuf, NULL); @@ -127,7 +171,7 @@ int virNetlinkCommand(struct nl_msg *nl_msg, "%s", _("nl_recv failed")); rc = -1; } -err_exit: +error: if (rc == -1) { VIR_FREE(*respbuf); *respbuf = NULL; @@ -138,6 +182,345 @@ err_exit: return rc; } +static void +virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) +{ + virMutexLock(&driver->lock); +} + +static void +virNetlinkEventServerUnlock(virNetlinkEventSrvPrivatePtr driver) +{ + virMutexUnlock(&driver->lock); +} + +/** + * virNetlinkEventRemoveClientPrimitive: + * + * @i: index of the client to remove from the table + * + * This static function does the low level removal of a client from + * the table once its index is known, including calling the remove + * callback (which usually will free resources required by the + * handler). The event server lock *must* be locked before calling + * this function. + * + * assumes success, returns nothing. + */ +static void +virNetlinkEventRemoveClientPrimitive(size_t i) +{ + virNetlinkEventRemoveCallback removeCB = server->handles[i].removeCB; + + if (removeCB) { + (removeCB)(server->handles[i].watch, + server->handles[i].macaddr, + server->handles[i].opaque); + } + server->handles[i].deleted = VIR_NETLINK_HANDLE_DELETED; +} + +static void +virNetlinkEventCallback(int watch, + int fd ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED, + void *opaque) +{ + virNetlinkEventSrvPrivatePtr srv = opaque; + unsigned char *msg; + struct sockaddr_nl peer; + struct ucred *creds = NULL; + int i, length; + bool handled = false; + + length = nl_recv(srv->netlinknh, &peer, &msg, &creds); + + if (length == 0) + return; + if (length < 0) { + netlinkError(errno, + "%s", _("nl_recv returned with error")); + return; + } + + virNetlinkEventServerLock(srv); + + VIR_DEBUG("dispatching to max %d clients, called from event watch %d", + (int)srv->handlesCount, watch); + + for (i = 0; i < srv->handlesCount; i++) { + if (srv->handles[i].deleted != VIR_NETLINK_HANDLE_VALID) + continue; + + VIR_DEBUG("dispatching client %d.", i); + + (srv->handles[i].handleCB)(msg, length, &peer, &handled, + srv->handles[i].opaque); + } + + if (!handled) + VIR_DEBUG("event not handled."); + VIR_FREE(msg); + virNetlinkEventServerUnlock(srv); +} + +/** + * virNetlinkEventServiceStop: + * + * stop the monitor to receive netlink messages for libvirtd. + * This removes the netlink socket fd from the event handler. + * + * returns -1 if the monitor cannot be unregistered, 0 upon success + */ +int +virNetlinkEventServiceStop(void) +{ + virNetlinkEventSrvPrivatePtr srv = server; + int i; + + VIR_INFO("stopping netlink event service"); + + if (!server) + return 0; + + virNetlinkEventServerLock(srv); + nl_close(srv->netlinknh); + nl_handle_destroy(srv->netlinknh); + virEventRemoveHandle(srv->eventwatch); + + /* free any remaining clients on the list */ + for (i = 0; i < srv->handlesCount; i++) { + if (srv->handles[i].deleted == VIR_NETLINK_HANDLE_VALID) + virNetlinkEventRemoveClientPrimitive(i); + } + + server = 0; + virNetlinkEventServerUnlock(srv); + + virMutexDestroy(&srv->lock); + VIR_FREE(srv); + return 0; +} + +/** + * virNetlinkEventServiceIsRunning: + * + * returns if the netlink event service is running. + * + * returns 'true' if the service is running, 'false' if stopped. + */ +bool +virNetlinkEventServiceIsRunning(void) +{ + return (server != NULL); +} + +/** + * virNetlinkEventServiceStart: + * + * start a monitor to receive netlink messages for libvirtd. + * This registers a netlink socket with the event interface. + * + * returns -1 if the monitor cannot be registered, 0 upon success + */ +int +virNetlinkEventServiceStart(void) +{ + virNetlinkEventSrvPrivatePtr srv; + int fd; + int ret = -1; + + if (server) + return 0; + + VIR_INFO("starting netlink event service"); + + if (VIR_ALLOC(srv) < 0) { + virReportOOMError(); + goto error; + } + + if (virMutexInit(&srv->lock) < 0) + goto error; + + virNetlinkEventServerLock(srv); + + /* Allocate a new socket and get fd */ + srv->netlinknh = nl_handle_alloc(); + + if (!srv->netlinknh) { + netlinkError(errno, + "%s", _("cannot allocate nlhandle for virNetlinkEvent server")); + goto error_locked; + } + + if (nl_connect(srv->netlinknh, NETLINK_ROUTE) < 0) { + netlinkError(errno, + "%s", _("cannot connect to netlink socket")); + goto error_server; + } + + fd = nl_socket_get_fd(srv->netlinknh); + + if (fd < 0) { + netlinkError(errno, + "%s", _("cannot get netlink socket fd")); + goto error_server; + } + + if (nl_socket_set_nonblocking(srv->netlinknh)) { + netlinkError(errno, "%s", + _("cannot set netlink socket nonblocking")); + goto error_server; + } + + if ((srv->eventwatch = virEventAddHandle(fd, + VIR_EVENT_HANDLE_READABLE, + virNetlinkEventCallback, + srv, NULL)) < 0) { + netlinkError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to add netlink event handle watch")); + goto error_server; + } + + srv->netlinkfd = fd; + VIR_DEBUG("netlink event listener on fd: %i running", fd); + + ret = 0; + server = srv; + +error_server: + if (ret < 0) { + nl_close(srv->netlinknh); + nl_handle_destroy(srv->netlinknh); + } +error_locked: + virNetlinkEventServerUnlock(srv); + if (ret < 0) { + virMutexDestroy(&srv->lock); + VIR_FREE(srv); + } +error: + return ret; +} + +/** + * virNetlinkEventAddClient: + * + * @handleCB: callback to invoke when an event occurs + * @removeCB: callback to invoke when removing a client + * @opaque: user data to pass to callback + * @macaddr: macaddr to store with the data. Used to identify callers. May be null. + * + * register a callback for handling of netlink messages. The + * registered function receives the entire netlink message and + * may choose to act upon it. + * + * returns -1 if the file handle cannot be registered, number of monitor upon success + */ +int +virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB, + virNetlinkEventRemoveCallback removeCB, + void *opaque, const unsigned char *macaddr) +{ + int i, r, ret = -1; + virNetlinkEventSrvPrivatePtr srv = server; + + if (handleCB == NULL) + return -1; + + virNetlinkEventServerLock(srv); + + VIR_DEBUG("adding client: %d.", nextWatch); + + r = 0; + /* first try to re-use deleted free slots */ + for (i = 0; i < srv->handlesCount; i++) { + if (srv->handles[i].deleted == VIR_NETLINK_HANDLE_DELETED) { + r = i; + goto addentry; + } + } + /* Resize the eventLoop array if needed */ + if (srv->handlesCount == srv->handlesAlloc) { + VIR_DEBUG("Used %zu handle slots, adding at least %d more", + srv->handlesAlloc, NETLINK_EVENT_ALLOC_EXTENT); + if (VIR_RESIZE_N(srv->handles, srv->handlesAlloc, + srv->handlesCount, NETLINK_EVENT_ALLOC_EXTENT) < 0) { + goto error; + } + } + r = srv->handlesCount++; + +addentry: + srv->handles[r].watch = nextWatch; + srv->handles[r].handleCB = handleCB; + srv->handles[r].removeCB = removeCB; + srv->handles[r].opaque = opaque; + srv->handles[r].deleted = VIR_NETLINK_HANDLE_VALID; + if (macaddr) + memcpy(srv->handles[r].macaddr, macaddr, VIR_MAC_BUFLEN); + else + memset(srv->handles[r].macaddr, 0, VIR_MAC_BUFLEN); + + VIR_DEBUG("added client to loop slot: %d. with macaddr ptr=%p", r, macaddr); + + ret = nextWatch++; +error: + virNetlinkEventServerUnlock(srv); + return ret; +} + +/** + * virNetlinkEventRemoveClient: + * + * @watch: watch whose handle to remove + * @macaddr: macaddr whose handle to remove + * + * Unregister a callback from a netlink monitor. + * The handler function referenced will no longer receive netlink messages. + * Either watch or macaddr may be used, the other should be null. + * + * returns -1 if the file handle was not registered, 0 upon success + */ +int +virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr) +{ + int i; + int ret = -1; + virNetlinkEventSrvPrivatePtr srv = server; + + VIR_DEBUG("removing client watch=%d, mac=%p.", watch, macaddr); + + if (watch <= 0 && !macaddr) { + VIR_WARN("Ignoring invalid netlink client id: %d", watch); + return -1; + } + + virNetlinkEventServerLock(srv); + + for (i = 0; i < srv->handlesCount; i++) { + if (srv->handles[i].deleted != VIR_NETLINK_HANDLE_VALID) + continue; + + if ((watch && srv->handles[i].watch == watch) || + (!watch && + memcmp(macaddr, srv->handles[i].macaddr, VIR_MAC_BUFLEN) == 0)) { + + virNetlinkEventRemoveClientPrimitive(i); + VIR_DEBUG("removed client: %d by %s.", srv->handles[i].watch, + srv->handles[i].watch ? "index" : "mac"); + ret = 0; + goto cleanup; + } + } + VIR_DEBUG("no client found to remove."); + +cleanup: + virNetlinkEventServerUnlock(srv); + return ret; +} + #else int virNetlinkCommand(struct nl_msg *nl_msg ATTRIBUTE_UNUSED, @@ -154,4 +537,75 @@ int virNetlinkCommand(struct nl_msg *nl_msg ATTRIBUTE_UNUSED, return -1; } +/** + * stopNetlinkEventServer: stop the monitor to receive netlink messages for libvirtd + */ +int virNetlinkEventServiceStop(void) +{ + netlinkError(VIR_ERR_INTERNAL_ERROR, + "%s", +# if defined(__linux__) && !defined(HAVE_LIBNL) + _("virNetlinkEventServiceStop is not supported since libnl was not available")); +# endif + return 0; +} + +/** + * startNetlinkEventServer: start a monitor to receive netlink messages for libvirtd + */ +int virNetlinkEventServiceStart(void) +{ +# if defined(__linux__) && !defined(HAVE_LIBNL) + netlinkError(VIR_ERR_INTERNAL_ERROR, + "%s", + _("virNetlinkEventServiceStart is not supported since libnl was not available")); +# endif + return 0; +} + +/** + * virNetlinkEventServiceIsRunning: returns if the netlink event service is running. + */ +int virNetlinkEventServiceIsRunning(void) +{ +# if defined(__linux__) && !defined(HAVE_LIBNL) + netlinkError(VIR_ERR_INTERNAL_ERROR, + "%s", + _("virNetlinkEventServiceIsRunning is not supported since libnl was not available")); +# endif + return 0; +} + +/** + * virNetlinkEventAddClient: register a callback for handling of netlink messages + */ +int virNetlinkEventAddClient(virNetlinkEventHandleCallback cb, + virNetlinkEventRemoveCallback cb, + void *opaque, const unsigned char *macaddr) +{ + netlinkError(VIR_ERR_INTERNAL_ERROR, + "%s", +# if defined(__linux__) && !defined(HAVE_LIBNL) + _("virNetlinkEventServiceAddClient is not supported since libnl was not available")); +# else + _("virNetlinkEventServiceAddClient is not supported on non-linux platforms")); +# endif + return -1; +} + +/** + * virNetlinkEventRemoveClient: unregister a callback from a netlink monitor + */ +int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr) +{ + netlinkError(VIR_ERR_INTERNAL_ERROR, + "%s", +# if defined(__linux__) && !defined(HAVE_LIBNL) + _("virNetlinkEventRemoveClient is not supported since libnl was not available")); +# else + _("virNetlinkEventRemoveClient is not supported on non-linux platforms")); +# endif + return -1; +} + #endif /* __linux__ */ diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h index a70abb6..75533a3 100644 --- a/src/util/virnetlink.h +++ b/src/util/virnetlink.h @@ -21,6 +21,7 @@ # define __VIR_NETLINK_H__ # include "config.h" +# include "internal.h" # if defined(__linux__) && defined(HAVE_LIBNL) @@ -29,6 +30,7 @@ # else struct nl_msg; +struct sockaddr_nl; # endif /* __linux__ */ @@ -36,4 +38,35 @@ int virNetlinkCommand(struct nl_msg *nl_msg, unsigned char **respbuf, unsigned int *respbuflen, int nl_pid); +typedef void (*virNetlinkEventHandleCallback)(unsigned char *msg, int length, struct sockaddr_nl *peer, bool *handled, void *opaque); + +typedef void (*virNetlinkEventRemoveCallback)(int watch, const unsigned char *macaddr, void *opaque); + +/** + * stopNetlinkEventServer: stop the monitor to receive netlink messages for libvirtd + */ +int virNetlinkEventServiceStop(void); + +/** + * startNetlinkEventServer: start a monitor to receive netlink messages for libvirtd + */ +int virNetlinkEventServiceStart(void); + +/** + * virNetlinkEventServiceIsRunning: returns if the netlink event service is running. + */ +bool virNetlinkEventServiceIsRunning(void); + +/** + * virNetlinkEventAddClient: register a callback for handling of netlink messages + */ +int virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB, + virNetlinkEventRemoveCallback removeCB, + void *opaque, const unsigned char *macaddr); + +/** + * virNetlinkEventRemoveClient: unregister a callback from a netlink monitor + */ +int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr); + #endif /* __VIR_NETLINK_H__ */ -- 1.7.7.6

From: "D. Herrendoerfer" <d.herrendoerfer@herrendoerfer.name> Add de-association handling for 802.1qbg (vepa) via lldpad netlink messages. Also adds the possibility to perform an association request without waiting for a confirmation. Signed-off-by: D. Herrendoerfer <d.herrendoerfer@herrendoerfer.name> --- src/qemu/qemu_migration.c | 2 +- src/util/virnetdevmacvlan.c | 360 +++++++++++++++++++++++++++++++++++++- src/util/virnetdevvportprofile.c | 33 +++- src/util/virnetdevvportprofile.h | 3 +- 4 files changed, 383 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 7df2d4f..ea06e69 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2606,7 +2606,7 @@ qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) { net->mac, virDomainNetGetActualDirectDev(net), def->uuid, - VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH) < 0) + VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH, false) < 0) goto err_exit; } last_good_net = i; diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c index 25d0846..d36b326 100644 --- a/src/util/virnetdevmacvlan.c +++ b/src/util/virnetdevmacvlan.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2010-2011 Red Hat, Inc. - * Copyright (C) 2010 IBM Corporation + * Copyright (C) 2010-2012 IBM Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -46,7 +46,6 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode, VIR_NETDEV_MACVLAN_MODE_LAST, "passthrough") #if WITH_MACVTAP - # include <stdint.h> # include <stdio.h> # include <errno.h> @@ -68,6 +67,8 @@ VIR_ENUM_IMPL(virNetDevMacVLanMode, VIR_NETDEV_MACVLAN_MODE_LAST, # include "virfile.h" # include "virnetlink.h" # include "virnetdev.h" +# include "virpidfile.h" + # define MACVTAP_NAME_PREFIX "macvtap" # define MACVTAP_NAME_PATTERN "macvtap%d" @@ -445,6 +446,328 @@ static const uint32_t modeMap[VIR_NETDEV_MACVLAN_MODE_LAST] = { [VIR_NETDEV_MACVLAN_MODE_PASSTHRU] = MACVLAN_MODE_PASSTHRU, }; +/* Struct to hold the state and configuration of a 802.1qbg port */ +struct virNetlinkCallbackData { + char *cr_ifname; + virNetDevVPortProfilePtr virtPortProfile; + unsigned char *macaddress; + char *linkdev; + unsigned char *vmuuid; + enum virNetDevVPortProfileOp vmOp; + unsigned int linkState; +}; + +typedef struct virNetlinkCallbackData *virNetlinkCallbackDataPtr; + +# define INSTANCE_STRLEN 36 + +static int instance2str(const unsigned char *p, char *dst, size_t size) +{ + if (dst && size > INSTANCE_STRLEN) { + snprintf(dst, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + p[0], p[1], p[2], p[3], + p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + return 0; + } + return -1; +} + +# define LLDPAD_PID_FILE "/var/run/lldpad.pid" +# define VIRIP_PID_FILE "/var/run/virip.pid" + +/** + * virNetDevMacVLanVPortProfileCallback: + * + * @msg: The buffer containing the received netlink message + * @length: The length of the received netlink message. + * @peer: The netling sockaddr containing the peer information + * @handled: Contains information if the message has been replied to yet + * @opaque: Contains vital information regarding the associated vm an interface + * + * This function is called when a netlink message is received. The function + * reads the message and responds if it is pertinent to the running VMs + * network interface. + */ + +static void +virNetDevMacVLanVPortProfileCallback(unsigned char *msg, + int length, + struct sockaddr_nl *peer, + bool *handled, + void *opaque) +{ + struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] = { + [IFLA_VF_MAC] = {.minlen = sizeof(struct ifla_vf_mac), + .maxlen = sizeof(struct ifla_vf_mac)}, + [IFLA_VF_VLAN] = {.minlen = sizeof(struct ifla_vf_vlan), + .maxlen = sizeof(struct ifla_vf_vlan)}, + }; + + struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] = { + [IFLA_PORT_RESPONSE] = {.type = NLA_U16}, + }; + + struct nlattr *tb[IFLA_MAX + 1], *tb3[IFLA_PORT_MAX + 1], + *tb_vfinfo[IFLA_VF_MAX + 1], *tb_vfinfo_list; + + struct ifinfomsg ifinfo; + struct nlmsghdr *hdr; + void *data; + int rem; + char *ifname; + bool indicate = false; + virNetlinkCallbackDataPtr calld = opaque; + pid_t lldpad_pid = 0; + pid_t virip_pid = 0; + + hdr = (struct nlmsghdr *) msg; + data = nlmsg_data(hdr); + + /* Quickly decide if we want this or not */ + + if (virPidFileReadPath(LLDPAD_PID_FILE, &lldpad_pid) < 0) + return; + + ignore_value(virPidFileReadPath(VIRIP_PID_FILE, &virip_pid)); + + if (hdr->nlmsg_pid != lldpad_pid && hdr->nlmsg_pid != virip_pid) + return; /* we only care for lldpad and virip messages */ + if (hdr->nlmsg_type != RTM_SETLINK) + return; /* we only care for RTM_SETLINK */ + if (*handled) + return; /* if it has been handled - dont handle again */ + + /* DEBUG start */ + VIR_INFO("netlink message nl_sockaddr: %p len: %d", peer, length); + VIR_DEBUG("nlmsg_type = 0x%02x", hdr->nlmsg_type); + VIR_DEBUG("nlmsg_len = 0x%04x", hdr->nlmsg_len); + VIR_DEBUG("nlmsg_pid = %d", hdr->nlmsg_pid); + VIR_DEBUG("nlmsg_seq = 0x%08x", hdr->nlmsg_seq); + VIR_DEBUG("nlmsg_flags = 0x%04x", hdr->nlmsg_flags); + + VIR_DEBUG("lldpad pid = %d", lldpad_pid); + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_SETLINK: + case RTM_GETLINK: + VIR_DEBUG(" IFINFOMSG\n"); + VIR_DEBUG(" ifi_family = 0x%02x\n", + ((struct ifinfomsg *)data)->ifi_family); + VIR_DEBUG(" ifi_type = 0x%x\n", + ((struct ifinfomsg *)data)->ifi_type); + VIR_DEBUG(" ifi_index = %i\n", + ((struct ifinfomsg *)data)->ifi_index); + VIR_DEBUG(" ifi_flags = 0x%04x\n", + ((struct ifinfomsg *)data)->ifi_flags); + VIR_DEBUG(" ifi_change = 0x%04x\n", + ((struct ifinfomsg *)data)->ifi_change); + } + /* DEBUG end */ + + /* Parse netlink message assume a setlink with vfports */ + memcpy(&ifinfo, NLMSG_DATA(hdr), sizeof ifinfo); + VIR_DEBUG("family:%#x type:%#x index:%d flags:%#x change:%#x", + ifinfo.ifi_family, ifinfo.ifi_type, ifinfo.ifi_index, + ifinfo.ifi_flags, ifinfo.ifi_change); + if (nlmsg_parse(hdr, sizeof ifinfo, + (struct nlattr **)&tb, IFLA_MAX, NULL)) { + VIR_DEBUG("error parsing request..."); + return; + } + + if (tb[IFLA_VFINFO_LIST]) { + VIR_DEBUG("FOUND IFLA_VFINFO_LIST!"); + + nla_for_each_nested(tb_vfinfo_list, tb[IFLA_VFINFO_LIST], rem) { + if (nla_type(tb_vfinfo_list) != IFLA_VF_INFO) { + VIR_DEBUG("nested parsing of" + "IFLA_VFINFO_LIST failed."); + return; + } + if (nla_parse_nested(tb_vfinfo, IFLA_VF_MAX, + tb_vfinfo_list, ifla_vf_policy)) { + VIR_DEBUG("nested parsing of " + "IFLA_VF_INFO failed."); + return; + } + } + + if (tb_vfinfo[IFLA_VF_MAC]) { + struct ifla_vf_mac *mac = RTA_DATA(tb_vfinfo[IFLA_VF_MAC]); + unsigned char *m = mac->mac; + + VIR_DEBUG("IFLA_VF_MAC = %2x:%2x:%2x:%2x:%2x:%2x", + m[0], m[1], m[2], m[3], m[4], m[5]); + + if (memcmp(calld->macaddress, m, VIR_MAC_BUFLEN)) + { + /* Repeat the same check for a broadcast mac */ + int i; + + for (i = 0;i < VIR_MAC_BUFLEN; i++) { + if (calld->macaddress[i] != 0xff) { + VIR_DEBUG("MAC address match failed (wasn't broadcast)"); + return; + } + } + } + } + + if (tb_vfinfo[IFLA_VF_VLAN]) { + struct ifla_vf_vlan *vlan = RTA_DATA(tb_vfinfo[IFLA_VF_VLAN]); + + VIR_DEBUG("IFLA_VF_VLAN = %d", vlan->vlan); + } + } + + if (tb[IFLA_IFNAME]) { + ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); + VIR_DEBUG("IFLA_IFNAME = %s\n", ifname); + } + + if (tb[IFLA_OPERSTATE]) { + rem = *(unsigned short *)RTA_DATA(tb[IFLA_OPERSTATE]); + VIR_DEBUG("IFLA_OPERSTATE = %d\n", rem); + } + + if (tb[IFLA_VF_PORTS]) { + struct nlattr *tb_vf_ports; + + VIR_DEBUG("found IFLA_VF_PORTS\n"); + nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) { + + VIR_DEBUG("iterating\n"); + if (nla_type(tb_vf_ports) != IFLA_VF_PORT) { + VIR_DEBUG("not a IFLA_VF_PORT. skipping\n"); + continue; + } + if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports, + ifla_port_policy)) { + VIR_DEBUG("nested parsing on level 2" + " failed."); + } + if (tb3[IFLA_PORT_VF]) { + VIR_DEBUG("IFLA_PORT_VF = %d", + *(uint32_t *) (RTA_DATA(tb3[IFLA_PORT_VF]))); + } + if (tb3[IFLA_PORT_PROFILE]) { + VIR_DEBUG("IFLA_PORT_PROFILE = %s", + (char *) RTA_DATA(tb3[IFLA_PORT_PROFILE])); + } + + if (tb3[IFLA_PORT_VSI_TYPE]) { + struct ifla_port_vsi *pvsi; + int tid = 0; + + pvsi = (struct ifla_port_vsi *) + RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); + tid = ((pvsi->vsi_type_id[2] << 16) | + (pvsi->vsi_type_id[1] << 8) | + pvsi->vsi_type_id[0]); + + VIR_DEBUG("mgr_id: %d", pvsi->vsi_mgr_id); + VIR_DEBUG("type_id: %d", tid); + VIR_DEBUG("type_version: %d", + pvsi->vsi_type_version); + } + + if (tb3[IFLA_PORT_INSTANCE_UUID]) { + char instance[INSTANCE_STRLEN + 2]; + unsigned char *uuid; + + uuid = (unsigned char *) + RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); + instance2str(uuid, instance, sizeof(instance)); + VIR_DEBUG("IFLA_PORT_INSTANCE_UUID = %s\n", + instance); + } + + if (tb3[IFLA_PORT_REQUEST]) { + uint8_t req = *(uint8_t *) RTA_DATA(tb3[IFLA_PORT_REQUEST]); + VIR_DEBUG("IFLA_PORT_REQUEST = %d", req); + + if (req == PORT_REQUEST_DISASSOCIATE) { + VIR_DEBUG("Set dissaccociated."); + indicate = true; + } + } + + if (tb3[IFLA_PORT_RESPONSE]) { + VIR_DEBUG("IFLA_PORT_RESPONSE = %d\n", *(uint16_t *) + RTA_DATA(tb3[IFLA_PORT_RESPONSE])); + } + } + } + + if (!indicate) { + return; + } + + VIR_INFO("Re-send 802.1qbg associate request:"); + VIR_INFO(" if: %s", calld->cr_ifname); + VIR_INFO(" lf: %s", calld->linkdev); + VIR_INFO(" mac: %02x:%02x:%02x:%02x:%02x:%02x", + calld->macaddress[0], calld->macaddress[1], + calld->macaddress[2], calld->macaddress[3], + calld->macaddress[4], calld->macaddress[5]); + + ignore_value(virNetDevVPortProfileAssociate(calld->cr_ifname, + calld->virtPortProfile, + calld->macaddress, + calld->linkdev, + calld->vmuuid, + calld->vmOp, true)); + *handled = true; + return; +} + +/** + * virNetlinkCallbackDataFree + * + * @calld: pointer to a virNetlinkCallbackData object to free + * + * This function frees all the data associated with a virNetlinkCallbackData object + * as well as the object itself. If called with NULL, it does nothing. + * + * Returns nothing. + */ +static void +virNetlinkCallbackDataFree(virNetlinkCallbackDataPtr calld) +{ + if (calld) { + VIR_FREE(calld->cr_ifname); + VIR_FREE(calld->virtPortProfile); + VIR_FREE(calld->macaddress); + VIR_FREE(calld->linkdev); + VIR_FREE(calld->vmuuid); + } + VIR_FREE(calld); +} + +/** + * virNetDevMacVLanVPortProfileDestroyCallback: + * + * @watch: watch whose handle to remove + * @macaddr: macaddr whose handle to remove + * @opaque: Contains vital information regarding the associated vm + * + * This function is called when a netlink message handler is terminated. + * The function frees locally allocated data referenced in the opaque + * data, and the opaque object itself. + */ +static void +virNetDevMacVLanVPortProfileDestroyCallback(int watch ATTRIBUTE_UNUSED, + const unsigned char *macaddr ATTRIBUTE_UNUSED, + void *opaque) +{ + virNetlinkCallbackDataFree((virNetlinkCallbackDataPtr)opaque); +} + + /** * virNetDevMacVLanCreateWithVPortProfile: * Create an instance of a macvtap device and open its tap character @@ -485,6 +808,7 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname, int retries, do_retry = 0; uint32_t macvtapMode; const char *cr_ifname; + virNetlinkCallbackDataPtr calld = NULL; int ret; macvtapMode = modeMap[mode]; @@ -547,7 +871,7 @@ create_name: virtPortProfile, macaddress, linkdev, - vmuuid, vmOp) < 0) { + vmuuid, vmOp, false) < 0) { rc = -1; goto link_del_exit; } @@ -589,9 +913,36 @@ create_name: goto disassociate_exit; } + if (virNetlinkEventServiceIsRunning()) { + if (VIR_ALLOC(calld) < 0) + goto memory_error; + if ((calld->cr_ifname = strdup(cr_ifname)) == NULL) + goto memory_error; + if (VIR_ALLOC(calld->virtPortProfile) < 0) + goto memory_error; + memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile)); + if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0) + goto memory_error; + memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN); + if ((calld->linkdev = strdup(linkdev)) == NULL) + goto memory_error; + if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0) + goto memory_error; + memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN); + + calld->vmOp = vmOp; + + virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback, + virNetDevMacVLanVPortProfileDestroyCallback, + calld, macaddress); + } return rc; + memory_error: + virReportOOMError(); + virNetlinkCallbackDataFree(calld); + disassociate_exit: ignore_value(virNetDevVPortProfileDisassociate(cr_ifname, virtPortProfile, @@ -638,6 +989,9 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname, if (virNetDevMacVLanDelete(ifname) < 0) ret = -1; } + + virNetlinkEventRemoveClient(0, macaddr); + return ret; } diff --git a/src/util/virnetdevvportprofile.c b/src/util/virnetdevvportprofile.c index 67fd7bc..00fd123 100644 --- a/src/util/virnetdevvportprofile.c +++ b/src/util/virnetdevvportprofile.c @@ -650,7 +650,8 @@ virNetDevVPortProfileOpCommon(const char *ifname, int ifindex, const unsigned char *instanceId, const unsigned char *hostUUID, int32_t vf, - uint8_t op) + uint8_t op, + bool setlink_only) { int rc; unsigned char *recvbuf = NULL; @@ -675,6 +676,9 @@ virNetDevVPortProfileOpCommon(const char *ifname, int ifindex, return rc; } + if (setlink_only) /*for re-associations on existing links*/ + return 0; + while (--repeats >= 0) { rc = virNetDevVPortProfileLinkDump(NULL, ifindex, nltarget_kernel, tb, &recvbuf, virNetDevVPortProfileGetLldpadPid); @@ -751,7 +755,8 @@ static int virNetDevVPortProfileOp8021Qbg(const char *ifname, const unsigned char *macaddr, const virNetDevVPortProfilePtr virtPort, - enum virNetDevVPortProfileLinkOp virtPortOp) + enum virNetDevVPortProfileLinkOp virtPortOp, + bool setlink_only) { int rc = 0; int op = PORT_REQUEST_ASSOCIATE; @@ -804,7 +809,8 @@ virNetDevVPortProfileOp8021Qbg(const char *ifname, virtPort->u.virtPort8021Qbg.instanceID, NULL, vf, - op); + op, + setlink_only); err_exit: @@ -892,7 +898,8 @@ virNetDevVPortProfileOp8021Qbh(const char *ifname, (virtPortOp == VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR) ? PORT_REQUEST_PREASSOCIATE_RR - : PORT_REQUEST_ASSOCIATE); + : PORT_REQUEST_ASSOCIATE, + false); if (rc == -2) /* Association timed out, disassociate */ virNetDevVPortProfileOpCommon(NULL, ifindex, @@ -904,7 +911,8 @@ virNetDevVPortProfileOp8021Qbh(const char *ifname, NULL, NULL, vf, - PORT_REQUEST_DISASSOCIATE); + PORT_REQUEST_DISASSOCIATE, + false); break; case VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE: @@ -917,7 +925,8 @@ virNetDevVPortProfileOp8021Qbh(const char *ifname, NULL, NULL, vf, - PORT_REQUEST_DISASSOCIATE); + PORT_REQUEST_DISASSOCIATE, + false); break; default: @@ -938,6 +947,7 @@ err_exit: * @virtPort: pointer to the object holding port profile parameters * @vmuuid : the UUID of the virtual machine * @vmOp : The VM operation (i.e., create, no-op) + * @setlink_only : Only set the link - dont wait for the link to come up * * Associate a port on a swtich with a profile. This function * may notify a kernel driver or an external daemon to run @@ -954,7 +964,8 @@ virNetDevVPortProfileAssociate(const char *macvtap_ifname, const unsigned char *macvtap_macaddr, const char *linkdev, const unsigned char *vmuuid, - enum virNetDevVPortProfileOp vmOp) + enum virNetDevVPortProfileOp vmOp, + bool setlink_only) { int rc = 0; @@ -977,7 +988,8 @@ virNetDevVPortProfileAssociate(const char *macvtap_ifname, virtPort, (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START) ? VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE - : VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE); + : VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE, + setlink_only); break; case VIR_NETDEV_VPORT_PROFILE_8021QBH: @@ -1035,7 +1047,7 @@ virNetDevVPortProfileDisassociate(const char *macvtap_ifname, case VIR_NETDEV_VPORT_PROFILE_8021QBG: rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr, virtPort, - VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE); + VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE, false); break; case VIR_NETDEV_VPORT_PROFILE_8021QBH: @@ -1058,7 +1070,8 @@ int virNetDevVPortProfileAssociate(const char *macvtap_ifname ATTRIBUTE_UNUSED, const unsigned char *macvtap_macaddr ATTRIBUTE_UNUSED, const char *linkdev ATTRIBUTE_UNUSED, const unsigned char *vmuuid ATTRIBUTE_UNUSED, - enum virNetDevVPortProfileOp vmOp ATTRIBUTE_UNUSED) + enum virNetDevVPortProfileOp vmOp ATTRIBUTE_UNUSED, + bool setlink_only) { virReportSystemError(ENOSYS, "%s", _("Virtual port profile association not supported on this platform")); diff --git a/src/util/virnetdevvportprofile.h b/src/util/virnetdevvportprofile.h index ec2d06d..eb9f2b1 100644 --- a/src/util/virnetdevvportprofile.h +++ b/src/util/virnetdevvportprofile.h @@ -86,7 +86,8 @@ int virNetDevVPortProfileAssociate(const char *ifname, const unsigned char *macaddr, const char *linkdev, const unsigned char *vmuuid, - enum virNetDevVPortProfileOp vmOp) + enum virNetDevVPortProfileOp vmOp, + bool setlink_only) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK; -- 1.7.7.6
participants (1)
-
Laine Stump