Merge branch 'akpm' (fixes from Andrew)
[linux-drm-fsl-dcu.git] / net / netfilter / nft_ct.c
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
22 struct nft_ct {
23         enum nft_ct_keys        key:8;
24         enum ip_conntrack_dir   dir:8;
25         enum nft_registers      dreg:8;
26         uint8_t                 family;
27 };
28
29 static void nft_ct_eval(const struct nft_expr *expr,
30                         struct nft_data data[NFT_REG_MAX + 1],
31                         const struct nft_pktinfo *pkt)
32 {
33         const struct nft_ct *priv = nft_expr_priv(expr);
34         struct nft_data *dest = &data[priv->dreg];
35         enum ip_conntrack_info ctinfo;
36         const struct nf_conn *ct;
37         const struct nf_conn_help *help;
38         const struct nf_conntrack_tuple *tuple;
39         const struct nf_conntrack_helper *helper;
40         long diff;
41         unsigned int state;
42
43         ct = nf_ct_get(pkt->skb, &ctinfo);
44
45         switch (priv->key) {
46         case NFT_CT_STATE:
47                 if (ct == NULL)
48                         state = NF_CT_STATE_INVALID_BIT;
49                 else if (nf_ct_is_untracked(ct))
50                         state = NF_CT_STATE_UNTRACKED_BIT;
51                 else
52                         state = NF_CT_STATE_BIT(ctinfo);
53                 dest->data[0] = state;
54                 return;
55         }
56
57         if (ct == NULL)
58                 goto err;
59
60         switch (priv->key) {
61         case NFT_CT_DIRECTION:
62                 dest->data[0] = CTINFO2DIR(ctinfo);
63                 return;
64         case NFT_CT_STATUS:
65                 dest->data[0] = ct->status;
66                 return;
67 #ifdef CONFIG_NF_CONNTRACK_MARK
68         case NFT_CT_MARK:
69                 dest->data[0] = ct->mark;
70                 return;
71 #endif
72 #ifdef CONFIG_NF_CONNTRACK_SECMARK
73         case NFT_CT_SECMARK:
74                 dest->data[0] = ct->secmark;
75                 return;
76 #endif
77         case NFT_CT_EXPIRATION:
78                 diff = (long)jiffies - (long)ct->timeout.expires;
79                 if (diff < 0)
80                         diff = 0;
81                 dest->data[0] = jiffies_to_msecs(diff);
82                 return;
83         case NFT_CT_HELPER:
84                 if (ct->master == NULL)
85                         goto err;
86                 help = nfct_help(ct->master);
87                 if (help == NULL)
88                         goto err;
89                 helper = rcu_dereference(help->helper);
90                 if (helper == NULL)
91                         goto err;
92                 if (strlen(helper->name) >= sizeof(dest->data))
93                         goto err;
94                 strncpy((char *)dest->data, helper->name, sizeof(dest->data));
95                 return;
96         }
97
98         tuple = &ct->tuplehash[priv->dir].tuple;
99         switch (priv->key) {
100         case NFT_CT_L3PROTOCOL:
101                 dest->data[0] = nf_ct_l3num(ct);
102                 return;
103         case NFT_CT_SRC:
104                 memcpy(dest->data, tuple->src.u3.all,
105                        nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
106                 return;
107         case NFT_CT_DST:
108                 memcpy(dest->data, tuple->dst.u3.all,
109                        nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
110                 return;
111         case NFT_CT_PROTOCOL:
112                 dest->data[0] = nf_ct_protonum(ct);
113                 return;
114         case NFT_CT_PROTO_SRC:
115                 dest->data[0] = (__force __u16)tuple->src.u.all;
116                 return;
117         case NFT_CT_PROTO_DST:
118                 dest->data[0] = (__force __u16)tuple->dst.u.all;
119                 return;
120         }
121         return;
122 err:
123         data[NFT_REG_VERDICT].verdict = NFT_BREAK;
124 }
125
126 static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
127         [NFTA_CT_DREG]          = { .type = NLA_U32 },
128         [NFTA_CT_KEY]           = { .type = NLA_U32 },
129         [NFTA_CT_DIRECTION]     = { .type = NLA_U8 },
130 };
131
132 static int nft_ct_init(const struct nft_ctx *ctx,
133                        const struct nft_expr *expr,
134                        const struct nlattr * const tb[])
135 {
136         struct nft_ct *priv = nft_expr_priv(expr);
137         int err;
138
139         if (tb[NFTA_CT_DREG] == NULL ||
140             tb[NFTA_CT_KEY] == NULL)
141                 return -EINVAL;
142
143         priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
144         if (tb[NFTA_CT_DIRECTION] != NULL) {
145                 priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
146                 switch (priv->dir) {
147                 case IP_CT_DIR_ORIGINAL:
148                 case IP_CT_DIR_REPLY:
149                         break;
150                 default:
151                         return -EINVAL;
152                 }
153         }
154
155         switch (priv->key) {
156         case NFT_CT_STATE:
157         case NFT_CT_DIRECTION:
158         case NFT_CT_STATUS:
159 #ifdef CONFIG_NF_CONNTRACK_MARK
160         case NFT_CT_MARK:
161 #endif
162 #ifdef CONFIG_NF_CONNTRACK_SECMARK
163         case NFT_CT_SECMARK:
164 #endif
165         case NFT_CT_EXPIRATION:
166         case NFT_CT_HELPER:
167                 if (tb[NFTA_CT_DIRECTION] != NULL)
168                         return -EINVAL;
169                 break;
170         case NFT_CT_PROTOCOL:
171         case NFT_CT_SRC:
172         case NFT_CT_DST:
173         case NFT_CT_PROTO_SRC:
174         case NFT_CT_PROTO_DST:
175                 if (tb[NFTA_CT_DIRECTION] == NULL)
176                         return -EINVAL;
177                 break;
178         default:
179                 return -EOPNOTSUPP;
180         }
181
182         err = nf_ct_l3proto_try_module_get(ctx->afi->family);
183         if (err < 0)
184                 return err;
185         priv->family = ctx->afi->family;
186
187         priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
188         err = nft_validate_output_register(priv->dreg);
189         if (err < 0)
190                 goto err1;
191
192         err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
193         if (err < 0)
194                 goto err1;
195         return 0;
196
197 err1:
198         nf_ct_l3proto_module_put(ctx->afi->family);
199         return err;
200 }
201
202 static void nft_ct_destroy(const struct nft_expr *expr)
203 {
204         struct nft_ct *priv = nft_expr_priv(expr);
205
206         nf_ct_l3proto_module_put(priv->family);
207 }
208
209 static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr)
210 {
211         const struct nft_ct *priv = nft_expr_priv(expr);
212
213         if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
214                 goto nla_put_failure;
215         if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
216                 goto nla_put_failure;
217         if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
218                 goto nla_put_failure;
219         return 0;
220
221 nla_put_failure:
222         return -1;
223 }
224
225 static struct nft_expr_type nft_ct_type;
226 static const struct nft_expr_ops nft_ct_ops = {
227         .type           = &nft_ct_type,
228         .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
229         .eval           = nft_ct_eval,
230         .init           = nft_ct_init,
231         .destroy        = nft_ct_destroy,
232         .dump           = nft_ct_dump,
233 };
234
235 static struct nft_expr_type nft_ct_type __read_mostly = {
236         .name           = "ct",
237         .ops            = &nft_ct_ops,
238         .policy         = nft_ct_policy,
239         .maxattr        = NFTA_CT_MAX,
240         .owner          = THIS_MODULE,
241 };
242
243 static int __init nft_ct_module_init(void)
244 {
245         return nft_register_expr(&nft_ct_type);
246 }
247
248 static void __exit nft_ct_module_exit(void)
249 {
250         nft_unregister_expr(&nft_ct_type);
251 }
252
253 module_init(nft_ct_module_init);
254 module_exit(nft_ct_module_exit);
255
256 MODULE_LICENSE("GPL");
257 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
258 MODULE_ALIAS_NFT_EXPR("ct");