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