]> Pileus Git - ~andy/linux/blob - net/netfilter/nft_meta.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[~andy/linux] / net / netfilter / nft_meta.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/dst.h>
18 #include <net/sock.h>
19 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
20 #include <net/netfilter/nf_tables.h>
21
22 struct nft_meta {
23         enum nft_meta_keys      key:8;
24         union {
25                 enum nft_registers      dreg:8;
26                 enum nft_registers      sreg:8;
27         };
28 };
29
30 static void nft_meta_get_eval(const struct nft_expr *expr,
31                               struct nft_data data[NFT_REG_MAX + 1],
32                               const struct nft_pktinfo *pkt)
33 {
34         const struct nft_meta *priv = nft_expr_priv(expr);
35         const struct sk_buff *skb = pkt->skb;
36         const struct net_device *in = pkt->in, *out = pkt->out;
37         struct nft_data *dest = &data[priv->dreg];
38
39         switch (priv->key) {
40         case NFT_META_LEN:
41                 dest->data[0] = skb->len;
42                 break;
43         case NFT_META_PROTOCOL:
44                 *(__be16 *)dest->data = skb->protocol;
45                 break;
46         case NFT_META_PRIORITY:
47                 dest->data[0] = skb->priority;
48                 break;
49         case NFT_META_MARK:
50                 dest->data[0] = skb->mark;
51                 break;
52         case NFT_META_IIF:
53                 if (in == NULL)
54                         goto err;
55                 dest->data[0] = in->ifindex;
56                 break;
57         case NFT_META_OIF:
58                 if (out == NULL)
59                         goto err;
60                 dest->data[0] = out->ifindex;
61                 break;
62         case NFT_META_IIFNAME:
63                 if (in == NULL)
64                         goto err;
65                 strncpy((char *)dest->data, in->name, sizeof(dest->data));
66                 break;
67         case NFT_META_OIFNAME:
68                 if (out == NULL)
69                         goto err;
70                 strncpy((char *)dest->data, out->name, sizeof(dest->data));
71                 break;
72         case NFT_META_IIFTYPE:
73                 if (in == NULL)
74                         goto err;
75                 *(u16 *)dest->data = in->type;
76                 break;
77         case NFT_META_OIFTYPE:
78                 if (out == NULL)
79                         goto err;
80                 *(u16 *)dest->data = out->type;
81                 break;
82         case NFT_META_SKUID:
83                 if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT)
84                         goto err;
85
86                 read_lock_bh(&skb->sk->sk_callback_lock);
87                 if (skb->sk->sk_socket == NULL ||
88                     skb->sk->sk_socket->file == NULL) {
89                         read_unlock_bh(&skb->sk->sk_callback_lock);
90                         goto err;
91                 }
92
93                 dest->data[0] =
94                         from_kuid_munged(&init_user_ns,
95                                 skb->sk->sk_socket->file->f_cred->fsuid);
96                 read_unlock_bh(&skb->sk->sk_callback_lock);
97                 break;
98         case NFT_META_SKGID:
99                 if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT)
100                         goto err;
101
102                 read_lock_bh(&skb->sk->sk_callback_lock);
103                 if (skb->sk->sk_socket == NULL ||
104                     skb->sk->sk_socket->file == NULL) {
105                         read_unlock_bh(&skb->sk->sk_callback_lock);
106                         goto err;
107                 }
108                 dest->data[0] =
109                         from_kgid_munged(&init_user_ns,
110                                  skb->sk->sk_socket->file->f_cred->fsgid);
111                 read_unlock_bh(&skb->sk->sk_callback_lock);
112                 break;
113 #ifdef CONFIG_NET_CLS_ROUTE
114         case NFT_META_RTCLASSID: {
115                 const struct dst_entry *dst = skb_dst(skb);
116
117                 if (dst == NULL)
118                         goto err;
119                 dest->data[0] = dst->tclassid;
120                 break;
121         }
122 #endif
123 #ifdef CONFIG_NETWORK_SECMARK
124         case NFT_META_SECMARK:
125                 dest->data[0] = skb->secmark;
126                 break;
127 #endif
128         default:
129                 WARN_ON(1);
130                 goto err;
131         }
132         return;
133
134 err:
135         data[NFT_REG_VERDICT].verdict = NFT_BREAK;
136 }
137
138 static void nft_meta_set_eval(const struct nft_expr *expr,
139                               struct nft_data data[NFT_REG_MAX + 1],
140                               const struct nft_pktinfo *pkt)
141 {
142         const struct nft_meta *meta = nft_expr_priv(expr);
143         struct sk_buff *skb = pkt->skb;
144         u32 value = data[meta->sreg].data[0];
145
146         switch (meta->key) {
147         case NFT_META_MARK:
148                 skb->mark = value;
149                 break;
150         case NFT_META_PRIORITY:
151                 skb->priority = value;
152                 break;
153         case NFT_META_NFTRACE:
154                 skb->nf_trace = 1;
155                 break;
156         default:
157                 WARN_ON(1);
158         }
159 }
160
161 static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
162         [NFTA_META_DREG]        = { .type = NLA_U32 },
163         [NFTA_META_KEY]         = { .type = NLA_U32 },
164         [NFTA_META_SREG]        = { .type = NLA_U32 },
165 };
166
167 static int nft_meta_init_validate_set(uint32_t key)
168 {
169         switch (key) {
170         case NFT_META_MARK:
171         case NFT_META_PRIORITY:
172         case NFT_META_NFTRACE:
173                 return 0;
174         default:
175                 return -EOPNOTSUPP;
176         }
177 }
178
179 static int nft_meta_init_validate_get(uint32_t key)
180 {
181         switch (key) {
182         case NFT_META_LEN:
183         case NFT_META_PROTOCOL:
184         case NFT_META_PRIORITY:
185         case NFT_META_MARK:
186         case NFT_META_IIF:
187         case NFT_META_OIF:
188         case NFT_META_IIFNAME:
189         case NFT_META_OIFNAME:
190         case NFT_META_IIFTYPE:
191         case NFT_META_OIFTYPE:
192         case NFT_META_SKUID:
193         case NFT_META_SKGID:
194 #ifdef CONFIG_NET_CLS_ROUTE
195         case NFT_META_RTCLASSID:
196 #endif
197 #ifdef CONFIG_NETWORK_SECMARK
198         case NFT_META_SECMARK:
199 #endif
200                 return 0;
201         default:
202                 return -EOPNOTSUPP;
203         }
204
205 }
206
207 static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
208                          const struct nlattr * const tb[])
209 {
210         struct nft_meta *priv = nft_expr_priv(expr);
211         int err;
212
213         priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
214
215         if (tb[NFTA_META_DREG]) {
216                 err = nft_meta_init_validate_get(priv->key);
217                 if (err < 0)
218                         return err;
219
220                 priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
221                 err = nft_validate_output_register(priv->dreg);
222                 if (err < 0)
223                         return err;
224
225                 return nft_validate_data_load(ctx, priv->dreg, NULL,
226                                               NFT_DATA_VALUE);
227         }
228
229         err = nft_meta_init_validate_set(priv->key);
230         if (err < 0)
231                 return err;
232
233         priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
234
235         return 0;
236 }
237
238 static int nft_meta_get_dump(struct sk_buff *skb,
239                              const struct nft_expr *expr)
240 {
241         const struct nft_meta *priv = nft_expr_priv(expr);
242
243         if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
244                 goto nla_put_failure;
245         if (nla_put_be32(skb, NFTA_META_DREG, htonl(priv->dreg)))
246                 goto nla_put_failure;
247         return 0;
248
249 nla_put_failure:
250         return -1;
251 }
252
253 static int nft_meta_set_dump(struct sk_buff *skb,
254                              const struct nft_expr *expr)
255 {
256         const struct nft_meta *priv = nft_expr_priv(expr);
257
258         if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
259                 goto nla_put_failure;
260         if (nla_put_be32(skb, NFTA_META_SREG, htonl(priv->sreg)))
261                 goto nla_put_failure;
262
263         return 0;
264
265 nla_put_failure:
266         return -1;
267 }
268
269 static struct nft_expr_type nft_meta_type;
270 static const struct nft_expr_ops nft_meta_get_ops = {
271         .type           = &nft_meta_type,
272         .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
273         .eval           = nft_meta_get_eval,
274         .init           = nft_meta_init,
275         .dump           = nft_meta_get_dump,
276 };
277
278 static const struct nft_expr_ops nft_meta_set_ops = {
279         .type           = &nft_meta_type,
280         .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
281         .eval           = nft_meta_set_eval,
282         .init           = nft_meta_init,
283         .dump           = nft_meta_set_dump,
284 };
285
286 static const struct nft_expr_ops *
287 nft_meta_select_ops(const struct nft_ctx *ctx,
288                     const struct nlattr * const tb[])
289 {
290         if (tb[NFTA_META_KEY] == NULL)
291                 return ERR_PTR(-EINVAL);
292
293         if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
294                 return ERR_PTR(-EINVAL);
295
296         if (tb[NFTA_META_DREG])
297                 return &nft_meta_get_ops;
298
299         if (tb[NFTA_META_SREG])
300                 return &nft_meta_set_ops;
301
302         return ERR_PTR(-EINVAL);
303 }
304
305 static struct nft_expr_type nft_meta_type __read_mostly = {
306         .name           = "meta",
307         .select_ops     = &nft_meta_select_ops,
308         .policy         = nft_meta_policy,
309         .maxattr        = NFTA_META_MAX,
310         .owner          = THIS_MODULE,
311 };
312
313 static int __init nft_meta_module_init(void)
314 {
315         return nft_register_expr(&nft_meta_type);
316 }
317
318 static void __exit nft_meta_module_exit(void)
319 {
320         nft_unregister_expr(&nft_meta_type);
321 }
322
323 module_init(nft_meta_module_init);
324 module_exit(nft_meta_module_exit);
325
326 MODULE_LICENSE("GPL");
327 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
328 MODULE_ALIAS_NFT_EXPR("meta");