GNU Linux-libre 4.19.264-gnu1
[releases.git] / net / netfilter / nft_objref.c
1 /*
2  * Copyright (c) 2012-2016 Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  */
8
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/skbuff.h>
12 #include <linux/netlink.h>
13 #include <linux/netfilter.h>
14 #include <linux/netfilter/nf_tables.h>
15 #include <net/netfilter/nf_tables.h>
16
17 #define nft_objref_priv(expr)   *((struct nft_object **)nft_expr_priv(expr))
18
19 static void nft_objref_eval(const struct nft_expr *expr,
20                             struct nft_regs *regs,
21                             const struct nft_pktinfo *pkt)
22 {
23         struct nft_object *obj = nft_objref_priv(expr);
24
25         obj->ops->eval(obj, regs, pkt);
26 }
27
28 static int nft_objref_init(const struct nft_ctx *ctx,
29                            const struct nft_expr *expr,
30                            const struct nlattr * const tb[])
31 {
32         struct nft_object *obj = nft_objref_priv(expr);
33         u8 genmask = nft_genmask_next(ctx->net);
34         u32 objtype;
35
36         if (!tb[NFTA_OBJREF_IMM_NAME] ||
37             !tb[NFTA_OBJREF_IMM_TYPE])
38                 return -EINVAL;
39
40         objtype = ntohl(nla_get_be32(tb[NFTA_OBJREF_IMM_TYPE]));
41         obj = nft_obj_lookup(ctx->table, tb[NFTA_OBJREF_IMM_NAME], objtype,
42                              genmask);
43         if (IS_ERR(obj))
44                 return -ENOENT;
45
46         nft_objref_priv(expr) = obj;
47         obj->use++;
48
49         return 0;
50 }
51
52 static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr)
53 {
54         const struct nft_object *obj = nft_objref_priv(expr);
55
56         if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->name) ||
57             nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE,
58                          htonl(obj->ops->type->type)))
59                 goto nla_put_failure;
60
61         return 0;
62
63 nla_put_failure:
64         return -1;
65 }
66
67 static void nft_objref_deactivate(const struct nft_ctx *ctx,
68                                   const struct nft_expr *expr,
69                                   enum nft_trans_phase phase)
70 {
71         struct nft_object *obj = nft_objref_priv(expr);
72
73         if (phase == NFT_TRANS_COMMIT)
74                 return;
75
76         obj->use--;
77 }
78
79 static void nft_objref_activate(const struct nft_ctx *ctx,
80                                 const struct nft_expr *expr)
81 {
82         struct nft_object *obj = nft_objref_priv(expr);
83
84         obj->use++;
85 }
86
87 static struct nft_expr_type nft_objref_type;
88 static const struct nft_expr_ops nft_objref_ops = {
89         .type           = &nft_objref_type,
90         .size           = NFT_EXPR_SIZE(sizeof(struct nft_object *)),
91         .eval           = nft_objref_eval,
92         .init           = nft_objref_init,
93         .activate       = nft_objref_activate,
94         .deactivate     = nft_objref_deactivate,
95         .dump           = nft_objref_dump,
96 };
97
98 struct nft_objref_map {
99         struct nft_set          *set;
100         enum nft_registers      sreg:8;
101         struct nft_set_binding  binding;
102 };
103
104 static void nft_objref_map_eval(const struct nft_expr *expr,
105                                 struct nft_regs *regs,
106                                 const struct nft_pktinfo *pkt)
107 {
108         struct nft_objref_map *priv = nft_expr_priv(expr);
109         const struct nft_set *set = priv->set;
110         const struct nft_set_ext *ext;
111         struct nft_object *obj;
112         bool found;
113
114         found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
115                                  &ext);
116         if (!found) {
117                 regs->verdict.code = NFT_BREAK;
118                 return;
119         }
120         obj = *nft_set_ext_obj(ext);
121         obj->ops->eval(obj, regs, pkt);
122 }
123
124 static int nft_objref_map_init(const struct nft_ctx *ctx,
125                                const struct nft_expr *expr,
126                                const struct nlattr * const tb[])
127 {
128         struct nft_objref_map *priv = nft_expr_priv(expr);
129         u8 genmask = nft_genmask_next(ctx->net);
130         struct nft_set *set;
131         int err;
132
133         set = nft_set_lookup_global(ctx->net, ctx->table,
134                                     tb[NFTA_OBJREF_SET_NAME],
135                                     tb[NFTA_OBJREF_SET_ID], genmask);
136         if (IS_ERR(set))
137                 return PTR_ERR(set);
138
139         if (!(set->flags & NFT_SET_OBJECT))
140                 return -EINVAL;
141
142         priv->sreg = nft_parse_register(tb[NFTA_OBJREF_SET_SREG]);
143         err = nft_validate_register_load(priv->sreg, set->klen);
144         if (err < 0)
145                 return err;
146
147         priv->binding.flags = set->flags & NFT_SET_OBJECT;
148
149         err = nf_tables_bind_set(ctx, set, &priv->binding);
150         if (err < 0)
151                 return err;
152
153         priv->set = set;
154         return 0;
155 }
156
157 static int nft_objref_map_dump(struct sk_buff *skb, const struct nft_expr *expr)
158 {
159         const struct nft_objref_map *priv = nft_expr_priv(expr);
160
161         if (nft_dump_register(skb, NFTA_OBJREF_SET_SREG, priv->sreg) ||
162             nla_put_string(skb, NFTA_OBJREF_SET_NAME, priv->set->name))
163                 goto nla_put_failure;
164
165         return 0;
166
167 nla_put_failure:
168         return -1;
169 }
170
171 static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
172                                       const struct nft_expr *expr,
173                                       enum nft_trans_phase phase)
174 {
175         struct nft_objref_map *priv = nft_expr_priv(expr);
176
177         nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
178 }
179
180 static void nft_objref_map_activate(const struct nft_ctx *ctx,
181                                     const struct nft_expr *expr)
182 {
183         struct nft_objref_map *priv = nft_expr_priv(expr);
184
185         priv->set->use++;
186 }
187
188 static void nft_objref_map_destroy(const struct nft_ctx *ctx,
189                                    const struct nft_expr *expr)
190 {
191         struct nft_objref_map *priv = nft_expr_priv(expr);
192
193         nf_tables_destroy_set(ctx, priv->set);
194 }
195
196 static struct nft_expr_type nft_objref_type;
197 static const struct nft_expr_ops nft_objref_map_ops = {
198         .type           = &nft_objref_type,
199         .size           = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
200         .eval           = nft_objref_map_eval,
201         .init           = nft_objref_map_init,
202         .activate       = nft_objref_map_activate,
203         .deactivate     = nft_objref_map_deactivate,
204         .destroy        = nft_objref_map_destroy,
205         .dump           = nft_objref_map_dump,
206 };
207
208 static const struct nft_expr_ops *
209 nft_objref_select_ops(const struct nft_ctx *ctx,
210                       const struct nlattr * const tb[])
211 {
212         if (tb[NFTA_OBJREF_SET_SREG] &&
213             (tb[NFTA_OBJREF_SET_NAME] ||
214              tb[NFTA_OBJREF_SET_ID]))
215                 return &nft_objref_map_ops;
216         else if (tb[NFTA_OBJREF_IMM_NAME] &&
217                  tb[NFTA_OBJREF_IMM_TYPE])
218                 return &nft_objref_ops;
219
220         return ERR_PTR(-EOPNOTSUPP);
221 }
222
223 static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = {
224         [NFTA_OBJREF_IMM_NAME]  = { .type = NLA_STRING,
225                                     .len = NFT_OBJ_MAXNAMELEN - 1 },
226         [NFTA_OBJREF_IMM_TYPE]  = { .type = NLA_U32 },
227         [NFTA_OBJREF_SET_SREG]  = { .type = NLA_U32 },
228         [NFTA_OBJREF_SET_NAME]  = { .type = NLA_STRING,
229                                     .len = NFT_SET_MAXNAMELEN - 1 },
230         [NFTA_OBJREF_SET_ID]    = { .type = NLA_U32 },
231 };
232
233 static struct nft_expr_type nft_objref_type __read_mostly = {
234         .name           = "objref",
235         .select_ops     = nft_objref_select_ops,
236         .policy         = nft_objref_policy,
237         .maxattr        = NFTA_OBJREF_MAX,
238         .owner          = THIS_MODULE,
239 };
240
241 static int __init nft_objref_module_init(void)
242 {
243         return nft_register_expr(&nft_objref_type);
244 }
245
246 static void __exit nft_objref_module_exit(void)
247 {
248         nft_unregister_expr(&nft_objref_type);
249 }
250
251 module_init(nft_objref_module_init);
252 module_exit(nft_objref_module_exit);
253
254 MODULE_LICENSE("GPL");
255 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
256 MODULE_ALIAS_NFT_EXPR("objref");