1 /*
2  * virfirewall.c: integration with firewalls
3  *
4  * Copyright (C) 2013-2015 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 <stdarg.h>
24 
25 #define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW
26 #include "virfirewallpriv.h"
27 #include "virfirewalld.h"
28 #include "viralloc.h"
29 #include "virerror.h"
30 #include "virstring.h"
31 #include "vircommand.h"
32 #include "virlog.h"
33 #include "virfile.h"
34 #include "virthread.h"
35 
36 #define VIR_FROM_THIS VIR_FROM_FIREWALL
37 
38 VIR_LOG_INIT("util.firewall");
39 
40 typedef struct _virFirewallGroup virFirewallGroup;
41 
42 VIR_ENUM_DECL(virFirewallLayerCommand);
43 VIR_ENUM_IMPL(virFirewallLayerCommand,
44               VIR_FIREWALL_LAYER_LAST,
45               EBTABLES,
46               IPTABLES,
47               IP6TABLES,
48 );
49 
50 struct _virFirewallRule {
51     virFirewallLayer layer;
52 
53     virFirewallQueryCallback queryCB;
54     void *queryOpaque;
55     bool ignoreErrors;
56 
57     size_t argsAlloc;
58     size_t argsLen;
59     char **args;
60 };
61 
62 struct _virFirewallGroup {
63     unsigned int actionFlags;
64     unsigned int rollbackFlags;
65 
66     size_t naction;
67     virFirewallRule **action;
68 
69     size_t nrollback;
70     virFirewallRule **rollback;
71 
72     bool addingRollback;
73 };
74 
75 
76 struct _virFirewall {
77     int err;
78 
79     size_t ngroups;
80     virFirewallGroup **groups;
81     size_t currentGroup;
82 };
83 
84 static virFirewallBackend currentBackend = VIR_FIREWALL_BACKEND_AUTOMATIC;
85 static virMutex ruleLock = VIR_MUTEX_INITIALIZER;
86 
87 static int
88 virFirewallValidateBackend(virFirewallBackend backend);
89 
90 static int
virFirewallOnceInit(void)91 virFirewallOnceInit(void)
92 {
93     return virFirewallValidateBackend(currentBackend);
94 }
95 
96 VIR_ONCE_GLOBAL_INIT(virFirewall);
97 
98 static int
virFirewallValidateBackend(virFirewallBackend backend)99 virFirewallValidateBackend(virFirewallBackend backend)
100 {
101     const char *commands[] = {
102         IPTABLES, IP6TABLES, EBTABLES
103     };
104     size_t i;
105 
106     for (i = 0; i < G_N_ELEMENTS(commands); i++) {
107         g_autofree char *path = virFindFileInPath(commands[i]);
108 
109         if (!path) {
110             virReportSystemError(errno,
111                                  _("%s not available, firewall backend will not function"),
112                                  commands[i]);
113             return -1;
114         }
115     }
116     VIR_DEBUG("found iptables/ip6tables/ebtables");
117 
118     if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC ||
119         backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
120         int rv = virFirewallDIsRegistered();
121 
122         VIR_DEBUG("Firewalld is registered ? %d", rv);
123 
124         if (rv == -1)
125             return -1;
126 
127         if (rv == -2) {
128             if (backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
129                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
130                                _("firewalld backend requested, but service is not running"));
131                 return -1;
132             } else {
133                 VIR_DEBUG("firewalld service not running, using direct backend");
134                 backend = VIR_FIREWALL_BACKEND_DIRECT;
135             }
136         } else {
137             VIR_DEBUG("firewalld service running, using firewalld backend");
138             backend = VIR_FIREWALL_BACKEND_FIREWALLD;
139         }
140     }
141 
142     currentBackend = backend;
143     return 0;
144 }
145 
146 int
virFirewallSetBackend(virFirewallBackend backend)147 virFirewallSetBackend(virFirewallBackend backend)
148 {
149     currentBackend = backend;
150 
151     if (virFirewallInitialize() < 0)
152         return -1;
153 
154     return virFirewallValidateBackend(backend);
155 }
156 
157 static virFirewallGroup *
virFirewallGroupNew(void)158 virFirewallGroupNew(void)
159 {
160     return g_new0(virFirewallGroup, 1);
161 }
162 
163 
164 /**
165  * virFirewallNew:
166  *
167  * Creates a new firewall ruleset for changing rules
168  * of @layer. This should be followed by a call to
169  * virFirewallStartTransaction before adding
170  * any rules
171  *
172  * Returns the new firewall ruleset
173  */
virFirewallNew(void)174 virFirewall *virFirewallNew(void)
175 {
176     virFirewall *firewall;
177 
178     if (virFirewallInitialize() < 0)
179         return NULL;
180 
181     firewall = g_new0(virFirewall, 1);
182 
183     return firewall;
184 }
185 
186 
187 static void
virFirewallRuleFree(virFirewallRule * rule)188 virFirewallRuleFree(virFirewallRule *rule)
189 {
190     size_t i;
191 
192     if (!rule)
193         return;
194 
195     for (i = 0; i < rule->argsLen; i++)
196         g_free(rule->args[i]);
197     g_free(rule->args);
198     g_free(rule);
199 }
200 
201 
202 static void
virFirewallGroupFree(virFirewallGroup * group)203 virFirewallGroupFree(virFirewallGroup *group)
204 {
205     size_t i;
206 
207     if (!group)
208         return;
209 
210     for (i = 0; i < group->naction; i++)
211         virFirewallRuleFree(group->action[i]);
212     g_free(group->action);
213 
214     for (i = 0; i < group->nrollback; i++)
215         virFirewallRuleFree(group->rollback[i]);
216     g_free(group->rollback);
217 
218     g_free(group);
219 }
220 
221 
222 /**
223  * virFirewallFree:
224  *
225  * Release all memory associated with the firewall
226  * ruleset
227  */
virFirewallFree(virFirewall * firewall)228 void virFirewallFree(virFirewall *firewall)
229 {
230     size_t i;
231 
232     if (!firewall)
233         return;
234 
235     for (i = 0; i < firewall->ngroups; i++)
236         virFirewallGroupFree(firewall->groups[i]);
237     g_free(firewall->groups);
238 
239     g_free(firewall);
240 }
241 
242 #define VIR_FIREWALL_RETURN_IF_ERROR(firewall) \
243     do { \
244         if (!firewall || firewall->err) \
245             return; \
246     } while (0)
247 
248 #define VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule)\
249     do { \
250         if (!firewall || firewall->err || !rule) \
251             return; \
252     } while (0)
253 
254 #define VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall) \
255     do { \
256         if (!firewall || firewall->err) \
257             return NULL; \
258     } while (0)
259 
260 #define ADD_ARG(rule, str) \
261     do { \
262         VIR_RESIZE_N(rule->args, rule->argsAlloc, rule->argsLen, 1); \
263         rule->args[rule->argsLen++] = g_strdup(str); \
264     } while (0)
265 
266 static virFirewallRule *
virFirewallAddRuleFullV(virFirewall * firewall,virFirewallLayer layer,bool ignoreErrors,virFirewallQueryCallback cb,void * opaque,va_list args)267 virFirewallAddRuleFullV(virFirewall *firewall,
268                         virFirewallLayer layer,
269                         bool ignoreErrors,
270                         virFirewallQueryCallback cb,
271                         void *opaque,
272                         va_list args)
273 {
274     virFirewallGroup *group;
275     virFirewallRule *rule;
276     char *str;
277 
278     VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall);
279 
280     if (firewall->ngroups == 0) {
281         firewall->err = EINVAL;
282         return NULL;
283     }
284     group = firewall->groups[firewall->currentGroup];
285 
286 
287     rule = g_new0(virFirewallRule, 1);
288 
289     rule->layer = layer;
290     rule->queryCB = cb;
291     rule->queryOpaque = opaque;
292     rule->ignoreErrors = ignoreErrors;
293 
294     switch (rule->layer) {
295     case VIR_FIREWALL_LAYER_ETHERNET:
296         ADD_ARG(rule, "--concurrent");
297         break;
298     case VIR_FIREWALL_LAYER_IPV4:
299         ADD_ARG(rule, "-w");
300         break;
301     case VIR_FIREWALL_LAYER_IPV6:
302         ADD_ARG(rule, "-w");
303         break;
304     case VIR_FIREWALL_LAYER_LAST:
305         break;
306     }
307 
308     while ((str = va_arg(args, char *)) != NULL)
309         ADD_ARG(rule, str);
310 
311     if (group->addingRollback) {
312         VIR_APPEND_ELEMENT_COPY(group->rollback, group->nrollback, rule);
313     } else {
314         VIR_APPEND_ELEMENT_COPY(group->action, group->naction, rule);
315     }
316 
317 
318     return rule;
319 }
320 
321 
322 /**
323  * virFirewallAddRuleFull:
324  * @firewall: firewall ruleset to add to
325  * @layer: the firewall layer to change
326  * @ignoreErrors: true to ignore failure of the command
327  * @cb: callback to invoke with result of query
328  * @opaque: data passed into @cb
329  * @...: NULL terminated list of strings for the rule
330  *
331  * Add any type of rule to the firewall ruleset. Any output
332  * generated by the addition will be fed into the query
333  * callback @cb. This callback is permitted to create new
334  * rules by invoking the virFirewallAddRule method, but
335  * is not permitted to start new transactions.
336  *
337  * If @ignoreErrors is set to TRUE, then any failure of
338  * the command is ignored. If it is set to FALSE, then
339  * the behaviour upon failure is determined by the flags
340  * set when the transaction was started.
341  *
342  * Returns the new rule
343  */
virFirewallAddRuleFull(virFirewall * firewall,virFirewallLayer layer,bool ignoreErrors,virFirewallQueryCallback cb,void * opaque,...)344 virFirewallRule *virFirewallAddRuleFull(virFirewall *firewall,
345                                           virFirewallLayer layer,
346                                           bool ignoreErrors,
347                                           virFirewallQueryCallback cb,
348                                           void *opaque,
349                                           ...)
350 {
351     virFirewallRule *rule;
352     va_list args;
353     va_start(args, opaque);
354     rule = virFirewallAddRuleFullV(firewall, layer, ignoreErrors, cb, opaque, args);
355     va_end(args);
356     return rule;
357 }
358 
359 
360 /**
361  * virFirewallRemoveRule:
362  * @firewall: firewall ruleset to remove from
363  * @rule: the rule to remove
364  *
365  * Remove a rule from the current transaction
366  */
virFirewallRemoveRule(virFirewall * firewall,virFirewallRule * rule)367 void virFirewallRemoveRule(virFirewall *firewall,
368                            virFirewallRule *rule)
369 {
370     size_t i;
371     virFirewallGroup *group;
372 
373     /* Explicitly not checking firewall->err too,
374      * because if rule was partially created
375      * before hitting error we must still remove
376      * it to avoid leaking 'rule'
377      */
378     if (!firewall)
379         return;
380 
381     if (firewall->ngroups == 0)
382         return;
383     group = firewall->groups[firewall->currentGroup];
384 
385     if (group->addingRollback) {
386         for (i = 0; i < group->nrollback; i++) {
387             if (group->rollback[i] == rule) {
388                 VIR_DELETE_ELEMENT(group->rollback,
389                                    i,
390                                    group->nrollback);
391                 virFirewallRuleFree(rule);
392                 break;
393             }
394         }
395     } else {
396         for (i = 0; i < group->naction; i++) {
397             if (group->action[i] == rule) {
398                 VIR_DELETE_ELEMENT(group->action,
399                                    i,
400                                    group->naction);
401                 virFirewallRuleFree(rule);
402                 return;
403             }
404         }
405     }
406 }
407 
408 
virFirewallRuleAddArg(virFirewall * firewall,virFirewallRule * rule,const char * arg)409 void virFirewallRuleAddArg(virFirewall *firewall,
410                            virFirewallRule *rule,
411                            const char *arg)
412 {
413     VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
414 
415     ADD_ARG(rule, arg);
416 
417     return;
418 }
419 
420 
virFirewallRuleAddArgFormat(virFirewall * firewall,virFirewallRule * rule,const char * fmt,...)421 void virFirewallRuleAddArgFormat(virFirewall *firewall,
422                                  virFirewallRule *rule,
423                                  const char *fmt, ...)
424 {
425     g_autofree char *arg = NULL;
426     va_list list;
427 
428     VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
429 
430     va_start(list, fmt);
431     arg = g_strdup_vprintf(fmt, list);
432     va_end(list);
433 
434     ADD_ARG(rule, arg);
435 
436     return;
437 }
438 
439 
virFirewallRuleAddArgSet(virFirewall * firewall,virFirewallRule * rule,const char * const * args)440 void virFirewallRuleAddArgSet(virFirewall *firewall,
441                               virFirewallRule *rule,
442                               const char *const *args)
443 {
444     VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
445 
446     while (*args) {
447         ADD_ARG(rule, *args);
448         args++;
449     }
450 
451     return;
452 }
453 
454 
virFirewallRuleAddArgList(virFirewall * firewall,virFirewallRule * rule,...)455 void virFirewallRuleAddArgList(virFirewall *firewall,
456                                virFirewallRule *rule,
457                                ...)
458 {
459     va_list list;
460     const char *str;
461 
462     VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
463 
464     va_start(list, rule);
465 
466     while ((str = va_arg(list, char *)) != NULL)
467         ADD_ARG(rule, str);
468 
469     va_end(list);
470 
471     return;
472 }
473 
474 
virFirewallRuleGetArgCount(virFirewallRule * rule)475 size_t virFirewallRuleGetArgCount(virFirewallRule *rule)
476 {
477     if (!rule)
478         return 0;
479     return rule->argsLen;
480 }
481 
482 
483 /**
484  * virFirewallStartTransaction:
485  * @firewall: the firewall ruleset
486  * @flags: bitset of virFirewallTransactionFlags
487  *
488  * Start a new transaction with associated rollback
489  * block.
490  *
491  * Should be followed by calls to add various rules to
492  * the transaction. Then virFirwallStartRollback should
493  * be used to provide rules to rollback upon transaction
494  * failure
495  */
virFirewallStartTransaction(virFirewall * firewall,unsigned int flags)496 void virFirewallStartTransaction(virFirewall *firewall,
497                                  unsigned int flags)
498 {
499     virFirewallGroup *group;
500 
501     VIR_FIREWALL_RETURN_IF_ERROR(firewall);
502 
503     group = virFirewallGroupNew();
504     group->actionFlags = flags;
505 
506     VIR_EXPAND_N(firewall->groups, firewall->ngroups, 1);
507     firewall->groups[firewall->ngroups - 1] = group;
508     firewall->currentGroup = firewall->ngroups - 1;
509 }
510 
511 /**
512  * virFirewallBeginRollback:
513  * @firewall: the firewall ruleset
514  * @flags: bitset of virFirewallRollbackFlags
515  *
516  * Mark the beginning of a set of rules able to rollback
517  * changes in this and all earlier transactions.
518  *
519  * Should be followed by calls to add various rules needed
520  * to rollback state. Then virFirewallStartTransaction
521  * should be used to indicate the beginning of the next
522  * transactional ruleset.
523  */
virFirewallStartRollback(virFirewall * firewall,unsigned int flags)524 void virFirewallStartRollback(virFirewall *firewall,
525                               unsigned int flags)
526 {
527     virFirewallGroup *group;
528 
529     VIR_FIREWALL_RETURN_IF_ERROR(firewall);
530 
531     if (firewall->ngroups == 0) {
532         firewall->err = EINVAL;
533         return;
534     }
535 
536     group = firewall->groups[firewall->ngroups-1];
537     group->rollbackFlags = flags;
538     group->addingRollback = true;
539 }
540 
541 
542 static char *
virFirewallRuleToString(virFirewallRule * rule)543 virFirewallRuleToString(virFirewallRule *rule)
544 {
545     const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
546     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
547     size_t i;
548 
549     virBufferAdd(&buf, bin, -1);
550     for (i = 0; i < rule->argsLen; i++) {
551         virBufferAddLit(&buf, " ");
552         virBufferAdd(&buf, rule->args[i], -1);
553     }
554 
555     return virBufferContentAndReset(&buf);
556 }
557 
558 static int
virFirewallApplyRuleDirect(virFirewallRule * rule,bool ignoreErrors,char ** output)559 virFirewallApplyRuleDirect(virFirewallRule *rule,
560                            bool ignoreErrors,
561                            char **output)
562 {
563     size_t i;
564     const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
565     g_autoptr(virCommand) cmd = NULL;
566     int status;
567     g_autofree char *error = NULL;
568 
569     if (!bin) {
570         virReportError(VIR_ERR_INTERNAL_ERROR,
571                        _("Unknown firewall layer %d"),
572                        rule->layer);
573         return -1;
574     }
575 
576     cmd = virCommandNewArgList(bin, NULL);
577 
578     for (i = 0; i < rule->argsLen; i++)
579         virCommandAddArg(cmd, rule->args[i]);
580 
581     virCommandSetOutputBuffer(cmd, output);
582     virCommandSetErrorBuffer(cmd, &error);
583 
584     if (virCommandRun(cmd, &status) < 0)
585         return -1;
586 
587     if (status != 0) {
588         if (ignoreErrors) {
589             VIR_DEBUG("Ignoring error running command");
590         } else {
591             g_autofree char *args = virCommandToString(cmd, false);
592             virReportError(VIR_ERR_INTERNAL_ERROR,
593                            _("Failed to apply firewall rules %s: %s"),
594                            NULLSTR(args), NULLSTR(error));
595             VIR_FREE(*output);
596             return -1;
597         }
598     }
599 
600     return 0;
601 }
602 
603 
604 static int G_GNUC_UNUSED
virFirewallApplyRuleFirewallD(virFirewallRule * rule,bool ignoreErrors,char ** output)605 virFirewallApplyRuleFirewallD(virFirewallRule *rule,
606                               bool ignoreErrors,
607                               char **output)
608 {
609     /* wrapper necessary because virFirewallRule is a private struct */
610     return virFirewallDApplyRule(rule->layer, rule->args, rule->argsLen, ignoreErrors, output);
611 }
612 
613 
614 void
virFirewallBackendSynchronize(void)615 virFirewallBackendSynchronize(void)
616 {
617     const char *arg = "-V";
618     g_autofree char *output = NULL;
619 
620     switch (currentBackend) {
621     case VIR_FIREWALL_BACKEND_DIRECT:
622         /* nobody to synchronize with */
623         break;
624     case VIR_FIREWALL_BACKEND_FIREWALLD:
625         /* Send a simple rule via firewalld's passthrough iptables
626          * command so that we'll be sure firewalld has fully
627          * initialized and caught up with its internal queue of
628          * iptables commands. Waiting for this will prevent our own
629          * directly-executed iptables commands from being run while
630          * firewalld is still initializing.
631          */
632         ignore_value(virFirewallDApplyRule(VIR_FIREWALL_LAYER_IPV4,
633                                            (char **)&arg, 1, true, &output));
634         VIR_DEBUG("Result of 'iptables -V' via firewalld: %s", NULLSTR(output));
635         break;
636     case VIR_FIREWALL_BACKEND_AUTOMATIC:
637     case VIR_FIREWALL_BACKEND_LAST:
638         break;
639     }
640 }
641 
642 
643 static int
virFirewallApplyRule(virFirewall * firewall,virFirewallRule * rule,bool ignoreErrors)644 virFirewallApplyRule(virFirewall *firewall,
645                      virFirewallRule *rule,
646                      bool ignoreErrors)
647 {
648     g_autofree char *output = NULL;
649     g_autofree char *str = virFirewallRuleToString(rule);
650     g_auto(GStrv) lines = NULL;
651     VIR_INFO("Applying rule '%s'", NULLSTR(str));
652 
653     if (rule->ignoreErrors)
654         ignoreErrors = rule->ignoreErrors;
655 
656     switch (currentBackend) {
657     case VIR_FIREWALL_BACKEND_DIRECT:
658         if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
659             return -1;
660         break;
661     case VIR_FIREWALL_BACKEND_FIREWALLD:
662         /* Since we are using raw iptables rules, there is no
663          * advantage to going through firewalld, so instead just add
664          * them directly rather that via dbus calls to firewalld. This
665          * has the useful side effect of eliminating extra unwanted
666          * warning messages in the system logs when trying to delete
667          * rules that don't exist (which is something that happens
668          * often when libvirtd is started, and *always* when firewalld
669          * is restarted)
670          */
671         if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
672             return -1;
673         break;
674 
675     case VIR_FIREWALL_BACKEND_AUTOMATIC:
676     case VIR_FIREWALL_BACKEND_LAST:
677     default:
678         virReportEnumRangeError(virFirewallBackend, currentBackend);
679         return -1;
680     }
681 
682     if (rule->queryCB && output) {
683         if (!(lines = g_strsplit(output, "\n", -1)))
684             return -1;
685 
686         VIR_DEBUG("Invoking query %p with '%s'", rule->queryCB, output);
687         if (rule->queryCB(firewall, rule->layer, (const char *const *)lines, rule->queryOpaque) < 0)
688             return -1;
689 
690         if (firewall->err) {
691             virReportSystemError(firewall->err, "%s",
692                                  _("Unable to create rule"));
693             return -1;
694         }
695 
696     }
697 
698     return 0;
699 }
700 
701 static int
virFirewallApplyGroup(virFirewall * firewall,size_t idx)702 virFirewallApplyGroup(virFirewall *firewall,
703                       size_t idx)
704 {
705     virFirewallGroup *group = firewall->groups[idx];
706     bool ignoreErrors = (group->actionFlags & VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
707     size_t i;
708 
709     VIR_INFO("Starting transaction for firewall=%p group=%p flags=0x%x",
710              firewall, group, group->actionFlags);
711     firewall->currentGroup = idx;
712     group->addingRollback = false;
713     for (i = 0; i < group->naction; i++) {
714         if (virFirewallApplyRule(firewall,
715                                  group->action[i],
716                                  ignoreErrors) < 0)
717             return -1;
718     }
719     return 0;
720 }
721 
722 
723 static void
virFirewallRollbackGroup(virFirewall * firewall,size_t idx)724 virFirewallRollbackGroup(virFirewall *firewall,
725                          size_t idx)
726 {
727     virFirewallGroup *group = firewall->groups[idx];
728     size_t i;
729 
730     VIR_INFO("Starting rollback for group %p", group);
731     firewall->currentGroup = idx;
732     group->addingRollback = true;
733     for (i = 0; i < group->nrollback; i++) {
734         ignore_value(virFirewallApplyRule(firewall,
735                                           group->rollback[i],
736                                           true));
737     }
738 }
739 
740 
741 int
virFirewallApply(virFirewall * firewall)742 virFirewallApply(virFirewall *firewall)
743 {
744     size_t i, j;
745     int ret = -1;
746 
747     virMutexLock(&ruleLock);
748 
749     if (currentBackend == VIR_FIREWALL_BACKEND_AUTOMATIC) {
750         /* a specific backend should have been set when the firewall
751          * object was created. If not, it means none was found.
752          */
753         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
754                        _("Failed to initialize a valid firewall backend"));
755         goto cleanup;
756     }
757     if (!firewall || firewall->err) {
758         int err = EINVAL;
759 
760         if (firewall)
761             err = firewall->err;
762 
763         virReportSystemError(err, "%s", _("Unable to create rule"));
764         goto cleanup;
765     }
766 
767     VIR_DEBUG("Applying groups for %p", firewall);
768     for (i = 0; i < firewall->ngroups; i++) {
769         if (virFirewallApplyGroup(firewall, i) < 0) {
770             size_t first = i;
771             virErrorPtr saved_error;
772 
773             VIR_DEBUG("Rolling back groups up to %zu for %p", i, firewall);
774 
775             virErrorPreserveLast(&saved_error);
776 
777             /*
778              * Look at any inheritance markers to figure out
779              * what the first rollback group we need to apply is
780              */
781             for (j = 0; j < i; j++) {
782                 VIR_DEBUG("Checking inheritance of group %zu", i - j);
783                 if (firewall->groups[i - j]->rollbackFlags &
784                     VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS)
785                     first = (i - j) - 1;
786             }
787             /*
788              * Now apply all rollback groups in order
789              */
790             for (j = first; j <= i; j++) {
791                 VIR_DEBUG("Rolling back group %zu", j);
792                 virFirewallRollbackGroup(firewall, j);
793             }
794 
795             virErrorRestore(&saved_error);
796             VIR_DEBUG("Done rolling back groups for %p", firewall);
797             goto cleanup;
798         }
799     }
800     VIR_DEBUG("Done applying groups for %p", firewall);
801 
802     ret = 0;
803  cleanup:
804     virMutexUnlock(&ruleLock);
805     return ret;
806 }
807