]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/netfilter/nft_ct.c
netfilter: nft_meta: split nft_meta_init() into two functions for get/set
[mirror_ubuntu-bionic-kernel.git] / net / netfilter / nft_ct.c
CommitLineData
96518518 1/*
ef1f7df9 2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
96518518
PM
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 * Development of this code funded by Astaro AG (http://www.astaro.com/)
9 */
10
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/netlink.h>
15#include <linux/netfilter.h>
16#include <linux/netfilter/nf_tables.h>
17#include <net/netfilter/nf_tables.h>
18#include <net/netfilter/nf_conntrack.h>
19#include <net/netfilter/nf_conntrack_tuple.h>
20#include <net/netfilter/nf_conntrack_helper.h>
c4ede3d3 21#include <net/netfilter/nf_conntrack_ecache.h>
d2bf2f34 22#include <net/netfilter/nf_conntrack_labels.h>
96518518
PM
23
24struct nft_ct {
25 enum nft_ct_keys key:8;
26 enum ip_conntrack_dir dir:8;
d46f2cd2 27 union {
c4ede3d3
KE
28 enum nft_registers dreg:8;
29 enum nft_registers sreg:8;
30 };
96518518
PM
31};
32
c4ede3d3
KE
33static void nft_ct_get_eval(const struct nft_expr *expr,
34 struct nft_data data[NFT_REG_MAX + 1],
35 const struct nft_pktinfo *pkt)
96518518
PM
36{
37 const struct nft_ct *priv = nft_expr_priv(expr);
38 struct nft_data *dest = &data[priv->dreg];
39 enum ip_conntrack_info ctinfo;
40 const struct nf_conn *ct;
41 const struct nf_conn_help *help;
42 const struct nf_conntrack_tuple *tuple;
43 const struct nf_conntrack_helper *helper;
44 long diff;
45 unsigned int state;
46
47 ct = nf_ct_get(pkt->skb, &ctinfo);
48
49 switch (priv->key) {
50 case NFT_CT_STATE:
51 if (ct == NULL)
52 state = NF_CT_STATE_INVALID_BIT;
53 else if (nf_ct_is_untracked(ct))
54 state = NF_CT_STATE_UNTRACKED_BIT;
55 else
56 state = NF_CT_STATE_BIT(ctinfo);
57 dest->data[0] = state;
58 return;
59 }
60
61 if (ct == NULL)
62 goto err;
63
64 switch (priv->key) {
65 case NFT_CT_DIRECTION:
66 dest->data[0] = CTINFO2DIR(ctinfo);
67 return;
68 case NFT_CT_STATUS:
69 dest->data[0] = ct->status;
70 return;
71#ifdef CONFIG_NF_CONNTRACK_MARK
72 case NFT_CT_MARK:
73 dest->data[0] = ct->mark;
74 return;
75#endif
76#ifdef CONFIG_NF_CONNTRACK_SECMARK
77 case NFT_CT_SECMARK:
78 dest->data[0] = ct->secmark;
79 return;
80#endif
81 case NFT_CT_EXPIRATION:
82 diff = (long)jiffies - (long)ct->timeout.expires;
83 if (diff < 0)
84 diff = 0;
85 dest->data[0] = jiffies_to_msecs(diff);
86 return;
87 case NFT_CT_HELPER:
88 if (ct->master == NULL)
89 goto err;
90 help = nfct_help(ct->master);
91 if (help == NULL)
92 goto err;
93 helper = rcu_dereference(help->helper);
94 if (helper == NULL)
95 goto err;
96 if (strlen(helper->name) >= sizeof(dest->data))
97 goto err;
98 strncpy((char *)dest->data, helper->name, sizeof(dest->data));
99 return;
d2bf2f34
FW
100#ifdef CONFIG_NF_CONNTRACK_LABELS
101 case NFT_CT_LABELS: {
102 struct nf_conn_labels *labels = nf_ct_labels_find(ct);
103 unsigned int size;
104
105 if (!labels) {
106 memset(dest->data, 0, sizeof(dest->data));
107 return;
108 }
109
110 BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > sizeof(dest->data));
111 size = labels->words * sizeof(long);
112
113 memcpy(dest->data, labels->bits, size);
114 if (size < sizeof(dest->data))
115 memset(((char *) dest->data) + size, 0,
116 sizeof(dest->data) - size);
117 return;
118 }
119#endif
96518518
PM
120 }
121
122 tuple = &ct->tuplehash[priv->dir].tuple;
123 switch (priv->key) {
124 case NFT_CT_L3PROTOCOL:
125 dest->data[0] = nf_ct_l3num(ct);
126 return;
127 case NFT_CT_SRC:
128 memcpy(dest->data, tuple->src.u3.all,
129 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
130 return;
131 case NFT_CT_DST:
132 memcpy(dest->data, tuple->dst.u3.all,
133 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
134 return;
135 case NFT_CT_PROTOCOL:
136 dest->data[0] = nf_ct_protonum(ct);
137 return;
138 case NFT_CT_PROTO_SRC:
139 dest->data[0] = (__force __u16)tuple->src.u.all;
140 return;
141 case NFT_CT_PROTO_DST:
142 dest->data[0] = (__force __u16)tuple->dst.u.all;
143 return;
144 }
145 return;
146err:
147 data[NFT_REG_VERDICT].verdict = NFT_BREAK;
148}
149
c4ede3d3
KE
150static void nft_ct_set_eval(const struct nft_expr *expr,
151 struct nft_data data[NFT_REG_MAX + 1],
152 const struct nft_pktinfo *pkt)
153{
154 const struct nft_ct *priv = nft_expr_priv(expr);
155 struct sk_buff *skb = pkt->skb;
847c8e29 156#ifdef CONFIG_NF_CONNTRACK_MARK
c4ede3d3 157 u32 value = data[priv->sreg].data[0];
847c8e29 158#endif
c4ede3d3
KE
159 enum ip_conntrack_info ctinfo;
160 struct nf_conn *ct;
161
162 ct = nf_ct_get(skb, &ctinfo);
163 if (ct == NULL)
164 return;
165
166 switch (priv->key) {
167#ifdef CONFIG_NF_CONNTRACK_MARK
168 case NFT_CT_MARK:
169 if (ct->mark != value) {
170 ct->mark = value;
171 nf_conntrack_event_cache(IPCT_MARK, ct);
172 }
173 break;
174#endif
175 }
176}
177
96518518
PM
178static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
179 [NFTA_CT_DREG] = { .type = NLA_U32 },
180 [NFTA_CT_KEY] = { .type = NLA_U32 },
181 [NFTA_CT_DIRECTION] = { .type = NLA_U8 },
c4ede3d3 182 [NFTA_CT_SREG] = { .type = NLA_U32 },
96518518
PM
183};
184
9638f33e
PM
185static int nft_ct_l3proto_try_module_get(uint8_t family)
186{
187 int err;
188
189 if (family == NFPROTO_INET) {
190 err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
191 if (err < 0)
192 goto err1;
193 err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
194 if (err < 0)
195 goto err2;
196 } else {
197 err = nf_ct_l3proto_try_module_get(family);
198 if (err < 0)
199 goto err1;
200 }
201 return 0;
202
203err2:
204 nf_ct_l3proto_module_put(NFPROTO_IPV4);
205err1:
206 return err;
207}
208
209static void nft_ct_l3proto_module_put(uint8_t family)
210{
211 if (family == NFPROTO_INET) {
212 nf_ct_l3proto_module_put(NFPROTO_IPV4);
213 nf_ct_l3proto_module_put(NFPROTO_IPV6);
214 } else
215 nf_ct_l3proto_module_put(family);
216}
217
c4ede3d3
KE
218static int nft_ct_init_validate_get(const struct nft_expr *expr,
219 const struct nlattr * const tb[])
96518518
PM
220{
221 struct nft_ct *priv = nft_expr_priv(expr);
96518518 222
96518518
PM
223 if (tb[NFTA_CT_DIRECTION] != NULL) {
224 priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
225 switch (priv->dir) {
226 case IP_CT_DIR_ORIGINAL:
227 case IP_CT_DIR_REPLY:
228 break;
229 default:
230 return -EINVAL;
231 }
232 }
233
234 switch (priv->key) {
235 case NFT_CT_STATE:
236 case NFT_CT_DIRECTION:
237 case NFT_CT_STATUS:
238#ifdef CONFIG_NF_CONNTRACK_MARK
239 case NFT_CT_MARK:
240#endif
241#ifdef CONFIG_NF_CONNTRACK_SECMARK
242 case NFT_CT_SECMARK:
d2bf2f34
FW
243#endif
244#ifdef CONFIG_NF_CONNTRACK_LABELS
245 case NFT_CT_LABELS:
96518518
PM
246#endif
247 case NFT_CT_EXPIRATION:
248 case NFT_CT_HELPER:
249 if (tb[NFTA_CT_DIRECTION] != NULL)
250 return -EINVAL;
251 break;
51292c07 252 case NFT_CT_L3PROTOCOL:
96518518
PM
253 case NFT_CT_PROTOCOL:
254 case NFT_CT_SRC:
255 case NFT_CT_DST:
256 case NFT_CT_PROTO_SRC:
257 case NFT_CT_PROTO_DST:
258 if (tb[NFTA_CT_DIRECTION] == NULL)
259 return -EINVAL;
260 break;
261 default:
262 return -EOPNOTSUPP;
263 }
264
c4ede3d3
KE
265 return 0;
266}
267
268static int nft_ct_init_validate_set(uint32_t key)
269{
270 switch (key) {
e88e514e 271#ifdef CONFIG_NF_CONNTRACK_MARK
c4ede3d3
KE
272 case NFT_CT_MARK:
273 break;
e88e514e 274#endif
c4ede3d3
KE
275 default:
276 return -EOPNOTSUPP;
277 }
278
279 return 0;
280}
281
282static int nft_ct_init(const struct nft_ctx *ctx,
283 const struct nft_expr *expr,
284 const struct nlattr * const tb[])
285{
286 struct nft_ct *priv = nft_expr_priv(expr);
287 int err;
288
289 priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
290
291 if (tb[NFTA_CT_DREG]) {
292 err = nft_ct_init_validate_get(expr, tb);
293 if (err < 0)
294 return err;
295
296 priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
297 err = nft_validate_output_register(priv->dreg);
298 if (err < 0)
299 return err;
300
301 err = nft_validate_data_load(ctx, priv->dreg, NULL,
302 NFT_DATA_VALUE);
303 if (err < 0)
304 return err;
305 } else {
306 err = nft_ct_init_validate_set(priv->key);
307 if (err < 0)
308 return err;
309
310 priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
311 err = nft_validate_input_register(priv->sreg);
312 if (err < 0)
313 return err;
314 }
315
9638f33e 316 err = nft_ct_l3proto_try_module_get(ctx->afi->family);
96518518
PM
317 if (err < 0)
318 return err;
96518518 319
96518518 320 return 0;
96518518
PM
321}
322
62472bce
PM
323static void nft_ct_destroy(const struct nft_ctx *ctx,
324 const struct nft_expr *expr)
96518518 325{
d46f2cd2 326 nft_ct_l3proto_module_put(ctx->afi->family);
96518518
PM
327}
328
c4ede3d3 329static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
96518518
PM
330{
331 const struct nft_ct *priv = nft_expr_priv(expr);
332
333 if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
334 goto nla_put_failure;
335 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
336 goto nla_put_failure;
2a53bfb3
AB
337
338 switch (priv->key) {
339 case NFT_CT_PROTOCOL:
340 case NFT_CT_SRC:
341 case NFT_CT_DST:
342 case NFT_CT_PROTO_SRC:
343 case NFT_CT_PROTO_DST:
344 if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
345 goto nla_put_failure;
346 default:
347 break;
348 }
349
96518518
PM
350 return 0;
351
352nla_put_failure:
353 return -1;
354}
355
c4ede3d3
KE
356static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
357{
358 const struct nft_ct *priv = nft_expr_priv(expr);
359
360 if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg)))
361 goto nla_put_failure;
362 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
363 goto nla_put_failure;
364 return 0;
365
366nla_put_failure:
367 return -1;
368}
369
ef1f7df9 370static struct nft_expr_type nft_ct_type;
c4ede3d3 371static const struct nft_expr_ops nft_ct_get_ops = {
ef1f7df9 372 .type = &nft_ct_type,
96518518 373 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
c4ede3d3 374 .eval = nft_ct_get_eval,
96518518
PM
375 .init = nft_ct_init,
376 .destroy = nft_ct_destroy,
c4ede3d3 377 .dump = nft_ct_get_dump,
ef1f7df9
PM
378};
379
c4ede3d3
KE
380static const struct nft_expr_ops nft_ct_set_ops = {
381 .type = &nft_ct_type,
382 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
383 .eval = nft_ct_set_eval,
384 .init = nft_ct_init,
385 .destroy = nft_ct_destroy,
386 .dump = nft_ct_set_dump,
387};
388
389static const struct nft_expr_ops *
390nft_ct_select_ops(const struct nft_ctx *ctx,
391 const struct nlattr * const tb[])
392{
393 if (tb[NFTA_CT_KEY] == NULL)
394 return ERR_PTR(-EINVAL);
395
396 if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
397 return ERR_PTR(-EINVAL);
398
399 if (tb[NFTA_CT_DREG])
400 return &nft_ct_get_ops;
401
402 if (tb[NFTA_CT_SREG])
403 return &nft_ct_set_ops;
404
405 return ERR_PTR(-EINVAL);
406}
407
ef1f7df9
PM
408static struct nft_expr_type nft_ct_type __read_mostly = {
409 .name = "ct",
c4ede3d3 410 .select_ops = &nft_ct_select_ops,
96518518
PM
411 .policy = nft_ct_policy,
412 .maxattr = NFTA_CT_MAX,
ef1f7df9 413 .owner = THIS_MODULE,
96518518
PM
414};
415
416static int __init nft_ct_module_init(void)
417{
ef1f7df9 418 return nft_register_expr(&nft_ct_type);
96518518
PM
419}
420
421static void __exit nft_ct_module_exit(void)
422{
ef1f7df9 423 nft_unregister_expr(&nft_ct_type);
96518518
PM
424}
425
426module_init(nft_ct_module_init);
427module_exit(nft_ct_module_exit);
428
429MODULE_LICENSE("GPL");
430MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
431MODULE_ALIAS_NFT_EXPR("ct");