/* * 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 * . */ #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; GHashTable *macs; }; static virClassPtr virMacMapClass; static int virMacMapHashFree(void *payload, const char *name G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED) { g_strfreev(payload); return 0; } static void virMacMapDispose(void *obj) { virMacMapPtr mgr = obj; virHashForEach(mgr->macs, virMacMapHashFree, NULL); virHashFree(mgr->macs); } static int virMacMapOnceInit(void) { if (!VIR_CLASS_NEW(virMacMap, virClassForObjectLockable())) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virMacMap); static int virMacMapAddLocked(virMacMapPtr mgr, const char *domain, const char *mac) { char **macsList = NULL; if ((macsList = virHashLookup(mgr->macs, domain)) && virStringListHasString((const char**) macsList, mac)) { return 0; } if (virStringListAdd(&macsList, mac) < 0 || virHashUpdateEntry(mgr->macs, domain, macsList) < 0) return -1; return 0; } 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(&newMacsList, mac); if (!newMacsList) { 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) { g_autofree 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: virJSONValueFree(map); return ret; } static int virMACMapHashDumper(void *payload, const char *name, void *data) { virJSONValuePtr obj = virJSONValueNewObject(); virJSONValuePtr arr = NULL; const char **macs = payload; size_t i; int ret = -1; arr = virJSONValueNewArray(); 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; arr = virJSONValueNewArray(); if (virHashForEachSorted(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) { g_autofree char *str = NULL; if (virMacMapDumpStrLocked(mgr, &str) < 0) return -1; if (virFileRewriteStr(file, 0644, str) < 0) return -1; return 0; } char * virMacMapFileName(const char *dnsmasqStateDir, const char *bridge) { char *filename; filename = g_strdup_printf("%s/%s.macs", dnsmasqStateDir, bridge); return filename; } #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 = virHashNew(NULL))) 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; }