From: Tony Krowiak <akrowiak(a)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(a)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