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