@@ -11,6 +11,7 @@
* %NFXTM_CHAIN_MOVE: rename a chain
* %NFXTM_COMMIT: finalize and commit a transaction
* %NFXTM_TABLE_REPLACE:start a table replace transaction
+ * %NFXTM_ABORT: abort an active transaction
*/
enum nfxt_msg_type {
NFXTM_IDENTIFY = 1,
@@ -20,6 +21,7 @@ enum nfxt_msg_type {
NFXTM_CHAIN_MOVE,
NFXTM_COMMIT,
NFXTM_TABLE_REPLACE,
+ NFXTM_ABORT,
};
/**
@@ -522,6 +522,29 @@ xtnetlink_commit(struct sock *xtnl, struct sk_buff *iskb,
return xtnetlink_error(&ref, NFXTE_SUCCESS);
}
+/**
+ * NFXTM_ABORT: The client wants to explicitly abandon a transaction it itself
+ * had started earlier. Usually issued by the client when part of a
+ * transaction, for example a NFXTM_CHAIN_ADD within a
+ * NFXTM_TABLE_REPLACE..NFXTM_COMMIT window yielded ENOMEM.
+ */
+static int
+xtnetlink_abort(struct sock *xtnl, struct sk_buff *iskb,
+ const struct nlmsghdr *imsg, const struct nlattr *const *ad)
+{
+ struct xtnetlink_pktref ref =
+ {.c_skb = iskb, .c_msg = imsg, .sock = xtnl};
+ struct xtnetlink_transact *xa;
+
+ xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid);
+ if (xa == NULL)
+ return xtnetlink_error(&ref, NFXTE_TRANSACT_INACTIVE);
+
+ xtnetlink_transact_pop(xa);
+ xtnetlink_transact_free(xa);
+ return xtnetlink_error(&ref, NFXTE_SUCCESS);
+}
+
static const struct nla_policy xtnetlink_policy[] = {
[NFXTA_NAME] = {.type = NLA_NUL_STRING},
[NFXTA_ERRNO] = {.type = NLA_U32},
@@ -545,6 +568,7 @@ static const struct nfnl_callback xtnetlink_callback[] = {
[NFXTM_CHAIN_MOVE] = {.call = xtnetlink_chain_move, pol},
[NFXTM_COMMIT] = {.call = xtnetlink_commit, pol},
[NFXTM_TABLE_REPLACE] = {.call = xtnetlink_table_replace, pol},
+ [NFXTM_ABORT] = {.call = xtnetlink_abort, pol},
};
#undef pol
@@ -555,6 +579,9 @@ static const struct nfnetlink_subsystem xtnetlink_subsys = {
.cb_count = ARRAY_SIZE(xtnetlink_callback),
};
+/**
+ * Abort the transaction if the socket underneath us closes.
+ */
static int
xtnetlink_nlevent(struct notifier_block *blk, unsigned long event, void *ptr)
{
@@ -568,14 +595,20 @@ xtnetlink_nlevent(struct notifier_block *blk, unsigned long event, void *ptr)
* Is this needed, or do we have a user context in this NL notifier?
*
* If notifiers are not executed right when they are issued, this
- * becomes as a race, as a new NL socket could be created with the
- * same nladdr value (.portid member).
+ * becomes a race, as a new NL socket could be created with the
+ * same nladdr value (.portid member):
+ * 1. open xtnl socket
+ * 2. issue NFXTM_REPLACE_TABLE, new TA is started
+ * 3. close socket, notifier is queued
+ * 4. open xtnl socket
+ * 5. issue NFXTM_CHAIN_ADD, TA is reused (bad)
+ * 6. notifier runs eventually and late
*/
xa = xtnetlink_transact_get(note->net, note->portid);
- if (xa == NULL)
- return NOTIFY_DONE;
- xtnetlink_transact_pop(xa);
- xtnetlink_transact_free(xa);
+ if (xa != NULL) {
+ xtnetlink_transact_pop(xa);
+ xtnetlink_transact_free(xa);
+ }
return NOTIFY_DONE;
}
Support for the explicit ABORT operation over netlink. Signed-off-by: Jan Engelhardt <jengelh@inai.de> --- include/uapi/linux/netfilter/nfnetlink_xtables.h | 2 + net/netfilter/xt_nfnetlink.c | 45 +++++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-)