xref: /openbmc/linux/net/netfilter/nft_ct.c (revision 9b799b78)
1 /*
2  * Copyright (c) 2008-2009 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  * 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>
21 #include <net/netfilter/nf_conntrack_ecache.h>
22 #include <net/netfilter/nf_conntrack_labels.h>
23 
24 struct nft_ct {
25 	enum nft_ct_keys	key:8;
26 	enum ip_conntrack_dir	dir:8;
27 	union {
28 		enum nft_registers	dreg:8;
29 		enum nft_registers	sreg:8;
30 	};
31 };
32 
33 static void nft_ct_get_eval(const struct nft_expr *expr,
34 			    struct nft_regs *regs,
35 			    const struct nft_pktinfo *pkt)
36 {
37 	const struct nft_ct *priv = nft_expr_priv(expr);
38 	u32 *dest = &regs->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 = state;
58 		return;
59 	default:
60 		break;
61 	}
62 
63 	if (ct == NULL)
64 		goto err;
65 
66 	switch (priv->key) {
67 	case NFT_CT_DIRECTION:
68 		*dest = CTINFO2DIR(ctinfo);
69 		return;
70 	case NFT_CT_STATUS:
71 		*dest = ct->status;
72 		return;
73 #ifdef CONFIG_NF_CONNTRACK_MARK
74 	case NFT_CT_MARK:
75 		*dest = ct->mark;
76 		return;
77 #endif
78 #ifdef CONFIG_NF_CONNTRACK_SECMARK
79 	case NFT_CT_SECMARK:
80 		*dest = ct->secmark;
81 		return;
82 #endif
83 	case NFT_CT_EXPIRATION:
84 		diff = (long)jiffies - (long)ct->timeout.expires;
85 		if (diff < 0)
86 			diff = 0;
87 		*dest = jiffies_to_msecs(diff);
88 		return;
89 	case NFT_CT_HELPER:
90 		if (ct->master == NULL)
91 			goto err;
92 		help = nfct_help(ct->master);
93 		if (help == NULL)
94 			goto err;
95 		helper = rcu_dereference(help->helper);
96 		if (helper == NULL)
97 			goto err;
98 		strncpy((char *)dest, helper->name, NF_CT_HELPER_NAME_LEN);
99 		return;
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, 0, NF_CT_LABELS_MAX_SIZE);
107 			return;
108 		}
109 
110 		size = labels->words * sizeof(long);
111 		memcpy(dest, labels->bits, size);
112 		if (size < NF_CT_LABELS_MAX_SIZE)
113 			memset(((char *) dest) + size, 0,
114 			       NF_CT_LABELS_MAX_SIZE - size);
115 		return;
116 	}
117 #endif
118 	default:
119 		break;
120 	}
121 
122 	tuple = &ct->tuplehash[priv->dir].tuple;
123 	switch (priv->key) {
124 	case NFT_CT_L3PROTOCOL:
125 		*dest = nf_ct_l3num(ct);
126 		return;
127 	case NFT_CT_SRC:
128 		memcpy(dest, tuple->src.u3.all,
129 		       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
130 		return;
131 	case NFT_CT_DST:
132 		memcpy(dest, tuple->dst.u3.all,
133 		       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
134 		return;
135 	case NFT_CT_PROTOCOL:
136 		*dest = nf_ct_protonum(ct);
137 		return;
138 	case NFT_CT_PROTO_SRC:
139 		*dest = (__force __u16)tuple->src.u.all;
140 		return;
141 	case NFT_CT_PROTO_DST:
142 		*dest = (__force __u16)tuple->dst.u.all;
143 		return;
144 	default:
145 		break;
146 	}
147 	return;
148 err:
149 	regs->verdict.code = NFT_BREAK;
150 }
151 
152 static void nft_ct_set_eval(const struct nft_expr *expr,
153 			    struct nft_regs *regs,
154 			    const struct nft_pktinfo *pkt)
155 {
156 	const struct nft_ct *priv = nft_expr_priv(expr);
157 	struct sk_buff *skb = pkt->skb;
158 #ifdef CONFIG_NF_CONNTRACK_MARK
159 	u32 value = regs->data[priv->sreg];
160 #endif
161 	enum ip_conntrack_info ctinfo;
162 	struct nf_conn *ct;
163 
164 	ct = nf_ct_get(skb, &ctinfo);
165 	if (ct == NULL)
166 		return;
167 
168 	switch (priv->key) {
169 #ifdef CONFIG_NF_CONNTRACK_MARK
170 	case NFT_CT_MARK:
171 		if (ct->mark != value) {
172 			ct->mark = value;
173 			nf_conntrack_event_cache(IPCT_MARK, ct);
174 		}
175 		break;
176 #endif
177 	default:
178 		break;
179 	}
180 }
181 
182 static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
183 	[NFTA_CT_DREG]		= { .type = NLA_U32 },
184 	[NFTA_CT_KEY]		= { .type = NLA_U32 },
185 	[NFTA_CT_DIRECTION]	= { .type = NLA_U8 },
186 	[NFTA_CT_SREG]		= { .type = NLA_U32 },
187 };
188 
189 static int nft_ct_l3proto_try_module_get(uint8_t family)
190 {
191 	int err;
192 
193 	if (family == NFPROTO_INET) {
194 		err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
195 		if (err < 0)
196 			goto err1;
197 		err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
198 		if (err < 0)
199 			goto err2;
200 	} else {
201 		err = nf_ct_l3proto_try_module_get(family);
202 		if (err < 0)
203 			goto err1;
204 	}
205 	return 0;
206 
207 err2:
208 	nf_ct_l3proto_module_put(NFPROTO_IPV4);
209 err1:
210 	return err;
211 }
212 
213 static void nft_ct_l3proto_module_put(uint8_t family)
214 {
215 	if (family == NFPROTO_INET) {
216 		nf_ct_l3proto_module_put(NFPROTO_IPV4);
217 		nf_ct_l3proto_module_put(NFPROTO_IPV6);
218 	} else
219 		nf_ct_l3proto_module_put(family);
220 }
221 
222 static int nft_ct_get_init(const struct nft_ctx *ctx,
223 			   const struct nft_expr *expr,
224 			   const struct nlattr * const tb[])
225 {
226 	struct nft_ct *priv = nft_expr_priv(expr);
227 	unsigned int len;
228 	int err;
229 
230 	priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
231 	switch (priv->key) {
232 	case NFT_CT_DIRECTION:
233 		if (tb[NFTA_CT_DIRECTION] != NULL)
234 			return -EINVAL;
235 		len = sizeof(u8);
236 		break;
237 	case NFT_CT_STATE:
238 	case NFT_CT_STATUS:
239 #ifdef CONFIG_NF_CONNTRACK_MARK
240 	case NFT_CT_MARK:
241 #endif
242 #ifdef CONFIG_NF_CONNTRACK_SECMARK
243 	case NFT_CT_SECMARK:
244 #endif
245 	case NFT_CT_EXPIRATION:
246 		if (tb[NFTA_CT_DIRECTION] != NULL)
247 			return -EINVAL;
248 		len = sizeof(u32);
249 		break;
250 #ifdef CONFIG_NF_CONNTRACK_LABELS
251 	case NFT_CT_LABELS:
252 		if (tb[NFTA_CT_DIRECTION] != NULL)
253 			return -EINVAL;
254 		len = NF_CT_LABELS_MAX_SIZE;
255 		break;
256 #endif
257 	case NFT_CT_HELPER:
258 		if (tb[NFTA_CT_DIRECTION] != NULL)
259 			return -EINVAL;
260 		len = NF_CT_HELPER_NAME_LEN;
261 		break;
262 
263 	case NFT_CT_L3PROTOCOL:
264 	case NFT_CT_PROTOCOL:
265 		if (tb[NFTA_CT_DIRECTION] == NULL)
266 			return -EINVAL;
267 		len = sizeof(u8);
268 		break;
269 	case NFT_CT_SRC:
270 	case NFT_CT_DST:
271 		if (tb[NFTA_CT_DIRECTION] == NULL)
272 			return -EINVAL;
273 
274 		switch (ctx->afi->family) {
275 		case NFPROTO_IPV4:
276 			len = FIELD_SIZEOF(struct nf_conntrack_tuple,
277 					   src.u3.ip);
278 			break;
279 		case NFPROTO_IPV6:
280 		case NFPROTO_INET:
281 			len = FIELD_SIZEOF(struct nf_conntrack_tuple,
282 					   src.u3.ip6);
283 			break;
284 		default:
285 			return -EAFNOSUPPORT;
286 		}
287 		break;
288 	case NFT_CT_PROTO_SRC:
289 	case NFT_CT_PROTO_DST:
290 		if (tb[NFTA_CT_DIRECTION] == NULL)
291 			return -EINVAL;
292 		len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all);
293 		break;
294 	default:
295 		return -EOPNOTSUPP;
296 	}
297 
298 	if (tb[NFTA_CT_DIRECTION] != NULL) {
299 		priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
300 		switch (priv->dir) {
301 		case IP_CT_DIR_ORIGINAL:
302 		case IP_CT_DIR_REPLY:
303 			break;
304 		default:
305 			return -EINVAL;
306 		}
307 	}
308 
309 	priv->dreg = nft_parse_register(tb[NFTA_CT_DREG]);
310 	err = nft_validate_register_store(ctx, priv->dreg, NULL,
311 					  NFT_DATA_VALUE, len);
312 	if (err < 0)
313 		return err;
314 
315 	err = nft_ct_l3proto_try_module_get(ctx->afi->family);
316 	if (err < 0)
317 		return err;
318 
319 	return 0;
320 }
321 
322 static int nft_ct_set_init(const struct nft_ctx *ctx,
323 			   const struct nft_expr *expr,
324 			   const struct nlattr * const tb[])
325 {
326 	struct nft_ct *priv = nft_expr_priv(expr);
327 	unsigned int len;
328 	int err;
329 
330 	priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
331 	switch (priv->key) {
332 #ifdef CONFIG_NF_CONNTRACK_MARK
333 	case NFT_CT_MARK:
334 		len = FIELD_SIZEOF(struct nf_conn, mark);
335 		break;
336 #endif
337 	default:
338 		return -EOPNOTSUPP;
339 	}
340 
341 	priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]);
342 	err = nft_validate_register_load(priv->sreg, len);
343 	if (err < 0)
344 		return err;
345 
346 	err = nft_ct_l3proto_try_module_get(ctx->afi->family);
347 	if (err < 0)
348 		return err;
349 
350 	return 0;
351 }
352 
353 static void nft_ct_destroy(const struct nft_ctx *ctx,
354 			   const struct nft_expr *expr)
355 {
356 	nft_ct_l3proto_module_put(ctx->afi->family);
357 }
358 
359 static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
360 {
361 	const struct nft_ct *priv = nft_expr_priv(expr);
362 
363 	if (nft_dump_register(skb, NFTA_CT_DREG, priv->dreg))
364 		goto nla_put_failure;
365 	if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
366 		goto nla_put_failure;
367 
368 	switch (priv->key) {
369 	case NFT_CT_PROTOCOL:
370 	case NFT_CT_SRC:
371 	case NFT_CT_DST:
372 	case NFT_CT_PROTO_SRC:
373 	case NFT_CT_PROTO_DST:
374 		if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
375 			goto nla_put_failure;
376 	default:
377 		break;
378 	}
379 
380 	return 0;
381 
382 nla_put_failure:
383 	return -1;
384 }
385 
386 static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
387 {
388 	const struct nft_ct *priv = nft_expr_priv(expr);
389 
390 	if (nft_dump_register(skb, NFTA_CT_SREG, priv->sreg))
391 		goto nla_put_failure;
392 	if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
393 		goto nla_put_failure;
394 	return 0;
395 
396 nla_put_failure:
397 	return -1;
398 }
399 
400 static struct nft_expr_type nft_ct_type;
401 static const struct nft_expr_ops nft_ct_get_ops = {
402 	.type		= &nft_ct_type,
403 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_ct)),
404 	.eval		= nft_ct_get_eval,
405 	.init		= nft_ct_get_init,
406 	.destroy	= nft_ct_destroy,
407 	.dump		= nft_ct_get_dump,
408 };
409 
410 static const struct nft_expr_ops nft_ct_set_ops = {
411 	.type		= &nft_ct_type,
412 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_ct)),
413 	.eval		= nft_ct_set_eval,
414 	.init		= nft_ct_set_init,
415 	.destroy	= nft_ct_destroy,
416 	.dump		= nft_ct_set_dump,
417 };
418 
419 static const struct nft_expr_ops *
420 nft_ct_select_ops(const struct nft_ctx *ctx,
421 		    const struct nlattr * const tb[])
422 {
423 	if (tb[NFTA_CT_KEY] == NULL)
424 		return ERR_PTR(-EINVAL);
425 
426 	if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
427 		return ERR_PTR(-EINVAL);
428 
429 	if (tb[NFTA_CT_DREG])
430 		return &nft_ct_get_ops;
431 
432 	if (tb[NFTA_CT_SREG])
433 		return &nft_ct_set_ops;
434 
435 	return ERR_PTR(-EINVAL);
436 }
437 
438 static struct nft_expr_type nft_ct_type __read_mostly = {
439 	.name		= "ct",
440 	.select_ops	= &nft_ct_select_ops,
441 	.policy		= nft_ct_policy,
442 	.maxattr	= NFTA_CT_MAX,
443 	.owner		= THIS_MODULE,
444 };
445 
446 static int __init nft_ct_module_init(void)
447 {
448 	return nft_register_expr(&nft_ct_type);
449 }
450 
451 static void __exit nft_ct_module_exit(void)
452 {
453 	nft_unregister_expr(&nft_ct_type);
454 }
455 
456 module_init(nft_ct_module_init);
457 module_exit(nft_ct_module_exit);
458 
459 MODULE_LICENSE("GPL");
460 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
461 MODULE_ALIAS_NFT_EXPR("ct");
462