In the past virFirewall required all rollback commands for a group
(those commands necessary to "undo" any rules that had been added in
that group in case of a later failure) to be manually added by
switching into the virFirewall object into "rollback mode" and then
re-calling the inverse of the exact virFirewallAddCmd*() APIs that had
been called to add the original rules (ie. for each
"iptables --insert" command, for rollback we would need to add a
command with all arguments identical except that "--insert" would be
replaced by "--delete").
Because nftables can't search for rules to remove by comparing all the
arguments (it instead expects *only* a handle that is provided via
stdout when the rule was originally added), we won't be able to follow
the iptables method and manually construct the command to undo any
given nft command by just duplicating all the args of the command
(except the action). Instead we will need to be able to automatically
create a rollback command at the time the rule-adding command is
executed (e.g. an "nft delete rule" command that would include the
rule handle returned in stdout by an "nft add rule" command).
In order to make this happen, we need to be able to 1) learn whether
the user of the virFirewall API desires this behavior (handled by a new
transaction flag called VIR_FIREWALL_TRANSACTION_AUTO_ROLLBACK that
can be retrieved with the new virFirewallTransactionGetFlags() API),
and 2) add a new command to the current group's rollback command list (with
the new virFirewallAddRollbackCmd()).
We will actually use this capability in an upcoming patch.
Signed-off-by: Laine Stump <laine(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/virfirewall.c | 55 +++++++++++++++++++++++++++++++++++-----
src/util/virfirewall.h | 7 +++++
3 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 457b51e289..b3cbe1a833 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2404,6 +2404,7 @@ virFileCacheSetPriv;
# util/virfirewall.h
virFirewallAddCmdFull;
+virFirewallAddRollbackCmd;
virFirewallApply;
virFirewallBackendTypeFromString;
virFirewallBackendTypeToString;
diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c
index 77de34533d..9def8999d5 100644
--- a/src/util/virfirewall.c
+++ b/src/util/virfirewall.c
@@ -198,10 +198,12 @@ void virFirewallFree(virFirewall *firewall)
fwCmd->args[fwCmd->argsLen++] = g_strdup(str); \
} while (0)
+
static virFirewallCmd *
virFirewallAddCmdFullV(virFirewall *firewall,
virFirewallLayer layer,
bool ignoreErrors,
+ bool isRollback,
virFirewallQueryCallback cb,
void *opaque,
va_list args)
@@ -218,18 +220,16 @@ virFirewallAddCmdFullV(virFirewall *firewall,
}
group = firewall->groups[firewall->currentGroup];
-
fwCmd = g_new0(virFirewallCmd, 1);
-
fwCmd->layer = layer;
- fwCmd->queryCB = cb;
- fwCmd->queryOpaque = opaque;
while ((str = va_arg(args, char *)) != NULL)
ADD_ARG(fwCmd, str);
- if (group->addingRollback) {
+ if (isRollback || group->addingRollback) {
fwCmd->ignoreErrors = true; /* always ignore errors when rolling back */
+ fwCmd->queryCB = NULL; /* rollback commands can't have a callback */
+ fwCmd->queryOpaque = NULL;
VIR_APPEND_ELEMENT_COPY(group->rollback, group->nrollback, fwCmd);
} else {
/* when not rolling back, ignore errors if this group (transaction)
@@ -237,6 +237,8 @@ virFirewallAddCmdFullV(virFirewall *firewall,
* if this specific rule was created with ignoreErrors == true
*/
fwCmd->ignoreErrors = ignoreErrors || (group->actionFlags &
VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
+ fwCmd->queryCB = cb;
+ fwCmd->queryOpaque = opaque;
VIR_APPEND_ELEMENT_COPY(group->action, group->naction, fwCmd);
}
@@ -277,7 +279,33 @@ virFirewallCmd *virFirewallAddCmdFull(virFirewall *firewall,
virFirewallCmd *fwCmd;
va_list args;
va_start(args, opaque);
- fwCmd = virFirewallAddCmdFullV(firewall, layer, ignoreErrors, cb, opaque, args);
+ fwCmd = virFirewallAddCmdFullV(firewall, layer, ignoreErrors, false, cb, opaque,
args);
+ va_end(args);
+ return fwCmd;
+}
+
+
+/**
+ * virFirewallAddRollbackCmd:
+ * @firewall: firewall commands to add to
+ * @layer: the firewall layer to change
+ * @...: NULL terminated list of strings for the command
+ *
+ * Add a command to the current firewall command group "rollback".
+ * Rollback commands always ignore errors and don't support any
+ * callbacks.
+ *
+ * Returns the new Command
+ */
+virFirewallCmd *
+virFirewallAddRollbackCmd(virFirewall *firewall,
+ virFirewallLayer layer,
+ ...)
+{
+ virFirewallCmd *fwCmd;
+ va_list args;
+ va_start(args, layer);
+ fwCmd = virFirewallAddCmdFullV(firewall, layer, true, true, NULL, NULL, args);
va_end(args);
return fwCmd;
}
@@ -434,6 +462,21 @@ void virFirewallStartTransaction(virFirewall *firewall,
firewall->currentGroup = firewall->ngroups - 1;
}
+
+/**
+ * virFirewallTransactionGetFlags:
+ * @firewall: the firewall to look at
+ *
+ * Returns the virFirewallTransactionFlags for the currently active
+ * group (transaction) in @firewall.
+ */
+static virFirewallTransactionFlags G_GNUC_UNUSED
+virFirewallTransactionGetFlags(virFirewall *firewall)
+{
+ return firewall->groups[firewall->currentGroup]->actionFlags;
+}
+
+
/**
* virFirewallBeginRollback:
* @firewall: the firewall ruleset
diff --git a/src/util/virfirewall.h b/src/util/virfirewall.h
index 1ca1cce10a..e6aac365f1 100644
--- a/src/util/virfirewall.h
+++ b/src/util/virfirewall.h
@@ -73,6 +73,11 @@ virFirewallCmd *virFirewallAddCmdFull(virFirewall *firewall,
...)
G_GNUC_NULL_TERMINATED;
+virFirewallCmd *virFirewallAddRollbackCmd(virFirewall *firewall,
+ virFirewallLayer layer,
+ ...)
+ G_GNUC_NULL_TERMINATED;
+
void virFirewallRemoveCmd(virFirewall *firewall,
virFirewallCmd *rule);
@@ -105,6 +110,8 @@ typedef enum {
/* Ignore all errors when applying rules, so no
* rollback block will be required */
VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS = (1 << 0),
+ /* Set to auto-add a rollback rule for each rule that is applied */
+ VIR_FIREWALL_TRANSACTION_AUTO_ROLLBACK = (1 << 1),
} virFirewallTransactionFlags;
void virFirewallStartTransaction(virFirewall *firewall,
--
2.44.0