1 /*
2 * nwfilter_gentech_driver.c: generic technology driver
3 *
4 * Copyright (C) 2011-2014 Red Hat, Inc.
5 * Copyright (C) 2010 IBM Corp.
6 * Copyright (C) 2010 Stefan Berger
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see
20 * <http://www.gnu.org/licenses/>.
21 */
22
23 #include <config.h>
24
25 #include "internal.h"
26
27 #include "viralloc.h"
28 #include "virlog.h"
29 #include "domain_conf.h"
30 #include "virerror.h"
31 #include "nwfilter_gentech_driver.h"
32 #include "nwfilter_ebiptables_driver.h"
33 #include "nwfilter_dhcpsnoop.h"
34 #include "nwfilter_ipaddrmap.h"
35 #include "nwfilter_learnipaddr.h"
36 #include "virnetdev.h"
37 #include "datatypes.h"
38 #include "virsocketaddr.h"
39 #include "virstring.h"
40
41 #define VIR_FROM_THIS VIR_FROM_NWFILTER
42
43 VIR_LOG_INIT("nwfilter.nwfilter_gentech_driver");
44
45 #define NWFILTER_STD_VAR_MAC NWFILTER_VARNAME_MAC
46 #define NWFILTER_STD_VAR_IP NWFILTER_VARNAME_IP
47
48 #define NWFILTER_DFLT_LEARN "any"
49
50 static int _virNWFilterTeardownFilter(const char *ifname);
51
52
53 static virNWFilterTechDriver *filter_tech_drivers[] = {
54 &ebiptables_driver,
55 NULL
56 };
57
58 /* Serializes instantiation of filters. This is necessary
59 * to avoid lock ordering deadlocks. eg virNWFilterInstantiateFilterUpdate
60 * will hold a lock on a virNWFilterObj *. This in turn invokes
61 * virNWFilterDoInstantiate which invokes virNWFilterDetermineMissingVarsRec
62 * which invokes virNWFilterObjListFindInstantiateFilter. This iterates over
63 * every single virNWFilterObj *in the list. So if 2 threads try to
64 * instantiate a filter in parallel, they'll both hold 1 lock at the top level
65 * in virNWFilterInstantiateFilterUpdate which will cause the other thread
66 * to deadlock in virNWFilterObjListFindInstantiateFilter.
67 *
68 * XXX better long term solution is to make virNWFilterObjList use a
69 * hash table as is done for virDomainObjList. You can then get
70 * lockless lookup of objects by name.
71 */
72 static virMutex updateMutex;
73
virNWFilterTechDriversInit(bool privileged)74 int virNWFilterTechDriversInit(bool privileged)
75 {
76 size_t i = 0;
77 VIR_DEBUG("Initializing NWFilter technology drivers");
78 if (virMutexInitRecursive(&updateMutex) < 0)
79 return -1;
80
81 while (filter_tech_drivers[i]) {
82 if (!(filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
83 filter_tech_drivers[i]->init(privileged);
84 i++;
85 }
86 return 0;
87 }
88
89
virNWFilterTechDriversShutdown(void)90 void virNWFilterTechDriversShutdown(void)
91 {
92 size_t i = 0;
93 while (filter_tech_drivers[i]) {
94 if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
95 filter_tech_drivers[i]->shutdown();
96 i++;
97 }
98 virMutexDestroy(&updateMutex);
99 }
100
101
102 virNWFilterTechDriver *
virNWFilterTechDriverForName(const char * name)103 virNWFilterTechDriverForName(const char *name)
104 {
105 size_t i = 0;
106 while (filter_tech_drivers[i]) {
107 if (STREQ(filter_tech_drivers[i]->name, name)) {
108 if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED) == 0)
109 break;
110 return filter_tech_drivers[i];
111 }
112 i++;
113 }
114 return NULL;
115 }
116
117
118 static void
virNWFilterRuleInstFree(virNWFilterRuleInst * inst)119 virNWFilterRuleInstFree(virNWFilterRuleInst *inst)
120 {
121 if (!inst)
122 return;
123
124 virHashFree(inst->vars);
125 g_free(inst);
126 }
127
128
129 /**
130 * Convert a GHashTable into a string of comma-separated
131 * variable names.
132 */
133 struct printString
134 {
135 virBuffer buf;
136 const char *separator;
137 bool reportMAC;
138 bool reportIP;
139 };
140
141
142 static int
printString(void * payload G_GNUC_UNUSED,const char * name,void * data)143 printString(void *payload G_GNUC_UNUSED, const char *name, void *data)
144 {
145 struct printString *ps = data;
146
147 if ((STREQ(name, NWFILTER_STD_VAR_IP) && !ps->reportIP) ||
148 (STREQ(name, NWFILTER_STD_VAR_MAC) && !ps->reportMAC))
149 return 0;
150
151 if (virBufferUse(&ps->buf) && ps->separator)
152 virBufferAdd(&ps->buf, ps->separator, -1);
153
154 virBufferAdd(&ps->buf, name, -1);
155 return 0;
156 }
157
158 /**
159 * virNWFilterPrintVars
160 *
161 * @var: hash table containing variables
162 * @separator: separator to use between variable names, i.e., ", "
163 * @reportMAC: whether to report the 'MAC' variable
164 * @reportIP : whether to report the IP variable
165 *
166 * Returns a string of comma separated variable names
167 */
168 static char *
virNWFilterPrintVars(GHashTable * vars,const char * separator,bool reportMAC,bool reportIP)169 virNWFilterPrintVars(GHashTable *vars,
170 const char *separator,
171 bool reportMAC,
172 bool reportIP)
173 {
174 struct printString ps = {
175 .buf = VIR_BUFFER_INITIALIZER,
176 .separator = separator,
177 .reportMAC = reportMAC,
178 .reportIP = reportIP,
179 };
180
181 virHashForEach(vars, printString, &ps);
182
183 return virBufferContentAndReset(&ps.buf);
184 }
185
186
187 /**
188 * virNWFilterCreateVarsFrom:
189 * @vars1: pointer to hash table
190 * @vars2: pointer to hash table
191 *
192 * Returns pointer to new hashtable or NULL in case of error with
193 * error already reported.
194 *
195 * Creates a new hash table with contents of var1 and var2 added where
196 * contents of var2 will overwrite those of var1.
197 */
198 static GHashTable *
virNWFilterCreateVarsFrom(GHashTable * vars1,GHashTable * vars2)199 virNWFilterCreateVarsFrom(GHashTable *vars1,
200 GHashTable *vars2)
201 {
202 g_autoptr(GHashTable) res = virHashNew(virNWFilterVarValueHashFree);
203
204 if (virNWFilterHashTablePutAll(vars1, res) < 0)
205 return NULL;
206
207 if (virNWFilterHashTablePutAll(vars2, res) < 0)
208 return NULL;
209
210 return g_steal_pointer(&res);
211 }
212
213
214 typedef struct _virNWFilterInst virNWFilterInst;
215 struct _virNWFilterInst {
216 virNWFilterObj **filters;
217 size_t nfilters;
218 virNWFilterRuleInst **rules;
219 size_t nrules;
220 };
221
222
223 static void
virNWFilterInstReset(virNWFilterInst * inst)224 virNWFilterInstReset(virNWFilterInst *inst)
225 {
226 size_t i;
227
228 for (i = 0; i < inst->nfilters; i++)
229 virNWFilterObjUnlock(inst->filters[i]);
230 g_free(inst->filters);
231 inst->nfilters = 0;
232
233 for (i = 0; i < inst->nrules; i++)
234 virNWFilterRuleInstFree(inst->rules[i]);
235 g_free(inst->rules);
236 inst->nrules = 0;
237 }
238
239
240
241 static int
242 virNWFilterDefToInst(virNWFilterDriverState *driver,
243 virNWFilterDef *def,
244 GHashTable *vars,
245 enum instCase useNewFilter,
246 bool *foundNewFilter,
247 virNWFilterInst *inst);
248
249 static int
virNWFilterRuleDefToRuleInst(virNWFilterDef * def,virNWFilterRuleDef * rule,GHashTable * vars,virNWFilterInst * inst)250 virNWFilterRuleDefToRuleInst(virNWFilterDef *def,
251 virNWFilterRuleDef *rule,
252 GHashTable *vars,
253 virNWFilterInst *inst)
254 {
255 g_autoptr(GHashTable) tmpvars = virHashNew(virNWFilterVarValueHashFree);
256 virNWFilterRuleInst *ruleinst;
257
258 if (virNWFilterHashTablePutAll(vars, tmpvars) < 0)
259 return -1;
260
261 ruleinst = g_new0(virNWFilterRuleInst, 1);
262
263 ruleinst->chainSuffix = def->chainsuffix;
264 ruleinst->chainPriority = def->chainPriority;
265 ruleinst->def = rule;
266 ruleinst->priority = rule->priority;
267 ruleinst->vars = g_steal_pointer(&tmpvars);
268
269 VIR_APPEND_ELEMENT(inst->rules, inst->nrules, ruleinst);
270
271 return 0;
272 }
273
274
275 static int
virNWFilterIncludeDefToRuleInst(virNWFilterDriverState * driver,virNWFilterIncludeDef * inc,GHashTable * vars,enum instCase useNewFilter,bool * foundNewFilter,virNWFilterInst * inst)276 virNWFilterIncludeDefToRuleInst(virNWFilterDriverState *driver,
277 virNWFilterIncludeDef *inc,
278 GHashTable *vars,
279 enum instCase useNewFilter,
280 bool *foundNewFilter,
281 virNWFilterInst *inst)
282 {
283 virNWFilterObj *obj;
284 g_autoptr(GHashTable) tmpvars = NULL;
285 virNWFilterDef *childdef;
286 virNWFilterDef *newChilddef;
287
288 VIR_DEBUG("Instantiating filter %s", inc->filterref);
289
290 /* create a temporary hashmap for depth-first tree traversal */
291 if (!(tmpvars = virNWFilterCreateVarsFrom(inc->params, vars)))
292 return -1;
293
294 /* 'obj' is always appended to 'inst->filters' thus we don't unlock it */
295 if (!(obj = virNWFilterObjListFindInstantiateFilter(driver->nwfilters,
296 inc->filterref)))
297 return -1;
298
299 childdef = virNWFilterObjGetDef(obj);
300
301 switch (useNewFilter) {
302 case INSTANTIATE_FOLLOW_NEWFILTER:
303 newChilddef = virNWFilterObjGetNewDef(obj);
304 if (newChilddef) {
305 childdef = newChilddef;
306 *foundNewFilter = true;
307 }
308 break;
309 case INSTANTIATE_ALWAYS:
310 break;
311 }
312
313 VIR_APPEND_ELEMENT(inst->filters, inst->nfilters, obj);
314
315 if (virNWFilterDefToInst(driver,
316 childdef,
317 tmpvars,
318 useNewFilter,
319 foundNewFilter,
320 inst) < 0) {
321 virNWFilterInstReset(inst);
322 return -1;
323 }
324
325 return 0;
326 }
327
328
329 /**
330 * virNWFilterDefToInst:
331 * @driver: the driver state pointer
332 * @def: The filter to instantiate
333 * @vars: A map holding variable names and values used for instantiating
334 * the filter and its subfilters.
335 * @useNewFilter: instruct whether to use a newDef pointer rather than a
336 * def ptr which is useful during a filter update
337 * @foundNewFilter: pointer to int indivating whether a newDef pointer was
338 * ever used; variable expected to be initialized to 0 by caller
339 * @rulesout: array to be filled with rule instance
340 * @nrulesout: counter to be filled with number of rule instances
341 *
342 * Recursively expand a nested filter into a flat list of rule instances,
343 * in a depth-first traversal of the tree.
344 *
345 * Returns 0 on success, -1 on error
346 */
347 static int
virNWFilterDefToInst(virNWFilterDriverState * driver,virNWFilterDef * def,GHashTable * vars,enum instCase useNewFilter,bool * foundNewFilter,virNWFilterInst * inst)348 virNWFilterDefToInst(virNWFilterDriverState *driver,
349 virNWFilterDef *def,
350 GHashTable *vars,
351 enum instCase useNewFilter,
352 bool *foundNewFilter,
353 virNWFilterInst *inst)
354 {
355 size_t i;
356 int ret = -1;
357
358 for (i = 0; i < def->nentries; i++) {
359 if (def->filterEntries[i]->rule) {
360 if (virNWFilterRuleDefToRuleInst(def,
361 def->filterEntries[i]->rule,
362 vars,
363 inst) < 0)
364 goto cleanup;
365 } else if (def->filterEntries[i]->include) {
366 if (virNWFilterIncludeDefToRuleInst(driver,
367 def->filterEntries[i]->include,
368 vars,
369 useNewFilter, foundNewFilter,
370 inst) < 0)
371 goto cleanup;
372 }
373 }
374
375 ret = 0;
376 cleanup:
377 if (ret < 0)
378 virNWFilterInstReset(inst);
379 return ret;
380 }
381
382
383 static int
virNWFilterDetermineMissingVarsRec(virNWFilterDef * filter,GHashTable * vars,GHashTable * missing_vars,int useNewFilter,virNWFilterDriverState * driver)384 virNWFilterDetermineMissingVarsRec(virNWFilterDef *filter,
385 GHashTable *vars,
386 GHashTable *missing_vars,
387 int useNewFilter,
388 virNWFilterDriverState *driver)
389 {
390 virNWFilterObj *obj;
391 int rc = 0;
392 size_t i, j;
393 virNWFilterDef *next_filter;
394 virNWFilterDef *newNext_filter;
395 virNWFilterVarValue *val;
396
397 for (i = 0; i < filter->nentries; i++) {
398 virNWFilterRuleDef * rule = filter->filterEntries[i]->rule;
399 virNWFilterIncludeDef *inc = filter->filterEntries[i]->include;
400 if (rule) {
401 /* check all variables of this rule */
402 for (j = 0; j < rule->nVarAccess; j++) {
403 if (!virNWFilterVarAccessIsAvailable(rule->varAccess[j],
404 vars)) {
405 g_autofree char *varAccess = NULL;
406 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
407
408 virNWFilterVarAccessPrint(rule->varAccess[j], &buf);
409
410 if (!(val = virNWFilterVarValueCreateSimpleCopyValue("1")))
411 return -1;
412
413 varAccess = virBufferContentAndReset(&buf);
414 rc = virHashUpdateEntry(missing_vars, varAccess, val);
415 if (rc < 0) {
416 virNWFilterVarValueFree(val);
417 return -1;
418 }
419 }
420 }
421 } else if (inc) {
422 g_autoptr(GHashTable) tmpvars = NULL;
423
424 VIR_DEBUG("Following filter %s", inc->filterref);
425 if (!(obj = virNWFilterObjListFindInstantiateFilter(driver->nwfilters,
426 inc->filterref)))
427 return -1;
428
429 /* create a temporary hashmap for depth-first tree traversal */
430 if (!(tmpvars = virNWFilterCreateVarsFrom(inc->params, vars))) {
431 virNWFilterObjUnlock(obj);
432 return -1;
433 }
434
435 next_filter = virNWFilterObjGetDef(obj);
436
437 switch (useNewFilter) {
438 case INSTANTIATE_FOLLOW_NEWFILTER:
439 newNext_filter = virNWFilterObjGetNewDef(obj);
440 if (newNext_filter)
441 next_filter = newNext_filter;
442 break;
443 case INSTANTIATE_ALWAYS:
444 break;
445 }
446
447 rc = virNWFilterDetermineMissingVarsRec(next_filter,
448 tmpvars,
449 missing_vars,
450 useNewFilter,
451 driver);
452 virNWFilterObjUnlock(obj);
453 if (rc < 0)
454 return -1;
455 }
456 }
457 return 0;
458 }
459
460
461 /**
462 * virNWFilterDoInstantiate:
463 * @techdriver: The driver to use for instantiation
464 * @binding: description of port to bind the filter to
465 * @filter: The filter to instantiate
466 * @forceWithPendingReq: Ignore the check whether a pending learn request
467 * is active; 'true' only when the rules are applied late
468 *
469 * Returns 0 on success, a value otherwise.
470 *
471 * Instantiate a filter by instantiating the filter itself along with
472 * all its subfilters in a depth-first traversal of the tree of referenced
473 * filters. The name of the interface to which the rules belong must be
474 * provided. Apply the values of variables as needed.
475 *
476 * Call this function while holding the NWFilter filter update lock
477 */
478 static int
virNWFilterDoInstantiate(virNWFilterTechDriver * techdriver,virNWFilterBindingDef * binding,virNWFilterDef * filter,int ifindex,enum instCase useNewFilter,bool * foundNewFilter,bool teardownOld,virNWFilterDriverState * driver,bool forceWithPendingReq)479 virNWFilterDoInstantiate(virNWFilterTechDriver *techdriver,
480 virNWFilterBindingDef *binding,
481 virNWFilterDef *filter,
482 int ifindex,
483 enum instCase useNewFilter,
484 bool *foundNewFilter,
485 bool teardownOld,
486 virNWFilterDriverState *driver,
487 bool forceWithPendingReq)
488 {
489 int rc;
490 virNWFilterInst inst;
491 bool instantiate = true;
492 g_autofree char *buf = NULL;
493 virNWFilterVarValue *lv;
494 const char *learning;
495 bool reportIP = false;
496
497 GHashTable *missing_vars = virHashNew(virNWFilterVarValueHashFree);
498
499 memset(&inst, 0, sizeof(inst));
500
501 rc = virNWFilterDetermineMissingVarsRec(filter,
502 binding->filterparams,
503 missing_vars,
504 useNewFilter,
505 driver);
506 if (rc < 0)
507 goto error;
508
509 lv = virHashLookup(binding->filterparams, NWFILTER_VARNAME_CTRL_IP_LEARNING);
510 if (lv)
511 learning = virNWFilterVarValueGetNthValue(lv, 0);
512 else
513 learning = NULL;
514
515 if (learning == NULL)
516 learning = NWFILTER_DFLT_LEARN;
517
518 if (virHashSize(missing_vars) == 1) {
519 if (virHashLookup(missing_vars,
520 NWFILTER_STD_VAR_IP) != NULL) {
521 if (STRCASEEQ(learning, "none")) { /* no learning */
522 reportIP = true;
523 goto err_unresolvable_vars;
524 }
525 if (STRCASEEQ(learning, "dhcp")) {
526 rc = virNWFilterDHCPSnoopReq(techdriver,
527 binding,
528 driver);
529 goto error;
530 } else if (STRCASEEQ(learning, "any")) {
531 if (!virNWFilterHasLearnReq(ifindex)) {
532 rc = virNWFilterLearnIPAddress(techdriver,
533 binding,
534 ifindex,
535 driver,
536 DETECT_DHCP|DETECT_STATIC);
537 }
538 goto error;
539 } else {
540 rc = -1;
541 virReportError(VIR_ERR_PARSE_FAILED,
542 _("filter '%s' "
543 "learning value '%s' invalid."),
544 filter->name, learning);
545 goto error;
546 }
547 } else {
548 goto err_unresolvable_vars;
549 }
550 } else if (virHashSize(missing_vars) > 1) {
551 goto err_unresolvable_vars;
552 } else if (!forceWithPendingReq &&
553 virNWFilterHasLearnReq(ifindex)) {
554 goto error;
555 }
556
557 rc = virNWFilterDefToInst(driver,
558 filter,
559 binding->filterparams,
560 useNewFilter, foundNewFilter,
561 &inst);
562
563 if (rc < 0)
564 goto error;
565
566 switch (useNewFilter) {
567 case INSTANTIATE_FOLLOW_NEWFILTER:
568 instantiate = *foundNewFilter;
569 break;
570 case INSTANTIATE_ALWAYS:
571 instantiate = true;
572 break;
573 }
574
575 if (instantiate) {
576 if (virNWFilterLockIface(binding->portdevname) < 0)
577 goto error;
578
579 rc = techdriver->applyNewRules(binding->portdevname, inst.rules, inst.nrules);
580
581 if (teardownOld && rc == 0)
582 techdriver->tearOldRules(binding->portdevname);
583
584 if (rc == 0 && (virNetDevValidateConfig(binding->portdevname, NULL, ifindex) <= 0)) {
585 virResetLastError();
586 /* interface changed/disappeared */
587 techdriver->allTeardown(binding->portdevname);
588 rc = -1;
589 }
590
591 virNWFilterUnlockIface(binding->portdevname);
592 }
593
594 error:
595 virNWFilterInstReset(&inst);
596 virHashFree(missing_vars);
597
598 return rc;
599
600 err_unresolvable_vars:
601
602 buf = virNWFilterPrintVars(missing_vars, ", ", false, reportIP);
603 if (buf) {
604 virReportError(VIR_ERR_INTERNAL_ERROR,
605 _("Cannot instantiate filter due to unresolvable "
606 "variables or unavailable list elements: %s"), buf);
607 }
608
609 rc = -1;
610 goto error;
611 }
612
613
614 static int
virNWFilterVarHashmapAddStdValue(GHashTable * table,const char * var,const char * value)615 virNWFilterVarHashmapAddStdValue(GHashTable *table,
616 const char *var,
617 const char *value)
618 {
619 virNWFilterVarValue *val;
620
621 if (virHashLookup(table, var))
622 return 0;
623
624 if (!(val = virNWFilterVarValueCreateSimpleCopyValue(value)))
625 return -1;
626
627 if (virHashAddEntry(table, var, val) < 0) {
628 virNWFilterVarValueFree(val);
629 return -1;
630 }
631
632 return 0;
633 }
634
635
636 /*
637 * Call this function while holding the NWFilter filter update lock
638 */
639 static int
virNWFilterInstantiateFilterUpdate(virNWFilterDriverState * driver,bool teardownOld,virNWFilterBindingDef * binding,int ifindex,enum instCase useNewFilter,bool forceWithPendingReq,bool * foundNewFilter)640 virNWFilterInstantiateFilterUpdate(virNWFilterDriverState *driver,
641 bool teardownOld,
642 virNWFilterBindingDef *binding,
643 int ifindex,
644 enum instCase useNewFilter,
645 bool forceWithPendingReq,
646 bool *foundNewFilter)
647 {
648 int rc = -1;
649 const char *drvname = EBIPTABLES_DRIVER_ID;
650 virNWFilterTechDriver *techdriver;
651 virNWFilterObj *obj;
652 virNWFilterDef *filter;
653 virNWFilterDef *newFilter;
654 char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
655 virNWFilterVarValue *ipaddr;
656
657 techdriver = virNWFilterTechDriverForName(drvname);
658
659 if (!techdriver) {
660 virReportError(VIR_ERR_INTERNAL_ERROR,
661 _("Could not get access to ACL tech "
662 "driver '%s'"),
663 drvname);
664 return -1;
665 }
666
667 VIR_DEBUG("filter name: %s", binding->filter);
668
669 if (!(obj = virNWFilterObjListFindInstantiateFilter(driver->nwfilters,
670 binding->filter)))
671 return -1;
672
673 virMacAddrFormat(&binding->mac, vmmacaddr);
674 if (virNWFilterVarHashmapAddStdValue(binding->filterparams,
675 NWFILTER_STD_VAR_MAC,
676 vmmacaddr) < 0)
677 goto error;
678
679 ipaddr = virNWFilterIPAddrMapGetIPAddr(binding->portdevname);
680 if (ipaddr &&
681 virNWFilterVarHashmapAddStdValue(binding->filterparams,
682 NWFILTER_STD_VAR_IP,
683 virNWFilterVarValueGetSimple(ipaddr)) < 0)
684 goto error;
685
686
687 filter = virNWFilterObjGetDef(obj);
688
689 switch (useNewFilter) {
690 case INSTANTIATE_FOLLOW_NEWFILTER:
691 newFilter = virNWFilterObjGetNewDef(obj);
692 if (newFilter) {
693 filter = newFilter;
694 *foundNewFilter = true;
695 }
696 break;
697
698 case INSTANTIATE_ALWAYS:
699 break;
700 }
701
702 rc = virNWFilterDoInstantiate(techdriver, binding, filter,
703 ifindex, useNewFilter, foundNewFilter,
704 teardownOld, driver,
705 forceWithPendingReq);
706
707 error:
708 virNWFilterObjUnlock(obj);
709
710 return rc;
711 }
712
713
714 static int
virNWFilterInstantiateFilterInternal(virNWFilterDriverState * driver,virNWFilterBindingDef * binding,bool teardownOld,enum instCase useNewFilter,bool * foundNewFilter)715 virNWFilterInstantiateFilterInternal(virNWFilterDriverState *driver,
716 virNWFilterBindingDef *binding,
717 bool teardownOld,
718 enum instCase useNewFilter,
719 bool *foundNewFilter)
720 {
721 int ifindex;
722 int rc;
723
724 virMutexLock(&updateMutex);
725
726 /* after grabbing the filter update lock check for the interface; if
727 it's not there anymore its filters will be or are being removed
728 (while holding the lock) and we don't want to build new ones */
729 if (virNetDevExists(binding->portdevname) != 1 ||
730 virNetDevGetIndex(binding->portdevname, &ifindex) < 0) {
731 /* interfaces / VMs can disappear during filter instantiation;
732 don't mark it as an error */
733 virResetLastError();
734 rc = 0;
735 goto cleanup;
736 }
737
738 rc = virNWFilterInstantiateFilterUpdate(driver, teardownOld,
739 binding,
740 ifindex,
741 useNewFilter,
742 false, foundNewFilter);
743
744 cleanup:
745 virMutexUnlock(&updateMutex);
746
747 return rc;
748 }
749
750
751 int
virNWFilterInstantiateFilterLate(virNWFilterDriverState * driver,virNWFilterBindingDef * binding,int ifindex)752 virNWFilterInstantiateFilterLate(virNWFilterDriverState *driver,
753 virNWFilterBindingDef *binding,
754 int ifindex)
755 {
756 int rc;
757 bool foundNewFilter = false;
758
759 virNWFilterReadLockFilterUpdates();
760 virMutexLock(&updateMutex);
761
762 rc = virNWFilterInstantiateFilterUpdate(driver, true,
763 binding, ifindex,
764 INSTANTIATE_ALWAYS, true,
765 &foundNewFilter);
766 if (rc < 0) {
767 /* something went wrong... 'DOWN' the interface */
768 if ((virNetDevValidateConfig(binding->portdevname, NULL, ifindex) <= 0) ||
769 (virNetDevSetOnline(binding->portdevname, false) < 0)) {
770 virResetLastError();
771 /* assuming interface disappeared... */
772 _virNWFilterTeardownFilter(binding->portdevname);
773 }
774 }
775
776 virNWFilterUnlockFilterUpdates();
777 virMutexUnlock(&updateMutex);
778
779 return rc;
780 }
781
782
783 int
virNWFilterInstantiateFilter(virNWFilterDriverState * driver,virNWFilterBindingDef * binding)784 virNWFilterInstantiateFilter(virNWFilterDriverState *driver,
785 virNWFilterBindingDef *binding)
786 {
787 bool foundNewFilter = false;
788
789 return virNWFilterInstantiateFilterInternal(driver, binding,
790 1,
791 INSTANTIATE_ALWAYS,
792 &foundNewFilter);
793 }
794
795
796 int
virNWFilterUpdateInstantiateFilter(virNWFilterDriverState * driver,virNWFilterBindingDef * binding,bool * skipIface)797 virNWFilterUpdateInstantiateFilter(virNWFilterDriverState *driver,
798 virNWFilterBindingDef *binding,
799 bool *skipIface)
800 {
801 bool foundNewFilter = false;
802
803 int rc = virNWFilterInstantiateFilterInternal(driver, binding,
804 0,
805 INSTANTIATE_FOLLOW_NEWFILTER,
806 &foundNewFilter);
807
808 *skipIface = !foundNewFilter;
809 return rc;
810 }
811
812 static int
virNWFilterRollbackUpdateFilter(virNWFilterBindingDef * binding)813 virNWFilterRollbackUpdateFilter(virNWFilterBindingDef *binding)
814 {
815 const char *drvname = EBIPTABLES_DRIVER_ID;
816 int ifindex;
817 virNWFilterTechDriver *techdriver;
818
819 techdriver = virNWFilterTechDriverForName(drvname);
820 if (!techdriver) {
821 virReportError(VIR_ERR_INTERNAL_ERROR,
822 _("Could not get access to ACL tech "
823 "driver '%s'"),
824 drvname);
825 return -1;
826 }
827
828 /* don't tear anything while the address is being learned */
829 if (virNetDevGetIndex(binding->portdevname, &ifindex) < 0)
830 virResetLastError();
831 else if (virNWFilterHasLearnReq(ifindex))
832 return 0;
833
834 return techdriver->tearNewRules(binding->portdevname);
835 }
836
837
838 static int
virNWFilterTearOldFilter(virNWFilterBindingDef * binding)839 virNWFilterTearOldFilter(virNWFilterBindingDef *binding)
840 {
841 const char *drvname = EBIPTABLES_DRIVER_ID;
842 int ifindex;
843 virNWFilterTechDriver *techdriver;
844
845 techdriver = virNWFilterTechDriverForName(drvname);
846 if (!techdriver) {
847 virReportError(VIR_ERR_INTERNAL_ERROR,
848 _("Could not get access to ACL tech "
849 "driver '%s'"),
850 drvname);
851 return -1;
852 }
853
854 /* don't tear anything while the address is being learned */
855 if (virNetDevGetIndex(binding->portdevname, &ifindex) < 0)
856 virResetLastError();
857 else if (virNWFilterHasLearnReq(ifindex))
858 return 0;
859
860 return techdriver->tearOldRules(binding->portdevname);
861 }
862
863
864 static int
_virNWFilterTeardownFilter(const char * ifname)865 _virNWFilterTeardownFilter(const char *ifname)
866 {
867 const char *drvname = EBIPTABLES_DRIVER_ID;
868 virNWFilterTechDriver *techdriver;
869 techdriver = virNWFilterTechDriverForName(drvname);
870
871 if (!techdriver) {
872 virReportError(VIR_ERR_INTERNAL_ERROR,
873 _("Could not get access to ACL tech "
874 "driver '%s'"),
875 drvname);
876 return -1;
877 }
878
879 virNWFilterDHCPSnoopEnd(ifname);
880
881 virNWFilterTerminateLearnReq(ifname);
882
883 if (virNWFilterLockIface(ifname) < 0)
884 return -1;
885
886 techdriver->allTeardown(ifname);
887
888 virNWFilterIPAddrMapDelIPAddr(ifname, NULL);
889
890 virNWFilterUnlockIface(ifname);
891
892 return 0;
893 }
894
895
896 int
virNWFilterTeardownFilter(virNWFilterBindingDef * binding)897 virNWFilterTeardownFilter(virNWFilterBindingDef *binding)
898 {
899 int ret;
900 virMutexLock(&updateMutex);
901 ret = _virNWFilterTeardownFilter(binding->portdevname);
902 virMutexUnlock(&updateMutex);
903 return ret;
904 }
905
906 enum {
907 STEP_APPLY_NEW,
908 STEP_ROLLBACK,
909 STEP_SWITCH,
910 STEP_APPLY_CURRENT,
911 };
912
913 static int
virNWFilterBuildOne(virNWFilterDriverState * driver,virNWFilterBindingDef * binding,GHashTable * skipInterfaces,int step)914 virNWFilterBuildOne(virNWFilterDriverState *driver,
915 virNWFilterBindingDef *binding,
916 GHashTable *skipInterfaces,
917 int step)
918 {
919 bool skipIface;
920 int ret = 0;
921 VIR_DEBUG("Building filter for portdev=%s step=%d", binding->portdevname, step);
922
923 switch (step) {
924 case STEP_APPLY_NEW:
925 ret = virNWFilterUpdateInstantiateFilter(driver,
926 binding,
927 &skipIface);
928 if (ret == 0 && skipIface) {
929 /* filter tree unchanged -- no update needed */
930 ret = virHashAddEntry(skipInterfaces,
931 binding->portdevname,
932 (void *)~0);
933 }
934 break;
935
936 case STEP_ROLLBACK:
937 if (!virHashLookup(skipInterfaces, binding->portdevname))
938 ret = virNWFilterRollbackUpdateFilter(binding);
939 break;
940
941 case STEP_SWITCH:
942 if (!virHashLookup(skipInterfaces, binding->portdevname))
943 ret = virNWFilterTearOldFilter(binding);
944 break;
945
946 case STEP_APPLY_CURRENT:
947 ret = virNWFilterInstantiateFilter(driver,
948 binding);
949 break;
950 }
951
952 return ret;
953 }
954
955
956 struct virNWFilterBuildData {
957 virNWFilterDriverState *driver;
958 GHashTable *skipInterfaces;
959 int step;
960 };
961
962 static int
virNWFilterBuildIter(virNWFilterBindingObj * binding,void * opaque)963 virNWFilterBuildIter(virNWFilterBindingObj *binding, void *opaque)
964 {
965 struct virNWFilterBuildData *data = opaque;
966 virNWFilterBindingDef *def = virNWFilterBindingObjGetDef(binding);
967
968 return virNWFilterBuildOne(data->driver, def,
969 data->skipInterfaces, data->step);
970 }
971
972 int
virNWFilterBuildAll(virNWFilterDriverState * driver,bool newFilters)973 virNWFilterBuildAll(virNWFilterDriverState *driver,
974 bool newFilters)
975 {
976 struct virNWFilterBuildData data = {
977 .driver = driver,
978 };
979 int ret = 0;
980
981 VIR_DEBUG("Build all filters newFilters=%d", newFilters);
982
983 if (newFilters) {
984 data.skipInterfaces = virHashNew(NULL);
985
986 data.step = STEP_APPLY_NEW;
987 if (virNWFilterBindingObjListForEach(driver->bindings,
988 virNWFilterBuildIter,
989 &data) < 0)
990 ret = -1;
991
992 if (ret == -1) {
993 data.step = STEP_ROLLBACK;
994 virNWFilterBindingObjListForEach(driver->bindings,
995 virNWFilterBuildIter,
996 &data);
997 } else {
998 data.step = STEP_SWITCH;
999 virNWFilterBindingObjListForEach(driver->bindings,
1000 virNWFilterBuildIter,
1001 &data);
1002 }
1003
1004 virHashFree(data.skipInterfaces);
1005 } else {
1006 data.step = STEP_APPLY_CURRENT;
1007 if (virNWFilterBindingObjListForEach(driver->bindings,
1008 virNWFilterBuildIter,
1009 &data) < 0)
1010 ret = -1;
1011 }
1012 return ret;
1013 }
1014