@@ -47,6 +47,15 @@ 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
+ */
+enum xtnetlink_transact_type {
+ XA_TABLE_BUFFER,
+};
+
+/**
* Per-client transaction state
* @netns: part of the tuple to uniquely identify client
* @use_count: tracking active operations on the TA's table
@@ -57,6 +66,8 @@ struct xtnetlink_pktref {
* entire ruleset at once from userspace, but has to collect it piecewise.
*
* @use_count is necessarily zero if no xtnl kernel code currently executes.
+ * Each client (uniquely identified by <netns,nladdr>) can have one buffer
+ * of every type (hence uniquely identified by <netns,nladdr,type>).
*/
struct xtnetlink_transact {
struct list_head anchor;
@@ -64,7 +75,10 @@ struct xtnetlink_transact {
uint32_t nladdr;
atomic_t use_count;
wait_queue_head_t waitq;
- struct xt2_table *table;
+ enum xtnetlink_transact_type type;
+ union {
+ struct xt2_table *table;
+ };
};
/**
@@ -100,7 +114,8 @@ static const unsigned int xtnetlink_revision_min; /* = 0; */
* helper, and shall further set xa->table before publishing the TA.
*/
static struct xtnetlink_transact *
-xtnetlink_transact_new(const struct net *net, uint32_t nladdr)
+xtnetlink_transact_new(const struct net *net, uint32_t nladdr,
+ enum xtnetlink_transact_type xa_type)
{
struct xtnetlink_transact *xa;
@@ -112,6 +127,7 @@ xtnetlink_transact_new(const struct net *net, uint32_t nladdr)
init_waitqueue_head(&xa->waitq);
xa->netns = net;
xa->nladdr = nladdr;
+ xa->type = xa_type;
xa->table = NULL;
return xa;
}
@@ -124,12 +140,14 @@ xtnetlink_transact_new(const struct net *net, uint32_t nladdr)
* The caller should hold appropriate locks.
*/
static struct xtnetlink_transact *
-xtnetlink_transact_lookup(const struct net *netns, uint32_t nladdr)
+xtnetlink_transact_lookup(const struct net *netns, uint32_t nladdr,
+ enum xtnetlink_transact_type xa_type)
{
struct xtnetlink_transact *e;
list_for_each_entry(e, &xtnetlink_transact_list, anchor)
- if (net_eq(e->netns, netns) && e->nladdr == nladdr)
+ if (net_eq(e->netns, netns) && e->nladdr == nladdr &&
+ e->type == xa_type)
return e;
return NULL;
}
@@ -143,12 +161,13 @@ xtnetlink_transact_lookup(const struct net *netns, uint32_t nladdr)
* The read lock ensures that no entry is going to disappear during the search.
*/
static struct xtnetlink_transact *
-xtnetlink_transact_get(struct net *netns, uint32_t nladdr)
+xtnetlink_transact_get(struct net *netns, uint32_t nladdr,
+ enum xtnetlink_transact_type xa_type)
{
struct xtnetlink_transact *xa;
read_lock(&xtnetlink_transact_lock);
- xa = xtnetlink_transact_lookup(netns, nladdr);
+ xa = xtnetlink_transact_lookup(netns, nladdr, xa_type);
if (xa != NULL)
atomic_inc(&xa->use_count);
read_unlock(&xtnetlink_transact_lock);
@@ -179,7 +198,8 @@ static int xtnetlink_transact_push(struct xtnetlink_transact *xa)
* for the same socket first.
*/
write_lock(&xtnetlink_transact_lock);
- if (xtnetlink_transact_lookup(xa->netns, xa->nladdr) != NULL) {
+ if (xtnetlink_transact_lookup(xa->netns, xa->nladdr,
+ xa->type) != NULL) {
write_unlock(&xtnetlink_transact_lock);
return -EEXIST;
}
@@ -212,7 +232,10 @@ static void xtnetlink_transact_pop(struct xtnetlink_transact *xa)
static void xtnetlink_transact_free(struct xtnetlink_transact *xa)
{
- xt2_table_free(xa->table);
+ if (xa->type == XA_TABLE_BUFFER) {
+ if (xa->table != NULL)
+ xt2_table_free(xa->table);
+ }
kfree(xa);
}
@@ -327,7 +350,7 @@ xtnetlink_table_wget(struct xtnetlink_transact **xa, struct net *net,
struct xt2_pernet_data *pnet;
struct xt2_table *table;
- *xa = xtnetlink_transact_get(net, nladdr);
+ *xa = xtnetlink_transact_get(net, nladdr, XA_TABLE_BUFFER);
if (*xa == NULL) {
pnet = xtables2_pernet(net);
mutex_lock(&pnet->master_lock);
@@ -359,7 +382,7 @@ static struct xt2_table *
xtnetlink_table_rget(struct xtnetlink_transact **xa, struct net *net,
uint32_t nladdr)
{
- *xa = xtnetlink_transact_get(net, nladdr);
+ *xa = xtnetlink_transact_get(net, nladdr, XA_TABLE_BUFFER);
rcu_read_lock();
if (*xa == NULL)
return rcu_dereference(xtables2_pernet(net)->master);
@@ -556,7 +579,8 @@ xtnetlink_table_replace(struct sock *xtnl, struct sk_buff *iskb,
struct xtnetlink_transact *xa;
int ret;
- xa = xtnetlink_transact_new(sock_net(xtnl), NETLINK_CB(iskb).portid);
+ xa = xtnetlink_transact_new(sock_net(xtnl), NETLINK_CB(iskb).portid,
+ XA_TABLE_BUFFER);
if (xa == NULL)
return -ENOMEM;
xa->table = xt2_table_new();
@@ -584,7 +608,8 @@ xtnetlink_commit(struct sock *xtnl, struct sk_buff *iskb,
struct xtnetlink_transact *xa;
struct xt2_table *old_table;
- xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid);
+ xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid,
+ XA_TABLE_BUFFER);
if (xa == NULL)
return xtnetlink_error(&ref, NFXTE_TRANSACT_INACTIVE);
@@ -610,7 +635,8 @@ xtnetlink_abort(struct sock *xtnl, struct sk_buff *iskb,
{.c_skb = iskb, .c_msg = imsg, .sock = xtnl};
struct xtnetlink_transact *xa;
- xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid);
+ xa = xtnetlink_transact_get(sock_net(xtnl), NETLINK_CB(iskb).portid,
+ XA_TABLE_BUFFER);
if (xa == NULL)
return xtnetlink_error(&ref, NFXTE_TRANSACT_INACTIVE);
@@ -894,7 +920,7 @@ xtnetlink_nlevent(struct notifier_block *blk, unsigned long event, void *ptr)
* 5. issue NFXTM_CHAIN_ADD, TA is reused (bad)
* 6. notifier runs eventually and late
*/
- xa = xtnetlink_transact_get(note->net, note->portid);
+ xa = xtnetlink_transact_get(note->net, note->portid, XA_TABLE_BUFFER);
if (xa != NULL) {
xtnetlink_transact_pop(xa);
xtnetlink_transact_free(xa);
There will be another buffer type, XA_SPLICE_BUFFER, to collect rules that userspace sent as an update. Signed-off-by: Jan Engelhardt <jengelh@inai.de> --- net/netfilter/xt_nfnetlink.c | 54 +++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 14 deletions(-)