[libvirt] [PATCHv2 2/2] qemu: change macvtap multicast list in response to NIC_RX_FILTER_CHANGED

akrowiak at linux.vnet.ibm.com akrowiak at linux.vnet.ibm.com
Mon Oct 13 13:52:18 UTC 2014


From: Tony Krowiak <akrowiak at linux.vnet.ibm.com>

This patch adds functionality to processNicRxFilterChangedEvent().
The old and new multicast lists are compared and the filters in
the macvtap are programmed to match the guest's filters.

Signed-off-by: Tony Krowiak <akrowiak at linux.vnet.ibm.com>
---
 src/qemu/qemu_driver.c |  150 ++++++++++++++++++++++++++++++++++++++++--------
 src/util/virmacaddr.c  |   32 +++-------
 src/util/virnetdev.c   |  149 ++++++++++++++++++++++++++---------------------
 3 files changed, 218 insertions(+), 113 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 7c9b1ab..cf1ae9c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4147,6 +4147,118 @@ processDeviceDeletedEvent(virQEMUDriverPtr driver,
 
 
 static void
+syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter,
+                       virNetDevRxFilterPtr hostFilter)
+{
+    char newMacStr[VIR_MAC_STRING_BUFLEN];
+
+    if (virMacAddrCmp(&hostFilter->mac, &guestFilter->mac)) {
+        virMacAddrFormat(&guestFilter->mac, newMacStr);
+
+        /* set new MAC address from guest to associated macvtap device */
+        if (virNetDevSetMAC(ifname, &guestFilter->mac)) {
+            VIR_WARN("Couldn't set new MAC address %s to device %s "
+                     "while responding to NIC_RX_FILTER_CHANGED",
+                     newMacStr, ifname);
+        } else {
+            VIR_DEBUG("device %s MAC address set to %s", ifname, newMacStr);
+        }
+    }
+}
+
+
+static void
+syncNicRxFilterGuestMulticast(char *ifname, virNetDevRxFilterPtr guestFilter,
+                              virNetDevRxFilterPtr hostFilter)
+{
+    size_t i, j;
+    bool found;
+    char macstr[VIR_MAC_STRING_BUFLEN];
+
+    for (i = 0; i < guestFilter->multicast.nTable; i++) {
+        found = false;
+
+        for (j = 0; j < hostFilter->multicast.nTable; j++) {
+            if (virMacAddrCmp(&guestFilter->multicast.table[i],
+                              &hostFilter->multicast.table[j]) == 0) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            virMacAddrFormat(&guestFilter->multicast.table[i], macstr);
+
+            if (virNetDevAddMulti(ifname, &guestFilter->multicast.table[i])) {
+                VIR_WARN("Couldn't add new multicast MAC address %s to "
+                         "device %s while responding to NIC_RX_FILTER_CHANGED",
+                         macstr, ifname);
+            } else {
+                VIR_DEBUG("Added multicast MAC %s to %s interface",
+                          macstr, ifname);
+            }
+        }
+    }
+}
+
+
+static void
+syncNicRxFilterHostMulticast(char *ifname, virNetDevRxFilterPtr guestFilter,
+                             virNetDevRxFilterPtr hostFilter)
+{
+    size_t i, j;
+    bool found;
+    char macstr[VIR_MAC_STRING_BUFLEN];
+
+    for (i = 0; i < hostFilter->multicast.nTable; i++) {
+        found = false;
+
+        for (j = 0; j < guestFilter->multicast.nTable; j++) {
+            if (virMacAddrCmp(&hostFilter->multicast.table[i],
+                              &guestFilter->multicast.table[j]) == 0) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            virMacAddrFormat(&hostFilter->multicast.table[i], macstr);
+
+            if (virNetDevDelMulti(ifname, &hostFilter->multicast.table[i])) {
+                VIR_WARN("Couldn't delete multicast MAC address %s from "
+                         "device %s while responding to NIC_RX_FILTER_CHANGED",
+                         macstr, ifname);
+            } else {
+                VIR_DEBUG("Deleted multicast MAC %s from %s interface",
+                          macstr, ifname);
+            }
+        }
+    }
+}
+
+
+static void
+syncNicRxFilterMulticast(char *ifname,
+                         virNetDevRxFilterPtr guestFilter,
+                         virNetDevRxFilterPtr hostFilter)
+{
+VIR_DEBUG("RXFILTER: guest multicast list:");
+if (guestFilter->multicast.nTable) {
+char addr[VIR_MAC_STRING_BUFLEN];
+for (size_t i = 0; i < guestFilter->multicast.nTable; i++) {
+VIR_DEBUG("RXFILTER:    %s", virMacAddrFormat(&guestFilter->multicast.table[i], addr));
+}
+if (hostFilter->multicast.nTable) {
+char addr[VIR_MAC_STRING_BUFLEN];
+for (size_t i = 0; i < hostFilter->multicast.nTable; i++) {
+VIR_DEBUG("RXFILTER:    %s", virMacAddrFormat(&hostFilter->multicast.table[i], addr));
+}
+}
+    syncNicRxFilterGuestMulticast(ifname, guestFilter, hostFilter);
+    syncNicRxFilterHostMulticast(ifname, guestFilter, hostFilter);
+}
+
+static void
 processNicRxFilterChangedEvent(virQEMUDriverPtr driver,
                                virDomainObjPtr vm,
                                char *devAlias)
@@ -4155,9 +4267,8 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver,
     qemuDomainObjPrivatePtr priv = vm->privateData;
     virDomainDeviceDef dev;
     virDomainNetDefPtr def;
-    virNetDevRxFilterPtr filter = NULL;
-    virMacAddr oldMAC;
-    char newMacStr[VIR_MAC_STRING_BUFLEN];
+    virNetDevRxFilterPtr guestFilter = NULL;
+    virNetDevRxFilterPtr hostFilter = NULL;
     int ret;
 
     VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s "
@@ -4202,37 +4313,27 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver,
               "device %s in domain %s", def->info.alias, vm->def->name);
 
     qemuDomainObjEnterMonitor(driver, vm);
-    ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &filter);
+    ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &guestFilter);
     qemuDomainObjExitMonitor(driver, vm);
     if (ret < 0)
         goto endjob;
 
-    virMacAddrFormat(&filter->mac, newMacStr);
-
     if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_DIRECT) {
 
-        /* For macvtap connections, set the macvtap device's MAC
-         * address to match that of the guest device.
-         */
-
-        if (virNetDevGetMAC(def->ifname, &oldMAC) < 0) {
-            VIR_WARN("Couldn't get current MAC address of device %s "
+        if (virNetDevGetRxFilter(def->ifname, &hostFilter)) {
+            VIR_WARN("Couldn't get current RX filter for device %s "
                      "while responding to NIC_RX_FILTER_CHANGED",
                      def->ifname);
             goto endjob;
         }
 
-        if (virMacAddrCmp(&oldMAC, &filter->mac)) {
-            /* set new MAC address from guest to associated macvtap device */
-            if (virNetDevSetMAC(def->ifname, &filter->mac) < 0) {
-                VIR_WARN("Couldn't set new MAC address %s to device %s "
-                         "while responding to NIC_RX_FILTER_CHANGED",
-                         newMacStr, def->ifname);
-            } else {
-                VIR_DEBUG("device %s MAC address set to %s",
-                          def->ifname, newMacStr);
-            }
-        }
+        /* For macvtap connections, set the following macvtap network device
+         * attributes to match those of the guest network device:
+         * - MAC address
+         * - Multicast MAC address table
+         */
+        syncNicRxFilterMacAddr(def->ifname, guestFilter, hostFilter);
+        syncNicRxFilterMulticast(def->ifname, guestFilter, hostFilter);
     }
 
  endjob:
@@ -4242,7 +4343,8 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver,
     ignore_value(qemuDomainObjEndJob(driver, vm));
 
  cleanup:
-    virNetDevRxFilterFree(filter);
+    virNetDevRxFilterFree(hostFilter);
+    virNetDevRxFilterFree(guestFilter);
     VIR_FREE(devAlias);
     virObjectUnref(cfg);
 }
diff --git a/src/util/virmacaddr.c b/src/util/virmacaddr.c
index ae5e5d2..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 };
@@ -208,31 +209,18 @@ virMacAddrFormat(const virMacAddr *addr,
  * Return 0 upon success, or -1 in case of error.
  */
 int
-virMacAddrParseHex(const char* str, virMacAddrPtr addr)
+virMacAddrParseHex(const char *str, virMacAddrPtr addr)
 {
-    if (strlen(str) != VIR_MAC_HEXLEN)
-        return -1;
-
-    size_t iaddr;
-    size_t istr;
-
-
-    for (istr = 0, iaddr = 0; iaddr < VIR_MAC_BUFLEN; istr += 2, iaddr++) {
-        unsigned int hex;
-
-        if (sscanf(&str[istr], "%02x", &hex) != 1)
-            break;
-
-        if (hex > UCHAR_MAX)
-            break;
-
-        addr->addr[iaddr] = hex;
-    }
+    size_t i;
 
-    if (istr == VIR_MAC_HEXLEN)
-        return 0;
+    if (strspn(str, "0123456789abcdefABCDEF") != VIR_MAC_HEXLEN ||
+        str[VIR_MAC_HEXLEN])
+        return -1;
 
-    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],
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 5e53f5f..296d94d 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -56,6 +56,8 @@
 
 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
@@ -2004,9 +2006,9 @@ int virNetDevAddMulti(const char *ifname,
                       virMacAddrPtr macaddr ATTRIBUTE_UNUSED)
 {
     char macstr[VIR_MAC_STRING_BUFLEN];
-    virReportSystemError(errno,
-                         _("Cannot add multicast MAC %s on '%s' interface"),
-                         virMacAddrFormat(macaddr, macstr), ifname);
+    virReportError(ENOSYS,
+                   _("Cannot add multicast MAC %s on '%s' interface"),
+                   virMacAddrFormat(macaddr, macstr), ifname);
     return -1;
 }
 #endif
@@ -2054,9 +2056,9 @@ int virNetDevDelMulti(const char *ifname,
                       virMacAddrPtr macaddr ATTRIBUTE_UNUSED)
 {
     char macstr[VIR_MAC_STRING_BUFLEN];
-    virReportSystemError(errno,
-                         _("Cannot delete multicast MAC %s on '%s' interface"),
-                         virMacAddrFormat(macaddr, macstr), ifname);
+    virReportError(ENOSYS,
+                   _("Cannot delete multicast MAC %s on '%s' interface"),
+                   virMacAddrFormat(macaddr, macstr), ifname);
     return -1;
 }
 #endif
@@ -2085,7 +2087,7 @@ static int virNetDevParseMcast(char *buf, virNetDevMcastEntryPtr mcast)
             case VIR_MCAST_INDEX_TOKEN_IDX:
                 if (virStrToLong_i(token, &endptr, 10, &num) < 0) {
                     virReportSystemError(EINVAL,
-                                         _("Failed to parse index from '%s'"),
+                                         _("Failed to parse interface index from '%s'"),
                                          buf);
                     return -1;
 
@@ -2098,7 +2100,7 @@ static int virNetDevParseMcast(char *buf, virNetDevMcastEntryPtr mcast)
                 if (virStrncpy(mcast->name, token, strlen(token),
                     VIR_MCAST_NAME_LEN) == NULL) {
                     virReportSystemError(EINVAL,
-                                         _("Failed to parse NIC name from '%s'"),
+                                         _("Failed to parse network device name from '%s'"),
                                          buf);
                     return -1;
                 }
@@ -2146,75 +2148,90 @@ static int virNetDevParseMcast(char *buf, virNetDevMcastEntryPtr mcast)
 }
 
 
+static void virNetDevMcastEntryListFree(size_t nentries,
+                                        virNetDevMcastEntryPtr *entries)
+{
+    size_t i;
+
+    if (entries) {
+        for (i = 0; i < nentries; i++)
+            VIR_FREE(entries[i]);
+
+        VIR_FREE(entries);
+    }
+}
+
+
 static int virNetDevGetMcast(const char *ifname,
                              virNetDevMcastPtr mcast)
 {
-    FILE *file;
-    const char *path = "/proc/net/dev_mcast";
-    char buf[256];
-    int ret = -1;
-    virNetDevMcastEntry entry;
+    char *cur = NULL;
+    char *buf = NULL;
+    char *next = NULL;
+    int ret = -1, len;
+    virNetDevMcastEntryPtr entry = NULL;
     virNetDevMcastEntryPtr *entries = NULL;
     size_t nentries = 0;
-    size_t entries_sz = 0;
-    size_t i;
     mcast->entries = NULL;
     mcast->nentries = 0;
 
-    file = fopen(path, "r");
+    /* Read entire multicast table into memory */
+    if ((len = virFileReadAll(PROC_NET_DEV_MCAST, MAX_MCAST_SIZE, &buf)) <= 0)
+        goto cleanup;
 
-    if (!file) {
-        virReportSystemError(errno,
-                             _("cannot open multicast address file %s"), path);
-        return -1;
-    }
+    cur = buf;
 
-    while (fgets(buf, sizeof(buf), file)) {
-        if (virNetDevParseMcast(buf, &entry) < 0) {
-            goto error;
+    while (cur) {
+VIR_DEBUG("RXFILTER: %s", cur);
+        if (!entry) {
+            if (VIR_ALLOC(entry))
+                goto cleanup;
         }
+VIR_DEBUG("RXFILTER: entry=%p", entry);
+        next = strchr(cur, '\n');
 
-        if (entry.global && STREQ(ifname, entry.name)) {
-            if (VIR_RESIZE_N(entries, entries_sz,
-                nentries, 1) < 0) {
-                virReportSystemError(ENOMEM,
-                                     _("Failed to resize multicast MAC address array:  "
-                                       "ptr=%p, alloc=%zu, count=%zu, add=1"),
-                                       entries, entries_sz, nentries);
-                goto error;
-            }
+        if (next) {
+            next++;
+        }
 
-            if (VIR_ALLOC(entries[nentries]) < 0) {
-                char addr[VIR_MAC_STRING_BUFLEN];
-                virReportSystemError(ENOMEM,
-                                     _("Failed to allocate storage for MAC address %s"),
-                                     virMacAddrFormat(&mcast->entries[nentries]->macaddr,
-                                     addr));
-                goto error;
-            }
+        if (virNetDevParseMcast(cur, entry)) {
+            goto cleanup;
+        }
 
-            memcpy(entries[nentries++], &entry,
-                   sizeof(virNetDevMcastEntry));
+        /* Only return global multicast MAC addresses for
+         * specified interface */
+        if (entry->global && STREQ(ifname, entry->name)) {
+            if (VIR_APPEND_ELEMENT(entries, nentries, entry) < 0)
+                 goto cleanup;
+
+            entry = NULL;
+VIR_DEBUG("RXFILTER: entries[%lu]=%p", nentries-1, entries[nentries-1]);
+        } else if (next) {
+            memset(entry, 0, sizeof(virNetDevMcastEntry));
+        } else {
+            VIR_FREE(entry);
         }
 
-        memset(buf, 0, sizeof(buf));
-        memset(&entry, 0, sizeof(virNetDevMcastEntry));
+        cur = next;
     }
 
-
     mcast->nentries = nentries;
     mcast->entries = entries;
+if (nentries) {
+VIR_DEBUG("RXFILTER: entries:");
+char addr[VIR_MAC_STRING_BUFLEN];
+for (size_t i = 0; i < nentries; i++)
+VIR_DEBUG("RXFILTER:     %d  %s  %d  %d  %s",
+entries[i]->index, entries[i]->name, entries[i]->users, entries[i]->global,
+virMacAddrFormat(&entries[i]->macaddr, addr));
+}
     ret = 0;
+ cleanup:
+    if (ret < 0) {
+        virNetDevMcastEntryListFree(nentries, entries);
 
- error:
-    VIR_FORCE_FCLOSE(file);
-
-    if ((ret < 0) && (nentries > 0)) {
-        for (i = 0; i < nentries; i++) {
-            VIR_FREE(entries[i]);
-        }
-
-        VIR_FREE(entries);
+        if (entry)
+            VIR_FREE(entry);
     }
 
     return ret;
@@ -2231,30 +2248,33 @@ VIR_ENUM_IMPL(virNetDevRxFilterMode,
 static int virNetDevGetMulticastTable(const char *ifname,
                                       virNetDevRxFilterPtr filter)
 {
-    int i;
+    size_t i;
     int ret = -1;
     virNetDevMcast mcast;
     filter->multicast.nTable = 0;
     filter->multicast.table = NULL;
 
     if (virNetDevGetMcast(ifname, &mcast) < 0)
-        goto error;
+        goto cleanup;
 
     if (mcast.nentries > 0) {
         if (VIR_ALLOC_N(filter->multicast.table, mcast.nentries))
-            goto error;
+            goto cleanup;
 
         for (i = 0; i < mcast.nentries; i++) {
             virMacAddrSet(&filter->multicast.table[i],
                           &mcast.entries[i]->macaddr);
+char addr[VIR_MAC_STRING_BUFLEN];
+VIR_DEBUG("RXFILTER: filter->multicast.table[%lu]=%s", i, virMacAddrFormat(&filter->multicast.table[i], addr));
         }
 
         filter->multicast.nTable = mcast.nentries;
     }
 
     ret = 0;
+ cleanup:
+    virNetDevMcastEntryListFree(mcast.nentries, mcast.entries);
 
- error:
     return ret;
 }
 
@@ -2298,21 +2318,16 @@ int virNetDevGetRxFilter(const char *ifname,
     int ret = -1;
     virNetDevRxFilterPtr fil = virNetDevRxFilterNew();
 
-    if (!fil) {
-        virReportSystemError(ENOMEM,
-                             _("Failed to allocate filter for %s interface"),
-                             ifname);
-
-    }
+    if (!fil)
+        goto cleanup;
 
     if (virNetDevGetMAC(ifname, &fil->mac))
-            goto cleanup;
+        goto cleanup;
 
     if (virNetDevGetMulticastTable(ifname, fil))
         goto cleanup;
 
     ret = 0;
-
  cleanup:
     if (ret < 0) {
         virNetDevRxFilterFree(fil);
-- 
1.7.1




More information about the libvir-list mailing list