Skip to content

Commit 212ed75

Browse files
committed
netfilter: nf_tables: integrate pipapo into commit protocol
The pipapo set backend follows copy-on-update approach, maintaining one clone of the existing datastructure that is being updated. The clone and current datastructures are swapped via rcu from the commit step. The existing integration with the commit protocol is flawed because there is no operation to clean up the clone if the transaction is aborted. Moreover, the datastructure swap happens on set element activation. This patch adds two new operations for sets: commit and abort, these new operations are invoked from the commit and abort steps, after the transactions have been digested, and it updates the pipapo set backend to use it. This patch adds a new ->pending_update field to sets to maintain a list of sets that require this new commit and abort operations. Fixes: 3c4287f ("nf_tables: Add set type for arbitrary concatenation of ranges") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent ab39b11 commit 212ed75

File tree

3 files changed

+99
-16
lines changed

3 files changed

+99
-16
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,8 @@ struct nft_set_ops {
462462
const struct nft_set *set,
463463
const struct nft_set_elem *elem,
464464
unsigned int flags);
465-
465+
void (*commit)(const struct nft_set *set);
466+
void (*abort)(const struct nft_set *set);
466467
u64 (*privsize)(const struct nlattr * const nla[],
467468
const struct nft_set_desc *desc);
468469
bool (*estimate)(const struct nft_set_desc *desc,
@@ -557,6 +558,7 @@ struct nft_set {
557558
u16 policy;
558559
u16 udlen;
559560
unsigned char *udata;
561+
struct list_head pending_update;
560562
/* runtime data below here */
561563
const struct nft_set_ops *ops ____cacheline_aligned;
562564
u16 flags:14,

net/netfilter/nf_tables_api.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4919,6 +4919,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
49194919

49204920
set->num_exprs = num_exprs;
49214921
set->handle = nf_tables_alloc_handle(table);
4922+
INIT_LIST_HEAD(&set->pending_update);
49224923

49234924
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
49244925
if (err < 0)
@@ -9275,10 +9276,25 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
92759276
}
92769277
}
92779278

9279+
static void nft_set_commit_update(struct list_head *set_update_list)
9280+
{
9281+
struct nft_set *set, *next;
9282+
9283+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
9284+
list_del_init(&set->pending_update);
9285+
9286+
if (!set->ops->commit)
9287+
continue;
9288+
9289+
set->ops->commit(set);
9290+
}
9291+
}
9292+
92789293
static int nf_tables_commit(struct net *net, struct sk_buff *skb)
92799294
{
92809295
struct nftables_pernet *nft_net = nft_pernet(net);
92819296
struct nft_trans *trans, *next;
9297+
LIST_HEAD(set_update_list);
92829298
struct nft_trans_elem *te;
92839299
struct nft_chain *chain;
92849300
struct nft_table *table;
@@ -9453,6 +9469,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
94539469
nf_tables_setelem_notify(&trans->ctx, te->set,
94549470
&te->elem,
94559471
NFT_MSG_NEWSETELEM);
9472+
if (te->set->ops->commit &&
9473+
list_empty(&te->set->pending_update)) {
9474+
list_add_tail(&te->set->pending_update,
9475+
&set_update_list);
9476+
}
94569477
nft_trans_destroy(trans);
94579478
break;
94589479
case NFT_MSG_DELSETELEM:
@@ -9467,6 +9488,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
94679488
atomic_dec(&te->set->nelems);
94689489
te->set->ndeact--;
94699490
}
9491+
if (te->set->ops->commit &&
9492+
list_empty(&te->set->pending_update)) {
9493+
list_add_tail(&te->set->pending_update,
9494+
&set_update_list);
9495+
}
94709496
break;
94719497
case NFT_MSG_NEWOBJ:
94729498
if (nft_trans_obj_update(trans)) {
@@ -9529,6 +9555,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
95299555
}
95309556
}
95319557

9558+
nft_set_commit_update(&set_update_list);
9559+
95329560
nft_commit_notify(net, NETLINK_CB(skb).portid);
95339561
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
95349562
nf_tables_commit_audit_log(&adl, nft_net->base_seq);
@@ -9588,10 +9616,25 @@ static void nf_tables_abort_release(struct nft_trans *trans)
95889616
kfree(trans);
95899617
}
95909618

9619+
static void nft_set_abort_update(struct list_head *set_update_list)
9620+
{
9621+
struct nft_set *set, *next;
9622+
9623+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
9624+
list_del_init(&set->pending_update);
9625+
9626+
if (!set->ops->abort)
9627+
continue;
9628+
9629+
set->ops->abort(set);
9630+
}
9631+
}
9632+
95919633
static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
95929634
{
95939635
struct nftables_pernet *nft_net = nft_pernet(net);
95949636
struct nft_trans *trans, *next;
9637+
LIST_HEAD(set_update_list);
95959638
struct nft_trans_elem *te;
95969639

95979640
if (action == NFNL_ABORT_VALIDATE &&
@@ -9701,6 +9744,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
97019744
nft_setelem_remove(net, te->set, &te->elem);
97029745
if (!nft_setelem_is_catchall(te->set, &te->elem))
97039746
atomic_dec(&te->set->nelems);
9747+
9748+
if (te->set->ops->abort &&
9749+
list_empty(&te->set->pending_update)) {
9750+
list_add_tail(&te->set->pending_update,
9751+
&set_update_list);
9752+
}
97049753
break;
97059754
case NFT_MSG_DELSETELEM:
97069755
case NFT_MSG_DESTROYSETELEM:
@@ -9711,6 +9760,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
97119760
if (!nft_setelem_is_catchall(te->set, &te->elem))
97129761
te->set->ndeact--;
97139762

9763+
if (te->set->ops->abort &&
9764+
list_empty(&te->set->pending_update)) {
9765+
list_add_tail(&te->set->pending_update,
9766+
&set_update_list);
9767+
}
97149768
nft_trans_destroy(trans);
97159769
break;
97169770
case NFT_MSG_NEWOBJ:
@@ -9753,6 +9807,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
97539807
}
97549808
}
97559809

9810+
nft_set_abort_update(&set_update_list);
9811+
97569812
synchronize_rcu();
97579813

97589814
list_for_each_entry_safe_reverse(trans, next,

net/netfilter/nft_set_pipapo.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,17 +1600,10 @@ static void pipapo_free_fields(struct nft_pipapo_match *m)
16001600
}
16011601
}
16021602

1603-
/**
1604-
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1605-
* @rcu: RCU head
1606-
*/
1607-
static void pipapo_reclaim_match(struct rcu_head *rcu)
1603+
static void pipapo_free_match(struct nft_pipapo_match *m)
16081604
{
1609-
struct nft_pipapo_match *m;
16101605
int i;
16111606

1612-
m = container_of(rcu, struct nft_pipapo_match, rcu);
1613-
16141607
for_each_possible_cpu(i)
16151608
kfree(*per_cpu_ptr(m->scratch, i));
16161609

@@ -1625,7 +1618,19 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
16251618
}
16261619

16271620
/**
1628-
* pipapo_commit() - Replace lookup data with current working copy
1621+
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1622+
* @rcu: RCU head
1623+
*/
1624+
static void pipapo_reclaim_match(struct rcu_head *rcu)
1625+
{
1626+
struct nft_pipapo_match *m;
1627+
1628+
m = container_of(rcu, struct nft_pipapo_match, rcu);
1629+
pipapo_free_match(m);
1630+
}
1631+
1632+
/**
1633+
* nft_pipapo_commit() - Replace lookup data with current working copy
16291634
* @set: nftables API set representation
16301635
*
16311636
* While at it, check if we should perform garbage collection on the working
@@ -1635,7 +1640,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
16351640
* We also need to create a new working copy for subsequent insertions and
16361641
* deletions.
16371642
*/
1638-
static void pipapo_commit(const struct nft_set *set)
1643+
static void nft_pipapo_commit(const struct nft_set *set)
16391644
{
16401645
struct nft_pipapo *priv = nft_set_priv(set);
16411646
struct nft_pipapo_match *new_clone, *old;
@@ -1660,15 +1665,34 @@ static void pipapo_commit(const struct nft_set *set)
16601665
priv->clone = new_clone;
16611666
}
16621667

1668+
static void nft_pipapo_abort(const struct nft_set *set)
1669+
{
1670+
struct nft_pipapo *priv = nft_set_priv(set);
1671+
struct nft_pipapo_match *new_clone, *m;
1672+
1673+
if (!priv->dirty)
1674+
return;
1675+
1676+
m = rcu_dereference(priv->match);
1677+
1678+
new_clone = pipapo_clone(m);
1679+
if (IS_ERR(new_clone))
1680+
return;
1681+
1682+
priv->dirty = false;
1683+
1684+
pipapo_free_match(priv->clone);
1685+
priv->clone = new_clone;
1686+
}
1687+
16631688
/**
16641689
* nft_pipapo_activate() - Mark element reference as active given key, commit
16651690
* @net: Network namespace
16661691
* @set: nftables API set representation
16671692
* @elem: nftables API element representation containing key data
16681693
*
16691694
* On insertion, elements are added to a copy of the matching data currently
1670-
* in use for lookups, and not directly inserted into current lookup data, so
1671-
* we'll take care of that by calling pipapo_commit() here. Both
1695+
* in use for lookups, and not directly inserted into current lookup data. Both
16721696
* nft_pipapo_insert() and nft_pipapo_activate() are called once for each
16731697
* element, hence we can't purpose either one as a real commit operation.
16741698
*/
@@ -1684,8 +1708,6 @@ static void nft_pipapo_activate(const struct net *net,
16841708

16851709
nft_set_elem_change_active(net, set, &e->ext);
16861710
nft_set_elem_clear_busy(&e->ext);
1687-
1688-
pipapo_commit(set);
16891711
}
16901712

16911713
/**
@@ -1931,7 +1953,6 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set,
19311953
if (i == m->field_count) {
19321954
priv->dirty = true;
19331955
pipapo_drop(m, rulemap);
1934-
pipapo_commit(set);
19351956
return;
19361957
}
19371958

@@ -2230,6 +2251,8 @@ const struct nft_set_type nft_set_pipapo_type = {
22302251
.init = nft_pipapo_init,
22312252
.destroy = nft_pipapo_destroy,
22322253
.gc_init = nft_pipapo_gc_init,
2254+
.commit = nft_pipapo_commit,
2255+
.abort = nft_pipapo_abort,
22332256
.elemsize = offsetof(struct nft_pipapo_elem, ext),
22342257
},
22352258
};
@@ -2252,6 +2275,8 @@ const struct nft_set_type nft_set_pipapo_avx2_type = {
22522275
.init = nft_pipapo_init,
22532276
.destroy = nft_pipapo_destroy,
22542277
.gc_init = nft_pipapo_gc_init,
2278+
.commit = nft_pipapo_commit,
2279+
.abort = nft_pipapo_abort,
22552280
.elemsize = offsetof(struct nft_pipapo_elem, ext),
22562281
},
22572282
};

0 commit comments

Comments
 (0)