From 8a337b676f90040f96ef195a70262c26aec36019 Mon Sep 17 00:00:00 2001 Message-Id: <8a337b676f90040f96ef195a70262c26aec36019@dist-git> From: Tony Krowiak Date: Mon, 3 Nov 2014 10:00:22 -0500 Subject: [PATCH] util: Functions to update host network device's multicast filter https://bugzilla.redhat.com/show_bug.cgi?id=848199 This patch provides the utility functions to needed to synchronize the changes made to a guest domain network device's multicast filter with the corresponding macvtap device's filter on the host: * Get/add/remove multicast MAC addresses * Get the macvtap device's RX filter list Signed-off-by: Tony Krowiak Signed-off-by: Laine Stump (cherry picked from commit cc0e8c244d080f56392278e836cc378ba848e7aa) Signed-off-by: Jiri Denemark --- src/libvirt_private.syms | 4 + src/util/virmacaddr.c | 25 ++++ src/util/virmacaddr.h | 4 + src/util/virnetdev.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 10 ++ 5 files changed, 372 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e2aae21..e7eba7e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1568,13 +1568,16 @@ virMacAddrIsBroadcastRaw; virMacAddrIsMulticast; virMacAddrIsUnicast; virMacAddrParse; +virMacAddrParseHex; virMacAddrSet; virMacAddrSetRaw; # util/virnetdev.h +virNetDevAddMulti; virNetDevAddRoute; virNetDevClearIPv4Address; +virNetDevDelMulti; virNetDevExists; virNetDevGetIndex; virNetDevGetIPv4Address; @@ -1582,6 +1585,7 @@ virNetDevGetLinkInfo; virNetDevGetMAC; virNetDevGetMTU; virNetDevGetPhysicalFunction; +virNetDevGetRxFilter; virNetDevGetVirtualFunctionIndex; virNetDevGetVirtualFunctionInfo; virNetDevGetVirtualFunctions; diff --git a/src/util/virmacaddr.c b/src/util/virmacaddr.c index ebd1182..612a409 100644 --- a/src/util/virmacaddr.c +++ b/src/util/virmacaddr.c @@ -29,6 +29,7 @@ #include "c-ctype.h" #include "virmacaddr.h" #include "virrandom.h" +#include "virutil.h" static const unsigned char virMacAddrBroadcastAddrRaw[VIR_MAC_BUFLEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -198,6 +199,30 @@ virMacAddrFormat(const virMacAddr *addr, return str; } +/** + * virMacAddrParseHex: + * @str: string hexadecimal representation of MAC address, e.g., "F801EFCE3aCB" + * @addr: 6-byte MAC address + * + * Parse the hexadecimal representation of a MAC address + * + * Return 0 upon success, or -1 in case of error. + */ +int +virMacAddrParseHex(const char *str, virMacAddrPtr addr) +{ + size_t i; + + if (strspn(str, "0123456789abcdefABCDEF") != VIR_MAC_HEXLEN || + str[VIR_MAC_HEXLEN]) + return -1; + + for (i = 0; i < VIR_MAC_BUFLEN; i++) + addr->addr[i] = (virHexToBin(str[2 * i]) << 4 | + virHexToBin(str[2 * i + 1])); + return 0; +} + void virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN], virMacAddrPtr addr) { diff --git a/src/util/virmacaddr.h b/src/util/virmacaddr.h index 49efc36..ae26867 100644 --- a/src/util/virmacaddr.h +++ b/src/util/virmacaddr.h @@ -27,6 +27,7 @@ # include "internal.h" # define VIR_MAC_BUFLEN 6 +# define VIR_MAC_HEXLEN (VIR_MAC_BUFLEN * 2) # define VIR_MAC_PREFIX_BUFLEN 3 # define VIR_MAC_STRING_BUFLEN (VIR_MAC_BUFLEN * 3) @@ -50,6 +51,9 @@ void virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN], virMacAddrPtr addr); int virMacAddrParse(const char* str, virMacAddrPtr addr) ATTRIBUTE_RETURN_CHECK; +int virMacAddrParseHex(const char* str, + virMacAddrPtr addr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; bool virMacAddrIsUnicast(const virMacAddr *addr); bool virMacAddrIsMulticast(const virMacAddr *addr); bool virMacAddrIsBroadcastRaw(const unsigned char s[VIR_MAC_BUFLEN]); diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 36b5a49..381031a 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -56,6 +56,35 @@ VIR_LOG_INIT("util.netdev"); +#define PROC_NET_DEV_MCAST "/proc/net/dev_mcast" +#define MAX_MCAST_SIZE 50*14336 +#define VIR_MCAST_NAME_LEN (IFNAMSIZ + 1) +#define VIR_MCAST_INDEX_TOKEN_IDX 0 +#define VIR_MCAST_NAME_TOKEN_IDX 1 +#define VIR_MCAST_USERS_TOKEN_IDX 2 +#define VIR_MCAST_GLOBAL_TOKEN_IDX 3 +#define VIR_MCAST_ADDR_TOKEN_IDX 4 +#define VIR_MCAST_NUM_TOKENS 5 +#define VIR_MCAST_TOKEN_DELIMS " \n" +#define VIR_MCAST_ADDR_LEN (VIR_MAC_HEXLEN + 1) + +typedef struct _virNetDevMcastEntry virNetDevMcastEntry; +typedef virNetDevMcastEntry *virNetDevMcastEntryPtr; +struct _virNetDevMcastEntry { + int index; + char name[VIR_MCAST_NAME_LEN]; + int users; + bool global; + virMacAddr macaddr; +}; + +typedef struct _virNetDevMcastList virNetDevMcastList; +typedef virNetDevMcastList *virNetDevMcastListPtr; +struct _virNetDevMcastList { + size_t nentries; + virNetDevMcastEntryPtr *entries; +}; + #if defined(HAVE_STRUCT_IFREQ) static int virNetDevSetupControlFull(const char *ifname, struct ifreq *ifr, @@ -1935,6 +1964,238 @@ virNetDevGetLinkInfo(const char *ifname, #endif /* defined(__linux__) */ +#if defined(SIOCADDMULTI) && defined(HAVE_STRUCT_IFREQ) +/** + * virNetDevAddMulti: + * @ifname: interface name to which to add multicast MAC address + * @macaddr: MAC address + * + * This function adds the @macaddr to the multicast list for a given interface + * @ifname. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevAddMulti(const char *ifname, + virMacAddrPtr macaddr) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + virMacAddrGetRaw(macaddr, (unsigned char *)ifr.ifr_hwaddr.sa_data); + + if (ioctl(fd, SIOCADDMULTI, &ifr) < 0) { + char macstr[VIR_MAC_STRING_BUFLEN]; + virReportSystemError(errno, + _("Cannot add multicast MAC %s on '%s' interface"), + virMacAddrFormat(macaddr, macstr), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevAddMulti(const char *ifname ATTRIBUTE_UNUSED, + virMacAddrPtr macaddr ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Unable to add address to interface " + "multicast list on this platform")); + return -1; +} +#endif + +#if defined(SIOCDELMULTI) && defined(HAVE_STRUCT_IFREQ) +/** + * virNetDevDelMulti: + * @ifname: interface name from which to delete the multicast MAC address + * @macaddr: MAC address + * + * This function deletes the @macaddr from the multicast list for a given + * interface @ifname. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevDelMulti(const char *ifname, + virMacAddrPtr macaddr) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + virMacAddrGetRaw(macaddr, (unsigned char *)ifr.ifr_hwaddr.sa_data); + + if (ioctl(fd, SIOCDELMULTI, &ifr) < 0) { + char macstr[VIR_MAC_STRING_BUFLEN]; + virReportSystemError(errno, + _("Cannot add multicast MAC %s on '%s' interface"), + virMacAddrFormat(macaddr, macstr), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevDelMulti(const char *ifname ATTRIBUTE_UNUSED, + virMacAddrPtr macaddr ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Unable to delete address from interface " + "multicast list on this platform")); + return -1; +} +#endif + +static int virNetDevParseMcast(char *buf, virNetDevMcastEntryPtr mcast) +{ + int ifindex; + int num; + char *next; + char *token; + char *saveptr; + char *endptr; + + for (ifindex = 0, next = buf; ifindex < VIR_MCAST_NUM_TOKENS; ifindex++, + next = NULL) { + token = strtok_r(next, VIR_MCAST_TOKEN_DELIMS, &saveptr); + + if (token == NULL) { + virReportSystemError(EINVAL, + _("failed to parse multicast address from '%s'"), + buf); + return -1; + } + + switch (ifindex) { + case VIR_MCAST_INDEX_TOKEN_IDX: + if (virStrToLong_i(token, &endptr, 10, &num) < 0) { + virReportSystemError(EINVAL, + _("Failed to parse interface index from '%s'"), + buf); + return -1; + + } + mcast->index = num; + break; + case VIR_MCAST_NAME_TOKEN_IDX: + if (virStrncpy(mcast->name, token, strlen(token), + VIR_MCAST_NAME_LEN) == NULL) { + virReportSystemError(EINVAL, + _("Failed to parse network device name from '%s'"), + buf); + return -1; + } + break; + case VIR_MCAST_USERS_TOKEN_IDX: + if (virStrToLong_i(token, &endptr, 10, &num) < 0) { + virReportSystemError(EINVAL, + _("Failed to parse users from '%s'"), + buf); + return -1; + + } + mcast->users = num; + break; + case VIR_MCAST_GLOBAL_TOKEN_IDX: + if (virStrToLong_i(token, &endptr, 10, &num) < 0) { + virReportSystemError(EINVAL, + _("Failed to parse users from '%s'"), + buf); + return -1; + + } + mcast->global = num; + break; + case VIR_MCAST_ADDR_TOKEN_IDX: + if (virMacAddrParseHex((const char*)token, + &mcast->macaddr) < 0) { + virReportSystemError(EINVAL, + _("Failed to parse MAC address from '%s'"), + buf); + } + break; + default: + break; + } + } + return 0; +} + + +static void virNetDevMcastListClear(virNetDevMcastListPtr mcast) +{ + size_t i; + + for (i = 0; i < mcast->nentries; i++) + VIR_FREE(mcast->entries[i]); + VIR_FREE(mcast->entries); + mcast->nentries = 0; +} + + +static int virNetDevGetMcastList(const char *ifname, + virNetDevMcastListPtr mcast) +{ + char *cur = NULL; + char *buf = NULL; + char *next = NULL; + int ret = -1, len; + virNetDevMcastEntryPtr entry = NULL; + + mcast->entries = NULL; + mcast->nentries = 0; + + /* Read entire multicast table into memory */ + if ((len = virFileReadAll(PROC_NET_DEV_MCAST, MAX_MCAST_SIZE, &buf)) <= 0) + goto cleanup; + + cur = buf; + while (cur) { + if (!entry && VIR_ALLOC(entry) < 0) + goto cleanup; + + next = strchr(cur, '\n'); + if (next) + next++; + if (virNetDevParseMcast(cur, entry)) + goto cleanup; + + /* Only return global multicast MAC addresses for + * specified interface */ + if (entry->global && STREQ(ifname, entry->name)) { + if (VIR_APPEND_ELEMENT(mcast->entries, mcast->nentries, entry)) + goto cleanup; + } else { + memset(entry, 0, sizeof(virNetDevMcastEntry)); + } + cur = next && ((next - buf) < len) ? next : NULL; + } + + ret = 0; + cleanup: + if (ret < 0) + virNetDevMcastListClear(mcast); + + VIR_FREE(entry); + + return ret; +} + + VIR_ENUM_IMPL(virNetDevRxFilterMode, VIR_NETDEV_RX_FILTER_MODE_LAST, "none", @@ -1942,6 +2203,38 @@ VIR_ENUM_IMPL(virNetDevRxFilterMode, "all"); +static int virNetDevGetMulticastTable(const char *ifname, + virNetDevRxFilterPtr filter) +{ + size_t i; + int ret = -1; + virNetDevMcastList mcast; + filter->multicast.nTable = 0; + filter->multicast.table = NULL; + + if (virNetDevGetMcastList(ifname, &mcast) < 0) + goto cleanup; + + if (mcast.nentries > 0) { + if (VIR_ALLOC_N(filter->multicast.table, mcast.nentries) < 0) + goto cleanup; + + for (i = 0; i < mcast.nentries; i++) { + virMacAddrSet(&filter->multicast.table[i], + &mcast.entries[i]->macaddr); + } + + filter->multicast.nTable = mcast.nentries; + } + + ret = 0; + cleanup: + virNetDevMcastListClear(&mcast); + + return ret; +} + + virNetDevRxFilterPtr virNetDevRxFilterNew(void) { @@ -1964,3 +2257,39 @@ virNetDevRxFilterFree(virNetDevRxFilterPtr filter) VIR_FREE(filter); } } + + +/** + * virNetDevGetRxFilter: + * This function supplies the RX filter list for a given device interface + * + * @ifname: Name of the interface + * @filter: The RX filter list + * + * Returns 0 or -1 on failure. + */ +int virNetDevGetRxFilter(const char *ifname, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!fil) + goto cleanup; + + if (virNetDevGetMAC(ifname, &fil->mac)) + goto cleanup; + + if (virNetDevGetMulticastTable(ifname, fil)) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) { + virNetDevRxFilterFree(fil); + fil = NULL; + } + + *filter = fil; + return ret; +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 2a6e67d..ac4beff 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -189,5 +189,15 @@ int virNetDevGetLinkInfo(const char *ifname, virNetDevRxFilterPtr virNetDevRxFilterNew(void) ATTRIBUTE_RETURN_CHECK; void virNetDevRxFilterFree(virNetDevRxFilterPtr filter); +int virNetDevGetRxFilter(const char *ifname, + virNetDevRxFilterPtr *filter) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +int virNetDevAddMulti(const char *ifname, + virMacAddrPtr macaddr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevDelMulti(const char *ifname, + virMacAddrPtr macaddr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; #endif /* __VIR_NETDEV_H__ */ -- 2.1.3