1 /*
2 * virmacmap.c: MAC address <-> Domain name mapping
3 *
4 * Copyright (C) 2016 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "virmacmap.h"
24 #include "virobject.h"
25 #include "virlog.h"
26 #include "virjson.h"
27 #include "virfile.h"
28 #include "virhash.h"
29 #include "virstring.h"
30 #include "viralloc.h"
31
32 #define VIR_FROM_THIS VIR_FROM_NETWORK
33
34 VIR_LOG_INIT("util.virmacmap");
35
36 /**
37 * VIR_MAC_MAP_FILE_SIZE_MAX:
38 *
39 * Macro providing the upper limit on the size of mac maps file
40 */
41 #define VIR_MAC_MAP_FILE_SIZE_MAX (32 * 1024 * 1024)
42
43 struct virMacMap {
44 virObjectLockable parent;
45
46 GHashTable *macs;
47 };
48
49
50 static virClass *virMacMapClass;
51
52
53 static void
virMacMapDispose(void * obj)54 virMacMapDispose(void *obj)
55 {
56 virMacMap *mgr = obj;
57 GHashTableIter htitr;
58 void *value;
59
60 g_hash_table_iter_init(&htitr, mgr->macs);
61
62 while (g_hash_table_iter_next(&htitr, NULL, &value))
63 g_slist_free_full(value, g_free);
64
65 virHashFree(mgr->macs);
66 }
67
68
virMacMapOnceInit(void)69 static int virMacMapOnceInit(void)
70 {
71 if (!VIR_CLASS_NEW(virMacMap, virClassForObjectLockable()))
72 return -1;
73
74 return 0;
75 }
76
77 VIR_ONCE_GLOBAL_INIT(virMacMap);
78
79
80 static void
virMacMapAddLocked(virMacMap * mgr,const char * domain,const char * mac)81 virMacMapAddLocked(virMacMap *mgr,
82 const char *domain,
83 const char *mac)
84 {
85 GSList *orig_list;
86 GSList *list;
87 GSList *next;
88
89 list = orig_list = g_hash_table_lookup(mgr->macs, domain);
90
91 for (next = list; next; next = next->next) {
92 if (STREQ((const char *) next->data, mac))
93 return;
94 }
95
96 list = g_slist_append(list, g_strdup(mac));
97
98 if (list != orig_list)
99 g_hash_table_insert(mgr->macs, g_strdup(domain), list);
100 }
101
102
103 static void
virMacMapRemoveLocked(virMacMap * mgr,const char * domain,const char * mac)104 virMacMapRemoveLocked(virMacMap *mgr,
105 const char *domain,
106 const char *mac)
107 {
108 GSList *orig_list;
109 GSList *list;
110 GSList *next;
111
112 list = orig_list = g_hash_table_lookup(mgr->macs, domain);
113
114 if (!orig_list)
115 return;
116
117 for (next = list; next; next = next->next) {
118 if (STREQ((const char *) next->data, mac)) {
119 list = g_slist_remove_link(list, next);
120 g_slist_free_full(next, g_free);
121 break;
122 }
123 }
124
125 if (list != orig_list) {
126 if (list)
127 g_hash_table_insert(mgr->macs, g_strdup(domain), list);
128 else
129 g_hash_table_remove(mgr->macs, domain);
130 }
131 }
132
133
134 static int
virMacMapLoadFile(virMacMap * mgr,const char * file)135 virMacMapLoadFile(virMacMap *mgr,
136 const char *file)
137 {
138 g_autofree char *map_str = NULL;
139 g_autoptr(virJSONValue) map = NULL;
140 int map_str_len = 0;
141 size_t i;
142
143 if (virFileExists(file) &&
144 (map_str_len = virFileReadAll(file,
145 VIR_MAC_MAP_FILE_SIZE_MAX,
146 &map_str)) < 0)
147 return -1;
148
149 if (map_str_len == 0)
150 return 0;
151
152 if (!(map = virJSONValueFromString(map_str))) {
153 virReportError(VIR_ERR_INTERNAL_ERROR,
154 _("invalid json in file: %s"),
155 file);
156 return -1;
157 }
158
159 if (!virJSONValueIsArray(map)) {
160 virReportError(VIR_ERR_INTERNAL_ERROR,
161 _("Malformed file structure: %s"),
162 file);
163 return -1;
164 }
165
166 for (i = 0; i < virJSONValueArraySize(map); i++) {
167 virJSONValue *tmp = virJSONValueArrayGet(map, i);
168 virJSONValue *macs;
169 const char *domain;
170 size_t j;
171 GSList *vals = NULL;
172
173 if (!(domain = virJSONValueObjectGetString(tmp, "domain"))) {
174 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
175 _("Missing domain"));
176 return -1;
177 }
178
179 if (!(macs = virJSONValueObjectGetArray(tmp, "macs"))) {
180 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
181 _("Missing macs"));
182 return -1;
183 }
184
185 if (g_hash_table_contains(mgr->macs, domain)) {
186 virReportError(VIR_ERR_INTERNAL_ERROR,
187 _("duplicate domain '%s'"), domain);
188 return -1;
189
190 }
191
192 for (j = 0; j < virJSONValueArraySize(macs); j++) {
193 virJSONValue *macJSON = virJSONValueArrayGet(macs, j);
194
195 vals = g_slist_prepend(vals, g_strdup(virJSONValueGetString(macJSON)));
196 }
197
198 vals = g_slist_reverse(vals);
199 g_hash_table_insert(mgr->macs, g_strdup(domain), vals);
200 }
201
202 return 0;
203 }
204
205
206 static int
virMACMapHashDumper(void * payload,const char * name,void * data)207 virMACMapHashDumper(void *payload,
208 const char *name,
209 void *data)
210 {
211 g_autoptr(virJSONValue) obj = virJSONValueNewObject();
212 g_autoptr(virJSONValue) arr = virJSONValueNewArray();
213 GSList *macs = payload;
214 GSList *next;
215
216 for (next = macs; next; next = next->next) {
217 g_autoptr(virJSONValue) m = virJSONValueNewString((const char *) next->data);
218
219 if (virJSONValueArrayAppend(arr, &m) < 0)
220 return -1;
221 }
222
223 if (virJSONValueObjectAppendString(obj, "domain", name) < 0 ||
224 virJSONValueObjectAppend(obj, "macs", &arr) < 0)
225 return -1;
226
227 if (virJSONValueArrayAppend(data, &obj) < 0)
228 return -1;
229
230 return 0;
231 }
232
233
234 static int
virMacMapDumpStrLocked(virMacMap * mgr,char ** str)235 virMacMapDumpStrLocked(virMacMap *mgr,
236 char **str)
237 {
238 g_autoptr(virJSONValue) arr = virJSONValueNewArray();
239
240 if (virHashForEachSorted(mgr->macs, virMACMapHashDumper, arr) < 0)
241 return -1;
242
243 if (!(*str = virJSONValueToString(arr, true)))
244 return -1;
245
246 return 0;
247 }
248
249
250 static int
virMacMapWriteFileLocked(virMacMap * mgr,const char * file)251 virMacMapWriteFileLocked(virMacMap *mgr,
252 const char *file)
253 {
254 g_autofree char *str = NULL;
255
256 if (virMacMapDumpStrLocked(mgr, &str) < 0)
257 return -1;
258
259 if (virFileRewriteStr(file, 0644, str) < 0)
260 return -1;
261
262 return 0;
263 }
264
265
266 char *
virMacMapFileName(const char * dnsmasqStateDir,const char * bridge)267 virMacMapFileName(const char *dnsmasqStateDir,
268 const char *bridge)
269 {
270 return g_strdup_printf("%s/%s.macs", dnsmasqStateDir, bridge);
271 }
272
273
274 #define VIR_MAC_HASH_TABLE_SIZE 10
275
276 virMacMap *
virMacMapNew(const char * file)277 virMacMapNew(const char *file)
278 {
279 virMacMap *mgr;
280
281 if (virMacMapInitialize() < 0)
282 return NULL;
283
284 if (!(mgr = virObjectLockableNew(virMacMapClass)))
285 return NULL;
286
287 virObjectLock(mgr);
288
289 mgr->macs = virHashNew(NULL);
290
291 if (file &&
292 virMacMapLoadFile(mgr, file) < 0)
293 goto error;
294
295 virObjectUnlock(mgr);
296 return mgr;
297
298 error:
299 virObjectUnlock(mgr);
300 virObjectUnref(mgr);
301 return NULL;
302 }
303
304
305 int
virMacMapAdd(virMacMap * mgr,const char * domain,const char * mac)306 virMacMapAdd(virMacMap *mgr,
307 const char *domain,
308 const char *mac)
309 {
310 virObjectLock(mgr);
311 virMacMapAddLocked(mgr, domain, mac);
312 virObjectUnlock(mgr);
313 return 0;
314 }
315
316
317 int
virMacMapRemove(virMacMap * mgr,const char * domain,const char * mac)318 virMacMapRemove(virMacMap *mgr,
319 const char *domain,
320 const char *mac)
321 {
322 virObjectLock(mgr);
323 virMacMapRemoveLocked(mgr, domain, mac);
324 virObjectUnlock(mgr);
325 return 0;
326 }
327
328
329 /* note that the returned pointer may be invalidated by other APIs in this module */
330 GSList *
virMacMapLookup(virMacMap * mgr,const char * domain)331 virMacMapLookup(virMacMap *mgr,
332 const char *domain)
333 {
334 GSList *ret;
335
336 virObjectLock(mgr);
337 ret = virHashLookup(mgr->macs, domain);
338 virObjectUnlock(mgr);
339 return ret;
340 }
341
342
343 int
virMacMapWriteFile(virMacMap * mgr,const char * filename)344 virMacMapWriteFile(virMacMap *mgr,
345 const char *filename)
346 {
347 int ret;
348
349 virObjectLock(mgr);
350 ret = virMacMapWriteFileLocked(mgr, filename);
351 virObjectUnlock(mgr);
352 return ret;
353 }
354
355
356 int
virMacMapDumpStr(virMacMap * mgr,char ** str)357 virMacMapDumpStr(virMacMap *mgr,
358 char **str)
359 {
360 int ret;
361
362 virObjectLock(mgr);
363 ret = virMacMapDumpStrLocked(mgr, str);
364 virObjectUnlock(mgr);
365 return ret;
366 }
367