Skip to content

Commit d0e2c7d

Browse files
committed
netfilter: nf_tables: add NFT_CHAIN_BINDING
This new chain flag specifies that: * the kernel dynamically allocates the chain name, if no chain name is specified. * If the immediate expression that refers to this chain is removed, then this bound chain (and its content) is destroyed. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 04b7db4 commit d0e2c7d

File tree

4 files changed

+138
-13
lines changed

4 files changed

+138
-13
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,8 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
899899
return (void *)&rule->data[rule->dlen];
900900
}
901901

902+
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule);
903+
902904
static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
903905
struct nft_regs *regs,
904906
const struct nft_pktinfo *pkt)
@@ -944,7 +946,8 @@ struct nft_chain {
944946
struct nft_table *table;
945947
u64 handle;
946948
u32 use;
947-
u8 flags:6,
949+
u8 flags:5,
950+
bound:1,
948951
genmask:2;
949952
char *name;
950953

@@ -989,6 +992,14 @@ int nft_chain_validate_dependency(const struct nft_chain *chain,
989992
int nft_chain_validate_hooks(const struct nft_chain *chain,
990993
unsigned int hook_flags);
991994

995+
static inline bool nft_chain_is_bound(struct nft_chain *chain)
996+
{
997+
return (chain->flags & NFT_CHAIN_BINDING) && chain->bound;
998+
}
999+
1000+
void nft_chain_del(struct nft_chain *chain);
1001+
void nf_tables_chain_destroy(struct nft_ctx *ctx);
1002+
9921003
struct nft_stats {
9931004
u64 bytes;
9941005
u64 pkts;

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ enum nft_table_attributes {
187187
enum nft_chain_flags {
188188
NFT_CHAIN_BASE = (1 << 0),
189189
NFT_CHAIN_HW_OFFLOAD = (1 << 1),
190+
NFT_CHAIN_BINDING = (1 << 2),
190191
};
191192

192193
/**

net/netfilter/nf_tables_api.c

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,9 @@ static int nft_flush_table(struct nft_ctx *ctx)
10561056
if (!nft_is_active_next(ctx->net, chain))
10571057
continue;
10581058

1059+
if (nft_chain_is_bound(chain))
1060+
continue;
1061+
10591062
ctx->chain = chain;
10601063

10611064
err = nft_delrule_by_chain(ctx);
@@ -1098,6 +1101,9 @@ static int nft_flush_table(struct nft_ctx *ctx)
10981101
if (!nft_is_active_next(ctx->net, chain))
10991102
continue;
11001103

1104+
if (nft_chain_is_bound(chain))
1105+
continue;
1106+
11011107
ctx->chain = chain;
11021108

11031109
err = nft_delchain(ctx);
@@ -1413,13 +1419,12 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
14131419
lockdep_commit_lock_is_held(net));
14141420
if (nft_dump_stats(skb, stats))
14151421
goto nla_put_failure;
1416-
1417-
if ((chain->flags & NFT_CHAIN_HW_OFFLOAD) &&
1418-
nla_put_be32(skb, NFTA_CHAIN_FLAGS,
1419-
htonl(NFT_CHAIN_HW_OFFLOAD)))
1420-
goto nla_put_failure;
14211422
}
14221423

1424+
if (chain->flags &&
1425+
nla_put_be32(skb, NFTA_CHAIN_FLAGS, htonl(chain->flags)))
1426+
goto nla_put_failure;
1427+
14231428
if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
14241429
goto nla_put_failure;
14251430

@@ -1621,7 +1626,7 @@ static void nf_tables_chain_free_chain_rules(struct nft_chain *chain)
16211626
kvfree(chain->rules_next);
16221627
}
16231628

1624-
static void nf_tables_chain_destroy(struct nft_ctx *ctx)
1629+
void nf_tables_chain_destroy(struct nft_ctx *ctx)
16251630
{
16261631
struct nft_chain *chain = ctx->chain;
16271632
struct nft_hook *hook, *next;
@@ -1928,6 +1933,8 @@ static int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
19281933
return 0;
19291934
}
19301935

1936+
static u64 chain_id;
1937+
19311938
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
19321939
u8 policy, u32 flags)
19331940
{
@@ -1936,6 +1943,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
19361943
struct nft_base_chain *basechain;
19371944
struct nft_stats __percpu *stats;
19381945
struct net *net = ctx->net;
1946+
char name[NFT_NAME_MAXLEN];
19391947
struct nft_trans *trans;
19401948
struct nft_chain *chain;
19411949
struct nft_rule **rules;
@@ -1947,6 +1955,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
19471955
if (nla[NFTA_CHAIN_HOOK]) {
19481956
struct nft_chain_hook hook;
19491957

1958+
if (flags & NFT_CHAIN_BINDING)
1959+
return -EOPNOTSUPP;
1960+
19501961
err = nft_chain_parse_hook(net, nla, &hook, family, true);
19511962
if (err < 0)
19521963
return err;
@@ -1976,16 +1987,33 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
19761987
return err;
19771988
}
19781989
} else {
1990+
if (flags & NFT_CHAIN_BASE)
1991+
return -EINVAL;
1992+
if (flags & NFT_CHAIN_HW_OFFLOAD)
1993+
return -EOPNOTSUPP;
1994+
19791995
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
19801996
if (chain == NULL)
19811997
return -ENOMEM;
1998+
1999+
chain->flags = flags;
19822000
}
19832001
ctx->chain = chain;
19842002

19852003
INIT_LIST_HEAD(&chain->rules);
19862004
chain->handle = nf_tables_alloc_handle(table);
19872005
chain->table = table;
1988-
chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
2006+
2007+
if (nla[NFTA_CHAIN_NAME]) {
2008+
chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
2009+
} else {
2010+
if (!(flags & NFT_CHAIN_BINDING))
2011+
return -EINVAL;
2012+
2013+
snprintf(name, sizeof(name), "__chain%llu", ++chain_id);
2014+
chain->name = kstrdup(name, GFP_KERNEL);
2015+
}
2016+
19892017
if (!chain->name) {
19902018
err = -ENOMEM;
19912019
goto err1;
@@ -2976,8 +3004,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
29763004
kfree(rule);
29773005
}
29783006

2979-
static void nf_tables_rule_release(const struct nft_ctx *ctx,
2980-
struct nft_rule *rule)
3007+
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
29813008
{
29823009
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
29833010
nf_tables_rule_destroy(ctx, rule);
@@ -3075,6 +3102,9 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
30753102
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
30763103
return PTR_ERR(chain);
30773104
}
3105+
if (nft_chain_is_bound(chain))
3106+
return -EOPNOTSUPP;
3107+
30783108
} else if (nla[NFTA_RULE_CHAIN_ID]) {
30793109
chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]);
30803110
if (IS_ERR(chain)) {
@@ -3294,6 +3324,8 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
32943324
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
32953325
return PTR_ERR(chain);
32963326
}
3327+
if (nft_chain_is_bound(chain))
3328+
return -EOPNOTSUPP;
32973329
}
32983330

32993331
nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
@@ -5330,11 +5362,24 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
53305362
*/
53315363
void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
53325364
{
5365+
struct nft_chain *chain;
5366+
struct nft_rule *rule;
5367+
53335368
if (type == NFT_DATA_VERDICT) {
53345369
switch (data->verdict.code) {
53355370
case NFT_JUMP:
53365371
case NFT_GOTO:
5337-
data->verdict.chain->use++;
5372+
chain = data->verdict.chain;
5373+
chain->use++;
5374+
5375+
if (!nft_chain_is_bound(chain))
5376+
break;
5377+
5378+
chain->table->use++;
5379+
list_for_each_entry(rule, &chain->rules, list)
5380+
chain->use++;
5381+
5382+
nft_chain_add(chain->table, chain);
53385383
break;
53395384
}
53405385
}
@@ -7474,7 +7519,7 @@ static void nft_obj_del(struct nft_object *obj)
74747519
list_del_rcu(&obj->list);
74757520
}
74767521

7477-
static void nft_chain_del(struct nft_chain *chain)
7522+
void nft_chain_del(struct nft_chain *chain)
74787523
{
74797524
struct nft_table *table = chain->table;
74807525

@@ -7825,6 +7870,10 @@ static int __nf_tables_abort(struct net *net, bool autoload)
78257870
kfree(nft_trans_chain_name(trans));
78267871
nft_trans_destroy(trans);
78277872
} else {
7873+
if (nft_chain_is_bound(trans->ctx.chain)) {
7874+
nft_trans_destroy(trans);
7875+
break;
7876+
}
78287877
trans->ctx.table->use--;
78297878
nft_chain_del(trans->ctx.chain);
78307879
nf_tables_unregister_hook(trans->ctx.net,
@@ -8321,10 +8370,23 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
83218370

83228371
static void nft_verdict_uninit(const struct nft_data *data)
83238372
{
8373+
struct nft_chain *chain;
8374+
struct nft_rule *rule;
8375+
83248376
switch (data->verdict.code) {
83258377
case NFT_JUMP:
83268378
case NFT_GOTO:
8327-
data->verdict.chain->use--;
8379+
chain = data->verdict.chain;
8380+
chain->use--;
8381+
8382+
if (!nft_chain_is_bound(chain))
8383+
break;
8384+
8385+
chain->table->use--;
8386+
list_for_each_entry(rule, &chain->rules, list)
8387+
chain->use--;
8388+
8389+
nft_chain_del(chain);
83288390
break;
83298391
}
83308392
}

net/netfilter/nft_immediate.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
5454
if (err < 0)
5555
goto err1;
5656

57+
if (priv->dreg == NFT_REG_VERDICT) {
58+
struct nft_chain *chain = priv->data.verdict.chain;
59+
60+
switch (priv->data.verdict.code) {
61+
case NFT_JUMP:
62+
case NFT_GOTO:
63+
if (nft_chain_is_bound(chain)) {
64+
err = -EBUSY;
65+
goto err1;
66+
}
67+
chain->bound = true;
68+
break;
69+
default:
70+
break;
71+
}
72+
}
73+
5774
return 0;
5875

5976
err1:
@@ -81,6 +98,39 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
8198
return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg));
8299
}
83100

101+
static void nft_immediate_destroy(const struct nft_ctx *ctx,
102+
const struct nft_expr *expr)
103+
{
104+
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
105+
const struct nft_data *data = &priv->data;
106+
struct nft_ctx chain_ctx;
107+
struct nft_chain *chain;
108+
struct nft_rule *rule;
109+
110+
if (priv->dreg != NFT_REG_VERDICT)
111+
return;
112+
113+
switch (data->verdict.code) {
114+
case NFT_JUMP:
115+
case NFT_GOTO:
116+
chain = data->verdict.chain;
117+
118+
if (!nft_chain_is_bound(chain))
119+
break;
120+
121+
chain_ctx = *ctx;
122+
chain_ctx.chain = chain;
123+
124+
list_for_each_entry(rule, &chain->rules, list)
125+
nf_tables_rule_release(&chain_ctx, rule);
126+
127+
nf_tables_chain_destroy(&chain_ctx);
128+
break;
129+
default:
130+
break;
131+
}
132+
}
133+
84134
static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr)
85135
{
86136
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
@@ -170,6 +220,7 @@ static const struct nft_expr_ops nft_imm_ops = {
170220
.init = nft_immediate_init,
171221
.activate = nft_immediate_activate,
172222
.deactivate = nft_immediate_deactivate,
223+
.destroy = nft_immediate_destroy,
173224
.dump = nft_immediate_dump,
174225
.validate = nft_immediate_validate,
175226
.offload = nft_immediate_offload,

0 commit comments

Comments
 (0)