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