Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / net / netfilter / nft_dynset.c
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
19 struct 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_expr                 *expr;
27         struct nft_set_binding          binding;
28 };
29
30 static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
31                             struct nft_regs *regs)
32 {
33         const struct nft_dynset *priv = nft_expr_priv(expr);
34         struct nft_set_ext *ext;
35         u64 timeout;
36         void *elem;
37
38         if (set->size && !atomic_add_unless(&set->nelems, 1, set->size))
39                 return NULL;
40
41         timeout = priv->timeout ? : set->timeout;
42         elem = nft_set_elem_init(set, &priv->tmpl,
43                                  &regs->data[priv->sreg_key],
44                                  &regs->data[priv->sreg_data],
45                                  timeout, GFP_ATOMIC);
46         if (elem == NULL) {
47                 if (set->size)
48                         atomic_dec(&set->nelems);
49                 return NULL;
50         }
51
52         ext = nft_set_elem_ext(set, elem);
53         if (priv->expr != NULL)
54                 nft_expr_clone(nft_set_ext_expr(ext), priv->expr);
55
56         return elem;
57 }
58
59 static void nft_dynset_eval(const struct nft_expr *expr,
60                             struct nft_regs *regs,
61                             const struct nft_pktinfo *pkt)
62 {
63         const struct nft_dynset *priv = nft_expr_priv(expr);
64         struct nft_set *set = priv->set;
65         const struct nft_set_ext *ext;
66         const struct nft_expr *sexpr;
67         u64 timeout;
68
69         if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
70                              expr, regs, &ext)) {
71                 sexpr = NULL;
72                 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
73                         sexpr = nft_set_ext_expr(ext);
74
75                 if (priv->op == NFT_DYNSET_OP_UPDATE &&
76                     nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
77                         timeout = priv->timeout ? : set->timeout;
78                         *nft_set_ext_expiration(ext) = jiffies + timeout;
79                 } else if (sexpr == NULL)
80                         goto out;
81
82                 if (sexpr != NULL)
83                         sexpr->ops->eval(sexpr, regs, pkt);
84                 return;
85         }
86 out:
87         regs->verdict.code = NFT_BREAK;
88 }
89
90 static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
91         [NFTA_DYNSET_SET_NAME]  = { .type = NLA_STRING },
92         [NFTA_DYNSET_SET_ID]    = { .type = NLA_U32 },
93         [NFTA_DYNSET_OP]        = { .type = NLA_U32 },
94         [NFTA_DYNSET_SREG_KEY]  = { .type = NLA_U32 },
95         [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
96         [NFTA_DYNSET_TIMEOUT]   = { .type = NLA_U64 },
97         [NFTA_DYNSET_EXPR]      = { .type = NLA_NESTED },
98 };
99
100 static int nft_dynset_init(const struct nft_ctx *ctx,
101                            const struct nft_expr *expr,
102                            const struct nlattr * const tb[])
103 {
104         struct nft_dynset *priv = nft_expr_priv(expr);
105         struct nft_set *set;
106         u64 timeout;
107         int err;
108
109         if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
110             tb[NFTA_DYNSET_OP] == NULL ||
111             tb[NFTA_DYNSET_SREG_KEY] == NULL)
112                 return -EINVAL;
113
114         set = nf_tables_set_lookup(ctx->table, tb[NFTA_DYNSET_SET_NAME]);
115         if (IS_ERR(set)) {
116                 if (tb[NFTA_DYNSET_SET_ID])
117                         set = nf_tables_set_lookup_byid(ctx->net,
118                                                         tb[NFTA_DYNSET_SET_ID]);
119                 if (IS_ERR(set))
120                         return PTR_ERR(set);
121         }
122
123         if (set->flags & NFT_SET_CONSTANT)
124                 return -EBUSY;
125
126         priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
127         switch (priv->op) {
128         case NFT_DYNSET_OP_ADD:
129                 break;
130         case NFT_DYNSET_OP_UPDATE:
131                 if (!(set->flags & NFT_SET_TIMEOUT))
132                         return -EOPNOTSUPP;
133                 break;
134         default:
135                 return -EOPNOTSUPP;
136         }
137
138         timeout = 0;
139         if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
140                 if (!(set->flags & NFT_SET_TIMEOUT))
141                         return -EINVAL;
142                 timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
143         }
144
145         priv->sreg_key = nft_parse_register(tb[NFTA_DYNSET_SREG_KEY]);
146         err = nft_validate_register_load(priv->sreg_key, set->klen);;
147         if (err < 0)
148                 return err;
149
150         if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
151                 if (!(set->flags & NFT_SET_MAP))
152                         return -EINVAL;
153                 if (set->dtype == NFT_DATA_VERDICT)
154                         return -EOPNOTSUPP;
155
156                 priv->sreg_data = nft_parse_register(tb[NFTA_DYNSET_SREG_DATA]);
157                 err = nft_validate_register_load(priv->sreg_data, set->dlen);
158                 if (err < 0)
159                         return err;
160         } else if (set->flags & NFT_SET_MAP)
161                 return -EINVAL;
162
163         if (tb[NFTA_DYNSET_EXPR] != NULL) {
164                 if (!(set->flags & NFT_SET_EVAL))
165                         return -EINVAL;
166                 if (!(set->flags & NFT_SET_ANONYMOUS))
167                         return -EOPNOTSUPP;
168
169                 priv->expr = nft_expr_init(ctx, tb[NFTA_DYNSET_EXPR]);
170                 if (IS_ERR(priv->expr))
171                         return PTR_ERR(priv->expr);
172
173                 err = -EOPNOTSUPP;
174                 if (!(priv->expr->ops->type->flags & NFT_EXPR_STATEFUL))
175                         goto err1;
176         } else if (set->flags & NFT_SET_EVAL)
177                 return -EINVAL;
178
179         nft_set_ext_prepare(&priv->tmpl);
180         nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
181         if (set->flags & NFT_SET_MAP)
182                 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
183         if (priv->expr != NULL)
184                 nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
185                                        priv->expr->ops->size);
186         if (set->flags & NFT_SET_TIMEOUT) {
187                 if (timeout || set->timeout)
188                         nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
189         }
190
191         priv->timeout = timeout;
192
193         err = nf_tables_bind_set(ctx, set, &priv->binding);
194         if (err < 0)
195                 goto err1;
196
197         priv->set = set;
198         return 0;
199
200 err1:
201         if (priv->expr != NULL)
202                 nft_expr_destroy(ctx, priv->expr);
203         return err;
204 }
205
206 static void nft_dynset_destroy(const struct nft_ctx *ctx,
207                                const struct nft_expr *expr)
208 {
209         struct nft_dynset *priv = nft_expr_priv(expr);
210
211         nf_tables_unbind_set(ctx, priv->set, &priv->binding);
212         if (priv->expr != NULL)
213                 nft_expr_destroy(ctx, priv->expr);
214 }
215
216 static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
217 {
218         const struct nft_dynset *priv = nft_expr_priv(expr);
219
220         if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key))
221                 goto nla_put_failure;
222         if (priv->set->flags & NFT_SET_MAP &&
223             nft_dump_register(skb, NFTA_DYNSET_SREG_DATA, priv->sreg_data))
224                 goto nla_put_failure;
225         if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
226                 goto nla_put_failure;
227         if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
228                 goto nla_put_failure;
229         if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
230                 goto nla_put_failure;
231         if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
232                 goto nla_put_failure;
233         return 0;
234
235 nla_put_failure:
236         return -1;
237 }
238
239 static struct nft_expr_type nft_dynset_type;
240 static const struct nft_expr_ops nft_dynset_ops = {
241         .type           = &nft_dynset_type,
242         .size           = NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
243         .eval           = nft_dynset_eval,
244         .init           = nft_dynset_init,
245         .destroy        = nft_dynset_destroy,
246         .dump           = nft_dynset_dump,
247 };
248
249 static struct nft_expr_type nft_dynset_type __read_mostly = {
250         .name           = "dynset",
251         .ops            = &nft_dynset_ops,
252         .policy         = nft_dynset_policy,
253         .maxattr        = NFTA_DYNSET_MAX,
254         .owner          = THIS_MODULE,
255 };
256
257 int __init nft_dynset_module_init(void)
258 {
259         return nft_register_expr(&nft_dynset_type);
260 }
261
262 void nft_dynset_module_exit(void)
263 {
264         nft_unregister_expr(&nft_dynset_type);
265 }