aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2015-04-05 08:41:08 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2015-04-08 10:58:27 -0400
commit22fe54d5fefcfa98c58cc2f4607dd26d9648b3f5 (patch)
tree153c791a6efb2c0eb7aca4baecb84cb76199b706 /net
parent11113e190bf0ad73086884f87efccc994ff28b3d (diff)
netfilter: nf_tables: add support for dynamic set updates
Add a new "dynset" expression for dynamic set updates. A new set op ->update() is added which, for non existant elements, invokes an initialization callback and inserts the new element. For both new or existing elements the extenstion pointer is returned to the caller to optionally perform timer updates or other actions. Element removal is not supported so far, however that seems to be a rather exotic need and can be added later on. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/Makefile2
-rw-r--r--net/netfilter/nf_tables_api.c10
-rw-r--r--net/netfilter/nf_tables_core.c7
-rw-r--r--net/netfilter/nft_dynset.c218
-rw-r--r--net/netfilter/nft_hash.c37
5 files changed, 268 insertions, 6 deletions
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 89f73a9e9874..a87d8b8ec730 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -70,7 +70,7 @@ obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
70 70
71# nf_tables 71# nf_tables
72nf_tables-objs += nf_tables_core.o nf_tables_api.o 72nf_tables-objs += nf_tables_core.o nf_tables_api.o
73nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o 73nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
74nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o 74nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
75 75
76obj-$(CONFIG_NF_TABLES) += nf_tables.o 76obj-$(CONFIG_NF_TABLES) += nf_tables.o
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 90b898491da7..598e53eb64b3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3183,11 +3183,11 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
3183 return trans; 3183 return trans;
3184} 3184}
3185 3185
3186static void *nft_set_elem_init(const struct nft_set *set, 3186void *nft_set_elem_init(const struct nft_set *set,
3187 const struct nft_set_ext_tmpl *tmpl, 3187 const struct nft_set_ext_tmpl *tmpl,
3188 const struct nft_data *key, 3188 const struct nft_data *key,
3189 const struct nft_data *data, 3189 const struct nft_data *data,
3190 u64 timeout, gfp_t gfp) 3190 u64 timeout, gfp_t gfp)
3191{ 3191{
3192 struct nft_set_ext *ext; 3192 struct nft_set_ext *ext;
3193 void *elem; 3193 void *elem;
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
index ef4dfcbaf149..7caf08a9225d 100644
--- a/net/netfilter/nf_tables_core.c
+++ b/net/netfilter/nf_tables_core.c
@@ -239,8 +239,14 @@ int __init nf_tables_core_module_init(void)
239 if (err < 0) 239 if (err < 0)
240 goto err6; 240 goto err6;
241 241
242 err = nft_dynset_module_init();
243 if (err < 0)
244 goto err7;
245
242 return 0; 246 return 0;
243 247
248err7:
249 nft_payload_module_exit();
244err6: 250err6:
245 nft_byteorder_module_exit(); 251 nft_byteorder_module_exit();
246err5: 252err5:
@@ -257,6 +263,7 @@ err1:
257 263
258void nf_tables_core_module_exit(void) 264void nf_tables_core_module_exit(void)
259{ 265{
266 nft_dynset_module_exit();
260 nft_payload_module_exit(); 267 nft_payload_module_exit();
261 nft_byteorder_module_exit(); 268 nft_byteorder_module_exit();
262 nft_bitwise_module_exit(); 269 nft_bitwise_module_exit();
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
new file mode 100644
index 000000000000..eeb72dee78ef
--- /dev/null
+++ b/net/netfilter/nft_dynset.c
@@ -0,0 +1,218 @@
1/*
2 * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/netlink.h>
14#include <linux/netfilter.h>
15#include <linux/netfilter/nf_tables.h>
16#include <net/netfilter/nf_tables.h>
17#include <net/netfilter/nf_tables_core.h>
18
19struct nft_dynset {
20 struct nft_set *set;
21 struct nft_set_ext_tmpl tmpl;
22 enum nft_dynset_ops op:8;
23 enum nft_registers sreg_key:8;
24 enum nft_registers sreg_data:8;
25 u64 timeout;
26 struct nft_set_binding binding;
27};
28
29static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
30 struct nft_data data[NFT_REG_MAX + 1])
31{
32 const struct nft_dynset *priv = nft_expr_priv(expr);
33 u64 timeout;
34 void *elem;
35
36 if (set->size && !atomic_add_unless(&set->nelems, 1, set->size))
37 return NULL;
38
39 timeout = priv->timeout ? : set->timeout;
40 elem = nft_set_elem_init(set, &priv->tmpl,
41 &data[priv->sreg_key], &data[priv->sreg_data],
42 timeout, GFP_ATOMIC);
43 if (elem == NULL) {
44 if (set->size)
45 atomic_dec(&set->nelems);
46 }
47 return elem;
48}
49
50static void nft_dynset_eval(const struct nft_expr *expr,
51 struct nft_data data[NFT_REG_MAX + 1],
52 const struct nft_pktinfo *pkt)
53{
54 const struct nft_dynset *priv = nft_expr_priv(expr);
55 struct nft_set *set = priv->set;
56 const struct nft_set_ext *ext;
57 u64 timeout;
58
59 if (set->ops->update(set, &data[priv->sreg_key], nft_dynset_new,
60 expr, data, &ext)) {
61 if (priv->op == NFT_DYNSET_OP_UPDATE &&
62 nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
63 timeout = priv->timeout ? : set->timeout;
64 *nft_set_ext_expiration(ext) = jiffies + timeout;
65 return;
66 }
67 }
68
69 data[NFT_REG_VERDICT].verdict = NFT_BREAK;
70}
71
72static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
73 [NFTA_DYNSET_SET_NAME] = { .type = NLA_STRING },
74 [NFTA_DYNSET_SET_ID] = { .type = NLA_U32 },
75 [NFTA_DYNSET_OP] = { .type = NLA_U32 },
76 [NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
77 [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
78 [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
79};
80
81static int nft_dynset_init(const struct nft_ctx *ctx,
82 const struct nft_expr *expr,
83 const struct nlattr * const tb[])
84{
85 struct nft_dynset *priv = nft_expr_priv(expr);
86 struct nft_set *set;
87 u64 timeout;
88 int err;
89
90 if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
91 tb[NFTA_DYNSET_OP] == NULL ||
92 tb[NFTA_DYNSET_SREG_KEY] == NULL)
93 return -EINVAL;
94
95 set = nf_tables_set_lookup(ctx->table, tb[NFTA_DYNSET_SET_NAME]);
96 if (IS_ERR(set)) {
97 if (tb[NFTA_DYNSET_SET_ID])
98 set = nf_tables_set_lookup_byid(ctx->net,
99 tb[NFTA_DYNSET_SET_ID]);
100 if (IS_ERR(set))
101 return PTR_ERR(set);
102 }
103
104 if (set->flags & NFT_SET_CONSTANT)
105 return -EBUSY;
106
107 priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
108 switch (priv->op) {
109 case NFT_DYNSET_OP_ADD:
110 break;
111 case NFT_DYNSET_OP_UPDATE:
112 if (!(set->flags & NFT_SET_TIMEOUT))
113 return -EOPNOTSUPP;
114 break;
115 default:
116 return -EOPNOTSUPP;
117 }
118
119 timeout = 0;
120 if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
121 if (!(set->flags & NFT_SET_TIMEOUT))
122 return -EINVAL;
123 timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
124 }
125
126 priv->sreg_key = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_KEY]));
127 err = nft_validate_input_register(priv->sreg_key);
128 if (err < 0)
129 return err;
130
131 if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
132 if (!(set->flags & NFT_SET_MAP))
133 return -EINVAL;
134 if (set->dtype == NFT_DATA_VERDICT)
135 return -EOPNOTSUPP;
136
137 priv->sreg_data = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_DATA]));
138 err = nft_validate_input_register(priv->sreg_data);
139 if (err < 0)
140 return err;
141 } else if (set->flags & NFT_SET_MAP)
142 return -EINVAL;
143
144 nft_set_ext_prepare(&priv->tmpl);
145 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
146 if (set->flags & NFT_SET_MAP)
147 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
148 if (set->flags & NFT_SET_TIMEOUT) {
149 if (timeout || set->timeout)
150 nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
151 }
152
153 priv->timeout = timeout;
154
155 err = nf_tables_bind_set(ctx, set, &priv->binding);
156 if (err < 0)
157 return err;
158
159 priv->set = set;
160 return 0;
161}
162
163static void nft_dynset_destroy(const struct nft_ctx *ctx,
164 const struct nft_expr *expr)
165{
166 struct nft_dynset *priv = nft_expr_priv(expr);
167
168 nf_tables_unbind_set(ctx, priv->set, &priv->binding);
169}
170
171static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
172{
173 const struct nft_dynset *priv = nft_expr_priv(expr);
174
175 if (nla_put_be32(skb, NFTA_DYNSET_SREG_KEY, htonl(priv->sreg_key)))
176 goto nla_put_failure;
177 if (priv->set->flags & NFT_SET_MAP &&
178 nla_put_be32(skb, NFTA_DYNSET_SREG_DATA, htonl(priv->sreg_data)))
179 goto nla_put_failure;
180 if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
181 goto nla_put_failure;
182 if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
183 goto nla_put_failure;
184 if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
185 goto nla_put_failure;
186 return 0;
187
188nla_put_failure:
189 return -1;
190}
191
192static struct nft_expr_type nft_dynset_type;
193static const struct nft_expr_ops nft_dynset_ops = {
194 .type = &nft_dynset_type,
195 .size = NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
196 .eval = nft_dynset_eval,
197 .init = nft_dynset_init,
198 .destroy = nft_dynset_destroy,
199 .dump = nft_dynset_dump,
200};
201
202static struct nft_expr_type nft_dynset_type __read_mostly = {
203 .name = "dynset",
204 .ops = &nft_dynset_ops,
205 .policy = nft_dynset_policy,
206 .maxattr = NFTA_DYNSET_MAX,
207 .owner = THIS_MODULE,
208};
209
210int __init nft_dynset_module_init(void)
211{
212 return nft_register_expr(&nft_dynset_type);
213}
214
215void nft_dynset_module_exit(void)
216{
217 nft_unregister_expr(&nft_dynset_type);
218}
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index c74e2bf1a1e4..bc23806b7fbe 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -90,6 +90,42 @@ static bool nft_hash_lookup(const struct nft_set *set,
90 return !!he; 90 return !!he;
91} 91}
92 92
93static bool nft_hash_update(struct nft_set *set, const struct nft_data *key,
94 void *(*new)(struct nft_set *,
95 const struct nft_expr *,
96 struct nft_data []),
97 const struct nft_expr *expr,
98 struct nft_data data[],
99 const struct nft_set_ext **ext)
100{
101 struct nft_hash *priv = nft_set_priv(set);
102 struct nft_hash_elem *he;
103 struct nft_hash_cmp_arg arg = {
104 .genmask = NFT_GENMASK_ANY,
105 .set = set,
106 .key = key,
107 };
108
109 he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
110 if (he != NULL)
111 goto out;
112
113 he = new(set, expr, data);
114 if (he == NULL)
115 goto err1;
116 if (rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
117 nft_hash_params))
118 goto err2;
119out:
120 *ext = &he->ext;
121 return true;
122
123err2:
124 nft_set_elem_destroy(set, he);
125err1:
126 return false;
127}
128
93static int nft_hash_insert(const struct nft_set *set, 129static int nft_hash_insert(const struct nft_set *set,
94 const struct nft_set_elem *elem) 130 const struct nft_set_elem *elem)
95{ 131{
@@ -335,6 +371,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
335 .deactivate = nft_hash_deactivate, 371 .deactivate = nft_hash_deactivate,
336 .remove = nft_hash_remove, 372 .remove = nft_hash_remove,
337 .lookup = nft_hash_lookup, 373 .lookup = nft_hash_lookup,
374 .update = nft_hash_update,
338 .walk = nft_hash_walk, 375 .walk = nft_hash_walk,
339 .features = NFT_SET_MAP | NFT_SET_TIMEOUT, 376 .features = NFT_SET_MAP | NFT_SET_TIMEOUT,
340 .owner = THIS_MODULE, 377 .owner = THIS_MODULE,