diff --git a/po/POTFILES.in b/po/POTFILES.in
index bdff67906ad55bc6c76de32083b0b072264eabef..b0a1ed401becfc8730fd14287231a4d0c8fbf1d3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -213,6 +213,7 @@ src/util/virkeyfile.c
src/util/virlease.c
src/util/virlockspace.c
src/util/virlog.c
+src/util/virmacmap.c
src/util/virnetdev.c
src/util/virnetdevbandwidth.c
src/util/virnetdevbridge.c
diff --git a/src/Makefile.am b/src/Makefile.am
index d440548bef1a1d909e50b30d482f01fa102f43c1..a9106fa97803210ab7f0aafe28f7c981867f471f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -137,6 +137,7 @@ UTIL_SOURCES = \
util/virlockspace.c util/virlockspace.h \
util/virlog.c util/virlog.h \
util/virmacaddr.h util/virmacaddr.c \
+ util/virmacmap.h util/virmacmap.c \
util/virnetdev.h util/virnetdev.c \
util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
util/virnetdevbridge.h util/virnetdevbridge.c \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9189f56fe4af82ca79306b5b9e78e63bc7b244c8..6303dec8b314a5b7ff3f2928dd2215b3fc5d9c2e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1927,6 +1927,15 @@ virMacAddrSet;
virMacAddrSetRaw;
+# util/virmacmap.h
+virMacMapAdd;
+virMacMapDumpStr;
+virMacMapLookup;
+virMacMapNew;
+virMacMapRemove;
+virMacMapWriteFile;
+
+
# util/virnetdev.h
virNetDevAddMulti;
virNetDevDelMulti;
diff --git a/src/util/virmacmap.c b/src/util/virmacmap.c
new file mode 100644
index 0000000000000000000000000000000000000000..36c364e100d773225a01bef9d6fa359a4ba862cd
--- /dev/null
+++ b/src/util/virmacmap.c
@@ -0,0 +1,388 @@
+/*
+ * virmacmap.c: MAC address <-> Domain name mapping
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ * Authors:
+ * Michal Privoznik
+ */
+
+#include
+
+#include "virmacmap.h"
+#include "virobject.h"
+#include "virlog.h"
+#include "virjson.h"
+#include "virfile.h"
+#include "virhash.h"
+#include "virstring.h"
+#include "viralloc.h"
+
+#define VIR_FROM_THIS VIR_FROM_NETWORK
+
+VIR_LOG_INIT("util.virmacmap");
+
+/**
+ * VIR_MAC_MAP_FILE_SIZE_MAX:
+ *
+ * Macro providing the upper limit on the size of mac maps file
+ */
+#define VIR_MAC_MAP_FILE_SIZE_MAX (32 * 1024 * 1024)
+
+struct virMacMap {
+ virObjectLockable parent;
+
+ virHashTablePtr macs;
+};
+
+
+static virClassPtr virMacMapClass;
+
+
+static void
+virMacMapDispose(void *obj)
+{
+ virMacMapPtr mgr = obj;
+ virHashFree(mgr->macs);
+}
+
+
+static void
+virMacMapHashFree(void *payload, const void *name ATTRIBUTE_UNUSED)
+{
+ virStringListFree(payload);
+}
+
+
+static int virMacMapOnceInit(void)
+{
+ if (!(virMacMapClass = virClassNew(virClassForObjectLockable(),
+ "virMacMapClass",
+ sizeof(virMacMap),
+ virMacMapDispose)))
+ return -1;
+
+ return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virMacMap);
+
+
+static int
+virMacMapAddLocked(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac)
+{
+ int ret = -1;
+ const char **macsList = NULL;
+ char **newMacsList = NULL;
+
+ if ((macsList = virHashLookup(mgr->macs, domain)) &&
+ virStringListHasString(macsList, mac)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!(newMacsList = virStringListAdd(macsList, mac)) ||
+ virHashUpdateEntry(mgr->macs, domain, newMacsList) < 0)
+ goto cleanup;
+ newMacsList = NULL;
+
+ ret = 0;
+ cleanup:
+ virStringListFree(newMacsList);
+ return ret;
+}
+
+
+static int
+virMacMapRemoveLocked(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac)
+{
+ char **macsList = NULL;
+ char **newMacsList = NULL;
+
+ if (!(macsList = virHashLookup(mgr->macs, domain)))
+ return 0;
+
+ newMacsList = macsList;
+ virStringListRemove(&macsList, mac);
+ if (!macsList) {
+ virHashSteal(mgr->macs, domain);
+ } else {
+ if (macsList != newMacsList &&
+ virHashUpdateEntry(mgr->macs, domain, newMacsList) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virMacMapLoadFile(virMacMapPtr mgr,
+ const char *file)
+{
+ char *map_str = NULL;
+ virJSONValuePtr map = NULL;
+ int map_str_len = 0;
+ size_t i;
+ int ret = -1;
+
+ if (virFileExists(file) &&
+ (map_str_len = virFileReadAll(file,
+ VIR_MAC_MAP_FILE_SIZE_MAX,
+ &map_str)) < 0)
+ goto cleanup;
+
+ if (map_str_len == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!(map = virJSONValueFromString(map_str))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("invalid json in file: %s"),
+ file);
+ goto cleanup;
+ }
+
+ if (!virJSONValueIsArray(map)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed file structure: %s"),
+ file);
+ goto cleanup;
+ }
+
+ for (i = 0; i < virJSONValueArraySize(map); i++) {
+ virJSONValuePtr tmp = virJSONValueArrayGet(map, i);
+ virJSONValuePtr macs;
+ const char *domain;
+ size_t j;
+
+ if (!(domain = virJSONValueObjectGetString(tmp, "domain"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing domain"));
+ goto cleanup;
+ }
+
+ if (!(macs = virJSONValueObjectGetArray(tmp, "macs"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing macs"));
+ goto cleanup;
+ }
+
+ for (j = 0; j < virJSONValueArraySize(macs); j++) {
+ virJSONValuePtr macJSON = virJSONValueArrayGet(macs, j);
+ const char *mac = virJSONValueGetString(macJSON);
+
+ if (virMacMapAddLocked(mgr, domain, mac) < 0)
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(map_str);
+ virJSONValueFree(map);
+ return ret;
+}
+
+
+static int
+virMACMapHashDumper(void *payload,
+ const void *name,
+ void *data)
+{
+ virJSONValuePtr obj = NULL;
+ virJSONValuePtr arr = NULL;
+ const char **macs = payload;
+ size_t i;
+ int ret = -1;
+
+ if (!(obj = virJSONValueNewObject()) ||
+ !(arr = virJSONValueNewArray()))
+ goto cleanup;
+
+ for (i = 0; macs[i]; i++) {
+ virJSONValuePtr m = virJSONValueNewString(macs[i]);
+
+ if (!m ||
+ virJSONValueArrayAppend(arr, m) < 0) {
+ virJSONValueFree(m);
+ goto cleanup;
+ }
+ }
+
+ if (virJSONValueObjectAppendString(obj, "domain", name) < 0 ||
+ virJSONValueObjectAppend(obj, "macs", arr) < 0)
+ goto cleanup;
+ arr = NULL;
+
+ if (virJSONValueArrayAppend(data, obj) < 0)
+ goto cleanup;
+ obj = NULL;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(obj);
+ virJSONValueFree(arr);
+ return ret;
+}
+
+
+static int
+virMacMapDumpStrLocked(virMacMapPtr mgr,
+ char **str)
+{
+ virJSONValuePtr arr;
+ int ret = -1;
+
+ if (!(arr = virJSONValueNewArray()))
+ goto cleanup;
+
+ if (virHashForEach(mgr->macs, virMACMapHashDumper, arr) < 0)
+ goto cleanup;
+
+ if (!(*str = virJSONValueToString(arr, true)))
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(arr);
+ return ret;
+}
+
+
+static int
+virMacMapWriteFileLocked(virMacMapPtr mgr,
+ const char *file)
+{
+ char *str;
+ int ret = -1;
+
+ if (virMacMapDumpStrLocked(mgr, &str) < 0)
+ goto cleanup;
+
+ if (virFileRewriteStr(file, 0644, str) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(str);
+ return ret;
+}
+
+
+#define VIR_MAC_HASH_TABLE_SIZE 10
+
+virMacMapPtr
+virMacMapNew(const char *file)
+{
+ virMacMapPtr mgr;
+
+ if (virMacMapInitialize() < 0)
+ return NULL;
+
+ if (!(mgr = virObjectLockableNew(virMacMapClass)))
+ return NULL;
+
+ virObjectLock(mgr);
+ if (!(mgr->macs = virHashCreate(VIR_MAC_HASH_TABLE_SIZE,
+ virMacMapHashFree)))
+ goto error;
+
+ if (file &&
+ virMacMapLoadFile(mgr, file) < 0)
+ goto error;
+
+ virObjectUnlock(mgr);
+ return mgr;
+
+ error:
+ virObjectUnlock(mgr);
+ virObjectUnref(mgr);
+ return NULL;
+}
+
+
+int
+virMacMapAdd(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac)
+{
+ int ret;
+
+ virObjectLock(mgr);
+ ret = virMacMapAddLocked(mgr, domain, mac);
+ virObjectUnlock(mgr);
+ return ret;
+}
+
+
+int
+virMacMapRemove(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac)
+{
+ int ret;
+
+ virObjectLock(mgr);
+ ret = virMacMapRemoveLocked(mgr, domain, mac);
+ virObjectUnlock(mgr);
+ return ret;
+}
+
+
+const char *const *
+virMacMapLookup(virMacMapPtr mgr,
+ const char *domain)
+{
+ const char *const *ret;
+
+ virObjectLock(mgr);
+ ret = virHashLookup(mgr->macs, domain);
+ virObjectUnlock(mgr);
+ return ret;
+}
+
+
+int
+virMacMapWriteFile(virMacMapPtr mgr,
+ const char *filename)
+{
+ int ret;
+
+ virObjectLock(mgr);
+ ret = virMacMapWriteFileLocked(mgr, filename);
+ virObjectUnlock(mgr);
+ return ret;
+}
+
+
+int
+virMacMapDumpStr(virMacMapPtr mgr,
+ char **str)
+{
+ int ret;
+
+ virObjectLock(mgr);
+ ret = virMacMapDumpStrLocked(mgr, str);
+ virObjectUnlock(mgr);
+ return ret;
+}
diff --git a/src/util/virmacmap.h b/src/util/virmacmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..82da833004f712ffa989c0ad0614ddbd4c023e7a
--- /dev/null
+++ b/src/util/virmacmap.h
@@ -0,0 +1,48 @@
+/*
+ * virmacmap.h: MAC address <-> Domain name mapping
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ * Authors:
+ * Michal Privoznik
+ */
+
+#ifndef __VIR_MACMAP_H__
+# define __VIR_MACMAP_H__
+
+typedef struct virMacMap virMacMap;
+typedef virMacMap *virMacMapPtr;
+
+virMacMapPtr virMacMapNew(const char *file);
+
+int virMacMapAdd(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac);
+
+int virMacMapRemove(virMacMapPtr mgr,
+ const char *domain,
+ const char *mac);
+
+const char *const *virMacMapLookup(virMacMapPtr mgr,
+ const char *domain);
+
+int virMacMapWriteFile(virMacMapPtr mgr,
+ const char *filename);
+
+int virMacMapDumpStr(virMacMapPtr mgr,
+ char **str);
+#endif /* __VIR_MACMAPPING_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6a24861ffe6e9942ff8d540ef76eb86db2af1d3b..9d5583d430f8ec769dc8287ed0844a939d7d6495 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -144,6 +144,7 @@ EXTRA_DIST = \
vircgroupdata \
virconfdata \
virfiledata \
+ virmacmaptestdata \
virmock.h \
virnetdaemondata \
virnetdevtestdata \
@@ -191,6 +192,7 @@ test_programs = virshtest sockettest \
domainconftest \
virhostdevtest \
vircaps2xmltest \
+ virmacmaptest \
virnetdevtest \
virtypedparamtest \
$(NULL)
@@ -406,6 +408,7 @@ test_libraries = libshunload.la \
virhostcpumock.la \
nssmock.la \
domaincapsmock.la \
+ virmacmapmock.la \
$(NULL)
if WITH_QEMU
test_libraries += libqemumonitortestutils.la \
@@ -1137,6 +1140,17 @@ nsslinktest_CFLAGS = \
nsslinktest_LDADD = ../tools/nss/libnss_libvirt_impl.la
nsslinktest_LDFLAGS = $(NULL)
+virmacmapmock_la_SOURCES = \
+ virmacmapmock.c
+virmacmapmock_la_CFLAGS = $(AM_CFLAGS)
+virmacmapmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS)
+virmacmapmock_la_LIBADD = $(MOCKLIBS_LIBS)
+
+virmacmaptest_SOURCES = \
+ virmacmaptest.c testutils.h testutils.c
+virmacmaptest_CLFAGS = $(AM_CFLAGS);
+virmacmaptest_LDADD = $(LDADDS)
+
virnetdevtest_SOURCES = \
virnetdevtest.c testutils.h testutils.c
virnetdevtest_CFLAGS = $(AM_CFLAGS) $(LIBNL_CFLAGS)
diff --git a/tests/virmacmapmock.c b/tests/virmacmapmock.c
new file mode 100644
index 0000000000000000000000000000000000000000..d01f1c9e46c727b2d466204edaa153e5cecbb417
--- /dev/null
+++ b/tests/virmacmapmock.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ * Author: Michal Privoznik
+ */
+
+#include
+
+#include "virrandom.h"
+
+uint64_t virRandomBits(int nbits ATTRIBUTE_UNUSED)
+{
+ return 4; /* chosen by fair dice roll.
+ guaranteed to be random. */
+}
diff --git a/tests/virmacmaptest.c b/tests/virmacmaptest.c
new file mode 100644
index 0000000000000000000000000000000000000000..34609ad44a7f97cfee48f8a22288b2cd0b910be4
--- /dev/null
+++ b/tests/virmacmaptest.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ * Author: Michal Privoznik
+ */
+
+#include
+
+#include "testutils.h"
+#include "virmacmap.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+struct testData {
+ const char *file;
+ const char *domain;
+ const char * const * macs;
+ virMacMapPtr mgr;
+};
+
+
+static int
+testMACLookup(const void *opaque)
+{
+ const struct testData *data = opaque;
+ virMacMapPtr mgr = NULL;
+ const char * const * macs;
+ size_t i, j;
+ char *file = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&file, "%s/virmacmaptestdata/%s.json",
+ abs_srcdir, data->file) < 0)
+ goto cleanup;
+
+ if (!(mgr = virMacMapNew(file)))
+ goto cleanup;
+
+ macs = virMacMapLookup(mgr, data->domain);
+
+ for (i = 0; macs && macs[i]; i++) {
+ for (j = 0; data->macs && data->macs[j]; j++) {
+ if (STREQ(macs[i], data->macs[j]))
+ break;
+ }
+
+ if (!data->macs || !data->macs[j]) {
+ fprintf(stderr,
+ "Unexpected %s in the returned list of MACs\n", macs[i]);
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; data->macs && data->macs[i]; i++) {
+ for (j = 0; macs && macs[j]; j++) {
+ if (STREQ(data->macs[i], macs[j]))
+ break;
+ }
+
+ if (!macs || !macs[j]) {
+ fprintf(stderr,
+ "Expected %s in the returned list of MACs\n", data->macs[i]);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(file);
+ virObjectUnref(mgr);
+ return ret;
+}
+
+
+static int
+testMACRemove(const void *opaque)
+{
+ const struct testData *data = opaque;
+ virMacMapPtr mgr = NULL;
+ const char * const * macs;
+ size_t i;
+ char *file = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&file, "%s/virmacmaptestdata/%s.json",
+ abs_srcdir, data->file) < 0)
+ goto cleanup;
+
+ if (!(mgr = virMacMapNew(file)))
+ goto cleanup;
+
+ for (i = 0; data->macs && data->macs[i]; i++) {
+ if (virMacMapRemove(mgr, data->domain, data->macs[i]) < 0) {
+ fprintf(stderr,
+ "Error when removing %s from the list of MACs\n", data->macs[i]);
+ goto cleanup;
+ }
+ }
+
+ if ((macs = virMacMapLookup(mgr, data->domain))) {
+ fprintf(stderr,
+ "Not removed all MACs for domain %s: %s\n", data->domain, macs[0]);
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(file);
+ virObjectUnref(mgr);
+ return ret;
+}
+
+
+static int
+testMACFlush(const void *opaque)
+{
+ const struct testData *data = opaque;
+ char *file = NULL;
+ char *str = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&file, "%s/virmacmaptestdata/%s.json",
+ abs_srcdir, data->file) < 0)
+ goto cleanup;
+
+ if (virMacMapDumpStr(data->mgr, &str) < 0)
+ goto cleanup;
+
+ if (virTestCompareToFile(str, file) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(file);
+ VIR_FREE(str);
+ return ret;
+}
+
+
+static int
+mymain(void)
+{
+ int ret = 0;
+ virMacMapPtr mgr = NULL;
+
+#define DO_TEST_BASIC(f, d, ...) \
+ do { \
+ const char * const m[] = {__VA_ARGS__, NULL }; \
+ struct testData data = {.file = f, .domain = d, .macs = m}; \
+ if (virTestRun("Lookup " #d " in " #f, \
+ testMACLookup, &data) < 0) \
+ ret = -1; \
+ if (virTestRun("Remove " #d " in " #f, \
+ testMACRemove, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
+#define DO_TEST_FLUSH_PROLOGUE \
+ if (!(mgr = virMacMapNew(NULL))) \
+ ret = -1;
+
+#define DO_TEST_FLUSH(d, ...) \
+ do { \
+ const char * const m[] = {__VA_ARGS__, NULL }; \
+ size_t i; \
+ for (i = 0; m[i]; i++) { \
+ if (virMacMapAdd(mgr, d, m[i]) < 0) { \
+ virObjectUnref(mgr); \
+ mgr = NULL; \
+ ret = -1; \
+ } \
+ } \
+ } while (0)
+
+
+#define DO_TEST_FLUSH_EPILOGUE(f) \
+ do { \
+ struct testData data = {.file = f, .mgr = mgr}; \
+ if (virTestRun("Flush " #f, testMACFlush, &data) < 0) \
+ ret = -1; \
+ virObjectUnref(mgr); \
+ mgr = NULL; \
+ } while (0)
+
+ DO_TEST_BASIC("empty", "none", NULL);
+ DO_TEST_BASIC("simple", "f24", "aa:bb:cc:dd:ee:ff");
+ DO_TEST_BASIC("simple2", "f24", "aa:bb:cc:dd:ee:ff", "a1:b2:c3:d4:e5:f6");
+ DO_TEST_BASIC("simple2", "f25", "00:11:22:33:44:55", "aa:bb:cc:00:11:22");
+
+ DO_TEST_FLUSH_PROLOGUE;
+ DO_TEST_FLUSH_EPILOGUE("empty");
+
+ DO_TEST_FLUSH_PROLOGUE;
+ DO_TEST_FLUSH("f24", "aa:bb:cc:dd:ee:ff");
+ DO_TEST_FLUSH_EPILOGUE("simple");
+
+ DO_TEST_FLUSH_PROLOGUE;
+ DO_TEST_FLUSH("f24", "aa:bb:cc:dd:ee:ff", "a1:b2:c3:d4:e5:f6");
+ DO_TEST_FLUSH("f25", "00:11:22:33:44:55", "aa:bb:cc:00:11:22");
+ DO_TEST_FLUSH_EPILOGUE("simple2");
+
+ DO_TEST_FLUSH_PROLOGUE;
+ DO_TEST_FLUSH("dom0", "e1:81:5d:f3:41:57", "76:0a:2a:a0:51:86", "01:c7:fc:01:c7:fc");
+ DO_TEST_FLUSH("dom0", "8e:82:53:60:32:4a", "14:7a:25:dc:7d:a0", "f8:d7:75:f8:d7:75");
+ DO_TEST_FLUSH("dom0", "73:d2:50:fb:0f:b1", "82:ee:a7:9b:e3:69", "a8:b4:cb:a8:b4:cb");
+ DO_TEST_FLUSH("dom0", "7e:81:86:0f:0b:fb", "94:e2:00:d9:4c:70", "dc:7b:83:dc:7b:83");
+ DO_TEST_FLUSH("dom0", "d1:19:a5:a1:52:a8", "22:03:a0:bf:cb:4a", "e3:c7:f8:e3:c7:f8");
+ DO_TEST_FLUSH("dom0", "aa:bf:3f:4f:21:8d", "28:67:45:72:8f:47", "eb:08:cd:eb:08:cd");
+ DO_TEST_FLUSH("dom0", "bd:f8:a7:e5:e2:bd", "c7:80:e3:b9:18:4d", "ce:da:c0:ce:da:c0");
+ DO_TEST_FLUSH("dom1", "8b:51:1d:9f:2f:29", "7c:ae:4c:3e:e1:11", "c6:68:4e:98:ff:6a");
+ DO_TEST_FLUSH("dom1", "43:0e:33:a1:3f:0f", "7a:3e:ed:bb:15:27", "b1:17:fd:95:d2:1b");
+ DO_TEST_FLUSH("dom1", "9e:89:49:99:51:0e", "89:b4:3f:08:88:2c", "54:0b:4c:e2:0a:39");
+ DO_TEST_FLUSH("dom1", "bb:88:07:19:51:9d", "b7:f1:1a:40:a2:95", "88:94:39:a3:90:b4");
+ DO_TEST_FLUSH_EPILOGUE("complex");
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmacmapmock.so")
diff --git a/tests/virmacmaptestdata/complex.json b/tests/virmacmaptestdata/complex.json
new file mode 100644
index 0000000000000000000000000000000000000000..192347c3f1cc2987f44e099e2df7e1e3f55185a9
--- /dev/null
+++ b/tests/virmacmaptestdata/complex.json
@@ -0,0 +1,45 @@
+[
+ {
+ "domain": "dom0",
+ "macs": [
+ "e1:81:5d:f3:41:57",
+ "76:0a:2a:a0:51:86",
+ "01:c7:fc:01:c7:fc",
+ "8e:82:53:60:32:4a",
+ "14:7a:25:dc:7d:a0",
+ "f8:d7:75:f8:d7:75",
+ "73:d2:50:fb:0f:b1",
+ "82:ee:a7:9b:e3:69",
+ "a8:b4:cb:a8:b4:cb",
+ "7e:81:86:0f:0b:fb",
+ "94:e2:00:d9:4c:70",
+ "dc:7b:83:dc:7b:83",
+ "d1:19:a5:a1:52:a8",
+ "22:03:a0:bf:cb:4a",
+ "e3:c7:f8:e3:c7:f8",
+ "aa:bf:3f:4f:21:8d",
+ "28:67:45:72:8f:47",
+ "eb:08:cd:eb:08:cd",
+ "bd:f8:a7:e5:e2:bd",
+ "c7:80:e3:b9:18:4d",
+ "ce:da:c0:ce:da:c0"
+ ]
+ },
+ {
+ "domain": "dom1",
+ "macs": [
+ "8b:51:1d:9f:2f:29",
+ "7c:ae:4c:3e:e1:11",
+ "c6:68:4e:98:ff:6a",
+ "43:0e:33:a1:3f:0f",
+ "7a:3e:ed:bb:15:27",
+ "b1:17:fd:95:d2:1b",
+ "9e:89:49:99:51:0e",
+ "89:b4:3f:08:88:2c",
+ "54:0b:4c:e2:0a:39",
+ "bb:88:07:19:51:9d",
+ "b7:f1:1a:40:a2:95",
+ "88:94:39:a3:90:b4"
+ ]
+ }
+]
diff --git a/tests/virmacmaptestdata/empty.json b/tests/virmacmaptestdata/empty.json
new file mode 100644
index 0000000000000000000000000000000000000000..41b42e677b97324ff33450d619b6812449bc3abe
--- /dev/null
+++ b/tests/virmacmaptestdata/empty.json
@@ -0,0 +1,3 @@
+[
+
+]
diff --git a/tests/virmacmaptestdata/simple.json b/tests/virmacmaptestdata/simple.json
new file mode 100644
index 0000000000000000000000000000000000000000..ea0fb083602994c50a20e549441da2eebb83e3db
--- /dev/null
+++ b/tests/virmacmaptestdata/simple.json
@@ -0,0 +1,8 @@
+[
+ {
+ "domain": "f24",
+ "macs": [
+ "aa:bb:cc:dd:ee:ff"
+ ]
+ }
+]
diff --git a/tests/virmacmaptestdata/simple2.json b/tests/virmacmaptestdata/simple2.json
new file mode 100644
index 0000000000000000000000000000000000000000..91b2cde0cdb92ed8f0e5b715da44d40af7ce1d86
--- /dev/null
+++ b/tests/virmacmaptestdata/simple2.json
@@ -0,0 +1,16 @@
+[
+ {
+ "domain": "f25",
+ "macs": [
+ "00:11:22:33:44:55",
+ "aa:bb:cc:00:11:22"
+ ]
+ },
+ {
+ "domain": "f24",
+ "macs": [
+ "aa:bb:cc:dd:ee:ff",
+ "a1:b2:c3:d4:e5:f6"
+ ]
+ }
+]