xref: /openbmc/linux/net/netfilter/nft_meta.c (revision 6a613ac6)
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 <linux/in.h>
18 #include <linux/ip.h>
19 #include <linux/ipv6.h>
20 #include <linux/smp.h>
21 #include <net/dst.h>
22 #include <net/sock.h>
23 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
24 #include <net/netfilter/nf_tables.h>
25 #include <net/netfilter/nft_meta.h>
26 
27 void nft_meta_get_eval(const struct nft_expr *expr,
28 		       struct nft_regs *regs,
29 		       const struct nft_pktinfo *pkt)
30 {
31 	const struct nft_meta *priv = nft_expr_priv(expr);
32 	const struct sk_buff *skb = pkt->skb;
33 	const struct net_device *in = pkt->in, *out = pkt->out;
34 	struct sock *sk;
35 	u32 *dest = &regs->data[priv->dreg];
36 
37 	switch (priv->key) {
38 	case NFT_META_LEN:
39 		*dest = skb->len;
40 		break;
41 	case NFT_META_PROTOCOL:
42 		*dest = 0;
43 		*(__be16 *)dest = skb->protocol;
44 		break;
45 	case NFT_META_NFPROTO:
46 		*dest = pkt->pf;
47 		break;
48 	case NFT_META_L4PROTO:
49 		*dest = pkt->tprot;
50 		break;
51 	case NFT_META_PRIORITY:
52 		*dest = skb->priority;
53 		break;
54 	case NFT_META_MARK:
55 		*dest = skb->mark;
56 		break;
57 	case NFT_META_IIF:
58 		if (in == NULL)
59 			goto err;
60 		*dest = in->ifindex;
61 		break;
62 	case NFT_META_OIF:
63 		if (out == NULL)
64 			goto err;
65 		*dest = out->ifindex;
66 		break;
67 	case NFT_META_IIFNAME:
68 		if (in == NULL)
69 			goto err;
70 		strncpy((char *)dest, in->name, IFNAMSIZ);
71 		break;
72 	case NFT_META_OIFNAME:
73 		if (out == NULL)
74 			goto err;
75 		strncpy((char *)dest, out->name, IFNAMSIZ);
76 		break;
77 	case NFT_META_IIFTYPE:
78 		if (in == NULL)
79 			goto err;
80 		*dest = 0;
81 		*(u16 *)dest = in->type;
82 		break;
83 	case NFT_META_OIFTYPE:
84 		if (out == NULL)
85 			goto err;
86 		*dest = 0;
87 		*(u16 *)dest = out->type;
88 		break;
89 	case NFT_META_SKUID:
90 		sk = skb_to_full_sk(skb);
91 		if (!sk || !sk_fullsock(sk))
92 			goto err;
93 
94 		read_lock_bh(&sk->sk_callback_lock);
95 		if (sk->sk_socket == NULL ||
96 		    sk->sk_socket->file == NULL) {
97 			read_unlock_bh(&sk->sk_callback_lock);
98 			goto err;
99 		}
100 
101 		*dest =	from_kuid_munged(&init_user_ns,
102 				sk->sk_socket->file->f_cred->fsuid);
103 		read_unlock_bh(&sk->sk_callback_lock);
104 		break;
105 	case NFT_META_SKGID:
106 		sk = skb_to_full_sk(skb);
107 		if (!sk || !sk_fullsock(sk))
108 			goto err;
109 
110 		read_lock_bh(&sk->sk_callback_lock);
111 		if (sk->sk_socket == NULL ||
112 		    sk->sk_socket->file == NULL) {
113 			read_unlock_bh(&sk->sk_callback_lock);
114 			goto err;
115 		}
116 		*dest =	from_kgid_munged(&init_user_ns,
117 				 sk->sk_socket->file->f_cred->fsgid);
118 		read_unlock_bh(&sk->sk_callback_lock);
119 		break;
120 #ifdef CONFIG_IP_ROUTE_CLASSID
121 	case NFT_META_RTCLASSID: {
122 		const struct dst_entry *dst = skb_dst(skb);
123 
124 		if (dst == NULL)
125 			goto err;
126 		*dest = dst->tclassid;
127 		break;
128 	}
129 #endif
130 #ifdef CONFIG_NETWORK_SECMARK
131 	case NFT_META_SECMARK:
132 		*dest = skb->secmark;
133 		break;
134 #endif
135 	case NFT_META_PKTTYPE:
136 		if (skb->pkt_type != PACKET_LOOPBACK) {
137 			*dest = skb->pkt_type;
138 			break;
139 		}
140 
141 		switch (pkt->pf) {
142 		case NFPROTO_IPV4:
143 			if (ipv4_is_multicast(ip_hdr(skb)->daddr))
144 				*dest = PACKET_MULTICAST;
145 			else
146 				*dest = PACKET_BROADCAST;
147 			break;
148 		case NFPROTO_IPV6:
149 			if (ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF)
150 				*dest = PACKET_MULTICAST;
151 			else
152 				*dest = PACKET_BROADCAST;
153 			break;
154 		default:
155 			WARN_ON(1);
156 			goto err;
157 		}
158 		break;
159 	case NFT_META_CPU:
160 		*dest = raw_smp_processor_id();
161 		break;
162 	case NFT_META_IIFGROUP:
163 		if (in == NULL)
164 			goto err;
165 		*dest = in->group;
166 		break;
167 	case NFT_META_OIFGROUP:
168 		if (out == NULL)
169 			goto err;
170 		*dest = out->group;
171 		break;
172 #ifdef CONFIG_CGROUP_NET_CLASSID
173 	case NFT_META_CGROUP:
174 		sk = skb_to_full_sk(skb);
175 		if (!sk || !sk_fullsock(sk))
176 			goto err;
177 		*dest = sk->sk_classid;
178 		break;
179 #endif
180 	default:
181 		WARN_ON(1);
182 		goto err;
183 	}
184 	return;
185 
186 err:
187 	regs->verdict.code = NFT_BREAK;
188 }
189 EXPORT_SYMBOL_GPL(nft_meta_get_eval);
190 
191 void nft_meta_set_eval(const struct nft_expr *expr,
192 		       struct nft_regs *regs,
193 		       const struct nft_pktinfo *pkt)
194 {
195 	const struct nft_meta *meta = nft_expr_priv(expr);
196 	struct sk_buff *skb = pkt->skb;
197 	u32 value = regs->data[meta->sreg];
198 
199 	switch (meta->key) {
200 	case NFT_META_MARK:
201 		skb->mark = value;
202 		break;
203 	case NFT_META_PRIORITY:
204 		skb->priority = value;
205 		break;
206 	case NFT_META_NFTRACE:
207 		skb->nf_trace = 1;
208 		break;
209 	default:
210 		WARN_ON(1);
211 	}
212 }
213 EXPORT_SYMBOL_GPL(nft_meta_set_eval);
214 
215 const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
216 	[NFTA_META_DREG]	= { .type = NLA_U32 },
217 	[NFTA_META_KEY]		= { .type = NLA_U32 },
218 	[NFTA_META_SREG]	= { .type = NLA_U32 },
219 };
220 EXPORT_SYMBOL_GPL(nft_meta_policy);
221 
222 int nft_meta_get_init(const struct nft_ctx *ctx,
223 		      const struct nft_expr *expr,
224 		      const struct nlattr * const tb[])
225 {
226 	struct nft_meta *priv = nft_expr_priv(expr);
227 	unsigned int len;
228 
229 	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
230 	switch (priv->key) {
231 	case NFT_META_PROTOCOL:
232 	case NFT_META_IIFTYPE:
233 	case NFT_META_OIFTYPE:
234 		len = sizeof(u16);
235 		break;
236 	case NFT_META_NFPROTO:
237 	case NFT_META_L4PROTO:
238 	case NFT_META_LEN:
239 	case NFT_META_PRIORITY:
240 	case NFT_META_MARK:
241 	case NFT_META_IIF:
242 	case NFT_META_OIF:
243 	case NFT_META_SKUID:
244 	case NFT_META_SKGID:
245 #ifdef CONFIG_IP_ROUTE_CLASSID
246 	case NFT_META_RTCLASSID:
247 #endif
248 #ifdef CONFIG_NETWORK_SECMARK
249 	case NFT_META_SECMARK:
250 #endif
251 	case NFT_META_PKTTYPE:
252 	case NFT_META_CPU:
253 	case NFT_META_IIFGROUP:
254 	case NFT_META_OIFGROUP:
255 #ifdef CONFIG_CGROUP_NET_CLASSID
256 	case NFT_META_CGROUP:
257 #endif
258 		len = sizeof(u32);
259 		break;
260 	case NFT_META_IIFNAME:
261 	case NFT_META_OIFNAME:
262 		len = IFNAMSIZ;
263 		break;
264 	default:
265 		return -EOPNOTSUPP;
266 	}
267 
268 	priv->dreg = nft_parse_register(tb[NFTA_META_DREG]);
269 	return nft_validate_register_store(ctx, priv->dreg, NULL,
270 					   NFT_DATA_VALUE, len);
271 }
272 EXPORT_SYMBOL_GPL(nft_meta_get_init);
273 
274 int nft_meta_set_init(const struct nft_ctx *ctx,
275 		      const struct nft_expr *expr,
276 		      const struct nlattr * const tb[])
277 {
278 	struct nft_meta *priv = nft_expr_priv(expr);
279 	unsigned int len;
280 	int err;
281 
282 	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
283 	switch (priv->key) {
284 	case NFT_META_MARK:
285 	case NFT_META_PRIORITY:
286 		len = sizeof(u32);
287 		break;
288 	case NFT_META_NFTRACE:
289 		len = sizeof(u8);
290 		break;
291 	default:
292 		return -EOPNOTSUPP;
293 	}
294 
295 	priv->sreg = nft_parse_register(tb[NFTA_META_SREG]);
296 	err = nft_validate_register_load(priv->sreg, len);
297 	if (err < 0)
298 		return err;
299 
300 	return 0;
301 }
302 EXPORT_SYMBOL_GPL(nft_meta_set_init);
303 
304 int nft_meta_get_dump(struct sk_buff *skb,
305 		      const struct nft_expr *expr)
306 {
307 	const struct nft_meta *priv = nft_expr_priv(expr);
308 
309 	if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
310 		goto nla_put_failure;
311 	if (nft_dump_register(skb, NFTA_META_DREG, priv->dreg))
312 		goto nla_put_failure;
313 	return 0;
314 
315 nla_put_failure:
316 	return -1;
317 }
318 EXPORT_SYMBOL_GPL(nft_meta_get_dump);
319 
320 int nft_meta_set_dump(struct sk_buff *skb,
321 		      const struct nft_expr *expr)
322 {
323 	const struct nft_meta *priv = nft_expr_priv(expr);
324 
325 	if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
326 		goto nla_put_failure;
327 	if (nft_dump_register(skb, NFTA_META_SREG, priv->sreg))
328 		goto nla_put_failure;
329 
330 	return 0;
331 
332 nla_put_failure:
333 	return -1;
334 }
335 EXPORT_SYMBOL_GPL(nft_meta_set_dump);
336 
337 static struct nft_expr_type nft_meta_type;
338 static const struct nft_expr_ops nft_meta_get_ops = {
339 	.type		= &nft_meta_type,
340 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
341 	.eval		= nft_meta_get_eval,
342 	.init		= nft_meta_get_init,
343 	.dump		= nft_meta_get_dump,
344 };
345 
346 static const struct nft_expr_ops nft_meta_set_ops = {
347 	.type		= &nft_meta_type,
348 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
349 	.eval		= nft_meta_set_eval,
350 	.init		= nft_meta_set_init,
351 	.dump		= nft_meta_set_dump,
352 };
353 
354 static const struct nft_expr_ops *
355 nft_meta_select_ops(const struct nft_ctx *ctx,
356 		    const struct nlattr * const tb[])
357 {
358 	if (tb[NFTA_META_KEY] == NULL)
359 		return ERR_PTR(-EINVAL);
360 
361 	if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
362 		return ERR_PTR(-EINVAL);
363 
364 	if (tb[NFTA_META_DREG])
365 		return &nft_meta_get_ops;
366 
367 	if (tb[NFTA_META_SREG])
368 		return &nft_meta_set_ops;
369 
370 	return ERR_PTR(-EINVAL);
371 }
372 
373 static struct nft_expr_type nft_meta_type __read_mostly = {
374 	.name		= "meta",
375 	.select_ops	= &nft_meta_select_ops,
376 	.policy		= nft_meta_policy,
377 	.maxattr	= NFTA_META_MAX,
378 	.owner		= THIS_MODULE,
379 };
380 
381 static int __init nft_meta_module_init(void)
382 {
383 	return nft_register_expr(&nft_meta_type);
384 }
385 
386 static void __exit nft_meta_module_exit(void)
387 {
388 	nft_unregister_expr(&nft_meta_type);
389 }
390 
391 module_init(nft_meta_module_init);
392 module_exit(nft_meta_module_exit);
393 
394 MODULE_LICENSE("GPL");
395 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
396 MODULE_ALIAS_NFT_EXPR("meta");
397