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
24struct 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
33static 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;
148err:
149	regs->verdict.code = NFT_BREAK;
150}
151
152static 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
182static 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
189static 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
207err2:
208	nf_ct_l3proto_module_put(NFPROTO_IPV4);
209err1:
210	return err;
211}
212
213static 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
222static 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
322static 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
353static 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
359static 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
382nla_put_failure:
383	return -1;
384}
385
386static 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
396nla_put_failure:
397	return -1;
398}
399
400static struct nft_expr_type nft_ct_type;
401static 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
410static 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
419static const struct nft_expr_ops *
420nft_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
438static 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
446static int __init nft_ct_module_init(void)
447{
448	return nft_register_expr(&nft_ct_type);
449}
450
451static void __exit nft_ct_module_exit(void)
452{
453	nft_unregister_expr(&nft_ct_type);
454}
455
456module_init(nft_ct_module_init);
457module_exit(nft_ct_module_exit);
458
459MODULE_LICENSE("GPL");
460MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
461MODULE_ALIAS_NFT_EXPR("ct");
462