xref: /openbmc/linux/net/netfilter/nft_ct.c (revision 3932b9ca)
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_data data[NFT_REG_MAX + 1],
35 			    const struct nft_pktinfo *pkt)
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;
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
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;
146 err:
147 	data[NFT_REG_VERDICT].verdict = NFT_BREAK;
148 }
149 
150 static 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;
156 #ifdef CONFIG_NF_CONNTRACK_MARK
157 	u32 value = data[priv->sreg].data[0];
158 #endif
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 
178 static 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 },
182 	[NFTA_CT_SREG]		= { .type = NLA_U32 },
183 };
184 
185 static 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 
203 err2:
204 	nf_ct_l3proto_module_put(NFPROTO_IPV4);
205 err1:
206 	return err;
207 }
208 
209 static 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 
218 static int nft_ct_get_init(const struct nft_ctx *ctx,
219 			   const struct nft_expr *expr,
220 			   const struct nlattr * const tb[])
221 {
222 	struct nft_ct *priv = nft_expr_priv(expr);
223 	int err;
224 
225 	priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
226 	switch (priv->key) {
227 	case NFT_CT_STATE:
228 	case NFT_CT_DIRECTION:
229 	case NFT_CT_STATUS:
230 #ifdef CONFIG_NF_CONNTRACK_MARK
231 	case NFT_CT_MARK:
232 #endif
233 #ifdef CONFIG_NF_CONNTRACK_SECMARK
234 	case NFT_CT_SECMARK:
235 #endif
236 #ifdef CONFIG_NF_CONNTRACK_LABELS
237 	case NFT_CT_LABELS:
238 #endif
239 	case NFT_CT_EXPIRATION:
240 	case NFT_CT_HELPER:
241 		if (tb[NFTA_CT_DIRECTION] != NULL)
242 			return -EINVAL;
243 		break;
244 	case NFT_CT_L3PROTOCOL:
245 	case NFT_CT_PROTOCOL:
246 	case NFT_CT_SRC:
247 	case NFT_CT_DST:
248 	case NFT_CT_PROTO_SRC:
249 	case NFT_CT_PROTO_DST:
250 		if (tb[NFTA_CT_DIRECTION] == NULL)
251 			return -EINVAL;
252 		break;
253 	default:
254 		return -EOPNOTSUPP;
255 	}
256 
257 	if (tb[NFTA_CT_DIRECTION] != NULL) {
258 		priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
259 		switch (priv->dir) {
260 		case IP_CT_DIR_ORIGINAL:
261 		case IP_CT_DIR_REPLY:
262 			break;
263 		default:
264 			return -EINVAL;
265 		}
266 	}
267 
268 	priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
269 	err = nft_validate_output_register(priv->dreg);
270 	if (err < 0)
271 		return err;
272 
273 	err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
274 	if (err < 0)
275 		return err;
276 
277 	err = nft_ct_l3proto_try_module_get(ctx->afi->family);
278 	if (err < 0)
279 		return err;
280 
281 	return 0;
282 }
283 
284 static int nft_ct_set_init(const struct nft_ctx *ctx,
285 			   const struct nft_expr *expr,
286 			   const struct nlattr * const tb[])
287 {
288 	struct nft_ct *priv = nft_expr_priv(expr);
289 	int err;
290 
291 	priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
292 	switch (priv->key) {
293 #ifdef CONFIG_NF_CONNTRACK_MARK
294 	case NFT_CT_MARK:
295 		break;
296 #endif
297 	default:
298 		return -EOPNOTSUPP;
299 	}
300 
301 	priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
302 	err = nft_validate_input_register(priv->sreg);
303 	if (err < 0)
304 		return err;
305 
306 	err = nft_ct_l3proto_try_module_get(ctx->afi->family);
307 	if (err < 0)
308 		return err;
309 
310 	return 0;
311 }
312 
313 static void nft_ct_destroy(const struct nft_ctx *ctx,
314 			   const struct nft_expr *expr)
315 {
316 	nft_ct_l3proto_module_put(ctx->afi->family);
317 }
318 
319 static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
320 {
321 	const struct nft_ct *priv = nft_expr_priv(expr);
322 
323 	if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
324 		goto nla_put_failure;
325 	if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
326 		goto nla_put_failure;
327 
328 	switch (priv->key) {
329 	case NFT_CT_PROTOCOL:
330 	case NFT_CT_SRC:
331 	case NFT_CT_DST:
332 	case NFT_CT_PROTO_SRC:
333 	case NFT_CT_PROTO_DST:
334 		if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
335 			goto nla_put_failure;
336 	default:
337 		break;
338 	}
339 
340 	return 0;
341 
342 nla_put_failure:
343 	return -1;
344 }
345 
346 static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
347 {
348 	const struct nft_ct *priv = nft_expr_priv(expr);
349 
350 	if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg)))
351 		goto nla_put_failure;
352 	if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
353 		goto nla_put_failure;
354 	return 0;
355 
356 nla_put_failure:
357 	return -1;
358 }
359 
360 static struct nft_expr_type nft_ct_type;
361 static const struct nft_expr_ops nft_ct_get_ops = {
362 	.type		= &nft_ct_type,
363 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_ct)),
364 	.eval		= nft_ct_get_eval,
365 	.init		= nft_ct_get_init,
366 	.destroy	= nft_ct_destroy,
367 	.dump		= nft_ct_get_dump,
368 };
369 
370 static const struct nft_expr_ops nft_ct_set_ops = {
371 	.type		= &nft_ct_type,
372 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_ct)),
373 	.eval		= nft_ct_set_eval,
374 	.init		= nft_ct_set_init,
375 	.destroy	= nft_ct_destroy,
376 	.dump		= nft_ct_set_dump,
377 };
378 
379 static const struct nft_expr_ops *
380 nft_ct_select_ops(const struct nft_ctx *ctx,
381 		    const struct nlattr * const tb[])
382 {
383 	if (tb[NFTA_CT_KEY] == NULL)
384 		return ERR_PTR(-EINVAL);
385 
386 	if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
387 		return ERR_PTR(-EINVAL);
388 
389 	if (tb[NFTA_CT_DREG])
390 		return &nft_ct_get_ops;
391 
392 	if (tb[NFTA_CT_SREG])
393 		return &nft_ct_set_ops;
394 
395 	return ERR_PTR(-EINVAL);
396 }
397 
398 static struct nft_expr_type nft_ct_type __read_mostly = {
399 	.name		= "ct",
400 	.select_ops	= &nft_ct_select_ops,
401 	.policy		= nft_ct_policy,
402 	.maxattr	= NFTA_CT_MAX,
403 	.owner		= THIS_MODULE,
404 };
405 
406 static int __init nft_ct_module_init(void)
407 {
408 	return nft_register_expr(&nft_ct_type);
409 }
410 
411 static void __exit nft_ct_module_exit(void)
412 {
413 	nft_unregister_expr(&nft_ct_type);
414 }
415 
416 module_init(nft_ct_module_init);
417 module_exit(nft_ct_module_exit);
418 
419 MODULE_LICENSE("GPL");
420 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
421 MODULE_ALIAS_NFT_EXPR("ct");
422