@@ -50,9 +50,21 @@ struct xtnetlink_pktref {
* Multiple types of transaction buffers do exist -
* %XA_TABLE_BUFFER: holds chains accumulating during any operation during
* %NFXTM_TABLE_REPLACE .. %NFXTM_COMMIT period
+ * %XA_SPLICE_BUFFER: holds rules accumulating during %NFXTM_CHAIN_SPLICE ..
+ * %NFXTM_COMMIT period
*/
enum xtnetlink_transact_type {
XA_TABLE_BUFFER,
+ XA_SPLICE_BUFFER,
+};
+
+/**
+ * This struct is used to record splice parameters and the ruleset until
+ * %NFXTM_COMMIT is commanded and xt2_chain_splice is called.
+ */
+struct xtnetlink_splice_param {
+ char name[sizeof((struct xt2_chain *)NULL)->name];
+ unsigned int offset, dlength;
};
/**
@@ -78,6 +90,7 @@ struct xtnetlink_transact {
enum xtnetlink_transact_type type;
union {
struct xt2_table *table;
+ struct xtnetlink_splice_param *splice_param;
};
};
@@ -235,6 +248,9 @@ static void xtnetlink_transact_free(struct xtnetlink_transact *xa)
if (xa->type == XA_TABLE_BUFFER) {
if (xa->table != NULL)
xt2_table_free(xa->table);
+ } else if (xa->type == XA_SPLICE_BUFFER) {
+ if (xa->splice_param != NULL)
+ kfree(xa->splice_param);
}
kfree(xa);
}
@@ -579,6 +595,8 @@ xtnetlink_table_replace(struct sock *xtnl, struct sk_buff *iskb,
struct xtnetlink_transact *xa;
int ret;
+ /* check that no XA_SPLICE_BUFFER is active */
+
xa = xtnetlink_transact_new(sock_net(xtnl), NETLINK_CB(iskb).portid,
XA_TABLE_BUFFER);
if (xa == NULL)
@@ -636,6 +654,19 @@ xtnetlink_abort(struct sock *xtnl, struct sk_buff *iskb,
struct xtnetlink_transact *xa;
xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid,
+ XA_SPLICE_BUFFER);
+ if (xa != NULL) {
+ /*
+ * CHAIN_SPLICE context is always topmost (one cannot call
+ * TABLE_REPLACE from CHAIN_SPLICE ctx), so if a rule buffer
+ * is present, abort that.
+ */
+ xtnetlink_transact_pop(xa);
+ xtnetlink_transact_free(xa);
+ return xtnetlink_error(&ref, NFXTE_SUCCESS);
+ }
+
+ xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid,
XA_TABLE_BUFFER);
if (xa == NULL)
return xtnetlink_error(&ref, NFXTE_TRANSACT_INACTIVE);
@@ -906,6 +937,12 @@ xtnetlink_nlevent(struct notifier_block *blk, unsigned long event, void *ptr)
if (event != NETLINK_URELEASE || note->protocol != NETLINK_NETFILTER)
return NOTIFY_DONE;
+
+ xa = xtnetlink_transact_get(note->net, note->portid, XA_SPLICE_BUFFER);
+ if (xa != NULL) {
+ xtnetlink_transact_pop(xa);
+ xtnetlink_transact_free(xa);
+ }
/*
* Freeing is non-sleeping thanks to kfree_rcu in xt2_table_free.
* Is this needed, or do we have a user context in this NL notifier?
Signed-off-by: Jan Engelhardt <jengelh@inai.de> --- net/netfilter/xt_nfnetlink.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)