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