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