1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> |
4 | * Copyright (c) 2014 Intel Corporation |
5 | * Author: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> |
6 | * |
7 | * Development of this code funded by Astaro AG (http://www.astaro.com/) |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/netlink.h> |
12 | #include <linux/netfilter.h> |
13 | #include <linux/netfilter/nf_tables.h> |
14 | #include <linux/in.h> |
15 | #include <linux/ip.h> |
16 | #include <linux/ipv6.h> |
17 | #include <linux/random.h> |
18 | #include <linux/smp.h> |
19 | #include <linux/static_key.h> |
20 | #include <net/dst.h> |
21 | #include <net/ip.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/nf_tables_core.h> |
26 | #include <net/netfilter/nft_meta.h> |
27 | #include <net/netfilter/nf_tables_offload.h> |
28 | |
29 | #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ |
30 | |
31 | #define NFT_META_SECS_PER_MINUTE 60 |
32 | #define NFT_META_SECS_PER_HOUR 3600 |
33 | #define NFT_META_SECS_PER_DAY 86400 |
34 | #define NFT_META_DAYS_PER_WEEK 7 |
35 | |
36 | static u8 nft_meta_weekday(void) |
37 | { |
38 | time64_t secs = ktime_get_real_seconds(); |
39 | unsigned int dse; |
40 | u8 wday; |
41 | |
42 | secs -= NFT_META_SECS_PER_MINUTE * sys_tz.tz_minuteswest; |
43 | dse = div_u64(dividend: secs, NFT_META_SECS_PER_DAY); |
44 | wday = (4 + dse) % NFT_META_DAYS_PER_WEEK; |
45 | |
46 | return wday; |
47 | } |
48 | |
49 | static u32 nft_meta_hour(time64_t secs) |
50 | { |
51 | struct tm tm; |
52 | |
53 | time64_to_tm(totalsecs: secs, offset: 0, result: &tm); |
54 | |
55 | return tm.tm_hour * NFT_META_SECS_PER_HOUR |
56 | + tm.tm_min * NFT_META_SECS_PER_MINUTE |
57 | + tm.tm_sec; |
58 | } |
59 | |
60 | static noinline_for_stack void |
61 | nft_meta_get_eval_time(enum nft_meta_keys key, |
62 | u32 *dest) |
63 | { |
64 | switch (key) { |
65 | case NFT_META_TIME_NS: |
66 | nft_reg_store64(dreg: dest, val: ktime_get_real_ns()); |
67 | break; |
68 | case NFT_META_TIME_DAY: |
69 | nft_reg_store8(dreg: dest, val: nft_meta_weekday()); |
70 | break; |
71 | case NFT_META_TIME_HOUR: |
72 | *dest = nft_meta_hour(secs: ktime_get_real_seconds()); |
73 | break; |
74 | default: |
75 | break; |
76 | } |
77 | } |
78 | |
79 | static noinline bool |
80 | nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt, |
81 | u32 *dest) |
82 | { |
83 | const struct sk_buff *skb = pkt->skb; |
84 | |
85 | switch (nft_pf(pkt)) { |
86 | case NFPROTO_IPV4: |
87 | if (ipv4_is_multicast(addr: ip_hdr(skb)->daddr)) |
88 | nft_reg_store8(dreg: dest, PACKET_MULTICAST); |
89 | else |
90 | nft_reg_store8(dreg: dest, PACKET_BROADCAST); |
91 | break; |
92 | case NFPROTO_IPV6: |
93 | nft_reg_store8(dreg: dest, PACKET_MULTICAST); |
94 | break; |
95 | case NFPROTO_NETDEV: |
96 | switch (skb->protocol) { |
97 | case htons(ETH_P_IP): { |
98 | int noff = skb_network_offset(skb); |
99 | struct iphdr *iph, _iph; |
100 | |
101 | iph = skb_header_pointer(skb, offset: noff, |
102 | len: sizeof(_iph), buffer: &_iph); |
103 | if (!iph) |
104 | return false; |
105 | |
106 | if (ipv4_is_multicast(addr: iph->daddr)) |
107 | nft_reg_store8(dreg: dest, PACKET_MULTICAST); |
108 | else |
109 | nft_reg_store8(dreg: dest, PACKET_BROADCAST); |
110 | |
111 | break; |
112 | } |
113 | case htons(ETH_P_IPV6): |
114 | nft_reg_store8(dreg: dest, PACKET_MULTICAST); |
115 | break; |
116 | default: |
117 | WARN_ON_ONCE(1); |
118 | return false; |
119 | } |
120 | break; |
121 | default: |
122 | WARN_ON_ONCE(1); |
123 | return false; |
124 | } |
125 | |
126 | return true; |
127 | } |
128 | |
129 | static noinline bool |
130 | nft_meta_get_eval_skugid(enum nft_meta_keys key, |
131 | u32 *dest, |
132 | const struct nft_pktinfo *pkt) |
133 | { |
134 | struct sock *sk = skb_to_full_sk(skb: pkt->skb); |
135 | struct socket *sock; |
136 | |
137 | if (!sk || !sk_fullsock(sk) || !net_eq(net1: nft_net(pkt), net2: sock_net(sk))) |
138 | return false; |
139 | |
140 | read_lock_bh(&sk->sk_callback_lock); |
141 | sock = sk->sk_socket; |
142 | if (!sock || !sock->file) { |
143 | read_unlock_bh(&sk->sk_callback_lock); |
144 | return false; |
145 | } |
146 | |
147 | switch (key) { |
148 | case NFT_META_SKUID: |
149 | *dest = from_kuid_munged(to: sock_net(sk)->user_ns, |
150 | uid: sock->file->f_cred->fsuid); |
151 | break; |
152 | case NFT_META_SKGID: |
153 | *dest = from_kgid_munged(to: sock_net(sk)->user_ns, |
154 | gid: sock->file->f_cred->fsgid); |
155 | break; |
156 | default: |
157 | break; |
158 | } |
159 | |
160 | read_unlock_bh(&sk->sk_callback_lock); |
161 | return true; |
162 | } |
163 | |
164 | #ifdef CONFIG_CGROUP_NET_CLASSID |
165 | static noinline bool |
166 | nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt) |
167 | { |
168 | struct sock *sk = skb_to_full_sk(skb: pkt->skb); |
169 | |
170 | if (!sk || !sk_fullsock(sk) || !net_eq(net1: nft_net(pkt), net2: sock_net(sk))) |
171 | return false; |
172 | |
173 | *dest = sock_cgroup_classid(skcd: &sk->sk_cgrp_data); |
174 | return true; |
175 | } |
176 | #endif |
177 | |
178 | static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key, |
179 | u32 *dest, |
180 | const struct nft_pktinfo *pkt) |
181 | { |
182 | const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); |
183 | |
184 | switch (key) { |
185 | case NFT_META_IIFKIND: |
186 | if (!in || !in->rtnl_link_ops) |
187 | return false; |
188 | strscpy_pad(dest: (char *)dest, src: in->rtnl_link_ops->kind, IFNAMSIZ); |
189 | break; |
190 | case NFT_META_OIFKIND: |
191 | if (!out || !out->rtnl_link_ops) |
192 | return false; |
193 | strscpy_pad(dest: (char *)dest, src: out->rtnl_link_ops->kind, IFNAMSIZ); |
194 | break; |
195 | default: |
196 | return false; |
197 | } |
198 | |
199 | return true; |
200 | } |
201 | |
202 | static void nft_meta_store_ifindex(u32 *dest, const struct net_device *dev) |
203 | { |
204 | *dest = dev ? dev->ifindex : 0; |
205 | } |
206 | |
207 | static void nft_meta_store_ifname(u32 *dest, const struct net_device *dev) |
208 | { |
209 | strscpy_pad(dest: (char *)dest, src: dev ? dev->name : "" , IFNAMSIZ); |
210 | } |
211 | |
212 | static bool nft_meta_store_iftype(u32 *dest, const struct net_device *dev) |
213 | { |
214 | if (!dev) |
215 | return false; |
216 | |
217 | nft_reg_store16(dreg: dest, val: dev->type); |
218 | return true; |
219 | } |
220 | |
221 | static bool nft_meta_store_ifgroup(u32 *dest, const struct net_device *dev) |
222 | { |
223 | if (!dev) |
224 | return false; |
225 | |
226 | *dest = dev->group; |
227 | return true; |
228 | } |
229 | |
230 | static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, |
231 | const struct nft_pktinfo *pkt) |
232 | { |
233 | switch (key) { |
234 | case NFT_META_IIFNAME: |
235 | nft_meta_store_ifname(dest, dev: nft_in(pkt)); |
236 | break; |
237 | case NFT_META_OIFNAME: |
238 | nft_meta_store_ifname(dest, dev: nft_out(pkt)); |
239 | break; |
240 | case NFT_META_IIF: |
241 | nft_meta_store_ifindex(dest, dev: nft_in(pkt)); |
242 | break; |
243 | case NFT_META_OIF: |
244 | nft_meta_store_ifindex(dest, dev: nft_out(pkt)); |
245 | break; |
246 | case NFT_META_IFTYPE: |
247 | if (!nft_meta_store_iftype(dest, dev: pkt->skb->dev)) |
248 | return false; |
249 | break; |
250 | case __NFT_META_IIFTYPE: |
251 | if (!nft_meta_store_iftype(dest, dev: nft_in(pkt))) |
252 | return false; |
253 | break; |
254 | case NFT_META_OIFTYPE: |
255 | if (!nft_meta_store_iftype(dest, dev: nft_out(pkt))) |
256 | return false; |
257 | break; |
258 | case NFT_META_IIFGROUP: |
259 | if (!nft_meta_store_ifgroup(dest, dev: nft_in(pkt))) |
260 | return false; |
261 | break; |
262 | case NFT_META_OIFGROUP: |
263 | if (!nft_meta_store_ifgroup(dest, dev: nft_out(pkt))) |
264 | return false; |
265 | break; |
266 | default: |
267 | return false; |
268 | } |
269 | |
270 | return true; |
271 | } |
272 | |
273 | #ifdef CONFIG_IP_ROUTE_CLASSID |
274 | static noinline bool |
275 | nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) |
276 | { |
277 | const struct dst_entry *dst = skb_dst(skb); |
278 | |
279 | if (!dst) |
280 | return false; |
281 | |
282 | *dest = dst->tclassid; |
283 | return true; |
284 | } |
285 | #endif |
286 | |
287 | static noinline u32 nft_meta_get_eval_sdif(const struct nft_pktinfo *pkt) |
288 | { |
289 | switch (nft_pf(pkt)) { |
290 | case NFPROTO_IPV4: |
291 | return inet_sdif(skb: pkt->skb); |
292 | case NFPROTO_IPV6: |
293 | return inet6_sdif(skb: pkt->skb); |
294 | } |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | static noinline void |
300 | nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt) |
301 | { |
302 | u32 sdif = nft_meta_get_eval_sdif(pkt); |
303 | const struct net_device *dev; |
304 | |
305 | dev = sdif ? dev_get_by_index_rcu(net: nft_net(pkt), ifindex: sdif) : NULL; |
306 | nft_meta_store_ifname(dest, dev); |
307 | } |
308 | |
309 | void nft_meta_get_eval(const struct nft_expr *expr, |
310 | struct nft_regs *regs, |
311 | const struct nft_pktinfo *pkt) |
312 | { |
313 | const struct nft_meta *priv = nft_expr_priv(expr); |
314 | const struct sk_buff *skb = pkt->skb; |
315 | u32 *dest = ®s->data[priv->dreg]; |
316 | |
317 | switch (priv->key) { |
318 | case NFT_META_LEN: |
319 | *dest = skb->len; |
320 | break; |
321 | case NFT_META_PROTOCOL: |
322 | nft_reg_store16(dreg: dest, val: (__force u16)skb->protocol); |
323 | break; |
324 | case NFT_META_NFPROTO: |
325 | nft_reg_store8(dreg: dest, val: nft_pf(pkt)); |
326 | break; |
327 | case NFT_META_L4PROTO: |
328 | if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) |
329 | goto err; |
330 | nft_reg_store8(dreg: dest, val: pkt->tprot); |
331 | break; |
332 | case NFT_META_PRIORITY: |
333 | *dest = skb->priority; |
334 | break; |
335 | case NFT_META_MARK: |
336 | *dest = skb->mark; |
337 | break; |
338 | case NFT_META_IIF: |
339 | case NFT_META_OIF: |
340 | case NFT_META_IIFNAME: |
341 | case NFT_META_OIFNAME: |
342 | case NFT_META_IIFTYPE: |
343 | case NFT_META_OIFTYPE: |
344 | case NFT_META_IIFGROUP: |
345 | case NFT_META_OIFGROUP: |
346 | if (!nft_meta_get_eval_ifname(key: priv->key, dest, pkt)) |
347 | goto err; |
348 | break; |
349 | case NFT_META_SKUID: |
350 | case NFT_META_SKGID: |
351 | if (!nft_meta_get_eval_skugid(key: priv->key, dest, pkt)) |
352 | goto err; |
353 | break; |
354 | #ifdef CONFIG_IP_ROUTE_CLASSID |
355 | case NFT_META_RTCLASSID: |
356 | if (!nft_meta_get_eval_rtclassid(skb, dest)) |
357 | goto err; |
358 | break; |
359 | #endif |
360 | #ifdef CONFIG_NETWORK_SECMARK |
361 | case NFT_META_SECMARK: |
362 | *dest = skb->secmark; |
363 | break; |
364 | #endif |
365 | case NFT_META_PKTTYPE: |
366 | if (skb->pkt_type != PACKET_LOOPBACK) { |
367 | nft_reg_store8(dreg: dest, val: skb->pkt_type); |
368 | break; |
369 | } |
370 | |
371 | if (!nft_meta_get_eval_pkttype_lo(pkt, dest)) |
372 | goto err; |
373 | break; |
374 | case NFT_META_CPU: |
375 | *dest = raw_smp_processor_id(); |
376 | break; |
377 | #ifdef CONFIG_CGROUP_NET_CLASSID |
378 | case NFT_META_CGROUP: |
379 | if (!nft_meta_get_eval_cgroup(dest, pkt)) |
380 | goto err; |
381 | break; |
382 | #endif |
383 | case NFT_META_PRANDOM: |
384 | *dest = get_random_u32(); |
385 | break; |
386 | #ifdef CONFIG_XFRM |
387 | case NFT_META_SECPATH: |
388 | nft_reg_store8(dreg: dest, val: secpath_exists(skb)); |
389 | break; |
390 | #endif |
391 | case NFT_META_IIFKIND: |
392 | case NFT_META_OIFKIND: |
393 | if (!nft_meta_get_eval_kind(key: priv->key, dest, pkt)) |
394 | goto err; |
395 | break; |
396 | case NFT_META_TIME_NS: |
397 | case NFT_META_TIME_DAY: |
398 | case NFT_META_TIME_HOUR: |
399 | nft_meta_get_eval_time(key: priv->key, dest); |
400 | break; |
401 | case NFT_META_SDIF: |
402 | *dest = nft_meta_get_eval_sdif(pkt); |
403 | break; |
404 | case NFT_META_SDIFNAME: |
405 | nft_meta_get_eval_sdifname(dest, pkt); |
406 | break; |
407 | default: |
408 | WARN_ON(1); |
409 | goto err; |
410 | } |
411 | return; |
412 | |
413 | err: |
414 | regs->verdict.code = NFT_BREAK; |
415 | } |
416 | EXPORT_SYMBOL_GPL(nft_meta_get_eval); |
417 | |
418 | void nft_meta_set_eval(const struct nft_expr *expr, |
419 | struct nft_regs *regs, |
420 | const struct nft_pktinfo *pkt) |
421 | { |
422 | const struct nft_meta *meta = nft_expr_priv(expr); |
423 | struct sk_buff *skb = pkt->skb; |
424 | u32 *sreg = ®s->data[meta->sreg]; |
425 | u32 value = *sreg; |
426 | u8 value8; |
427 | |
428 | switch (meta->key) { |
429 | case NFT_META_MARK: |
430 | skb->mark = value; |
431 | break; |
432 | case NFT_META_PRIORITY: |
433 | skb->priority = value; |
434 | break; |
435 | case NFT_META_PKTTYPE: |
436 | value8 = nft_reg_load8(sreg); |
437 | |
438 | if (skb->pkt_type != value8 && |
439 | skb_pkt_type_ok(ptype: value8) && |
440 | skb_pkt_type_ok(ptype: skb->pkt_type)) |
441 | skb->pkt_type = value8; |
442 | break; |
443 | case NFT_META_NFTRACE: |
444 | value8 = nft_reg_load8(sreg); |
445 | |
446 | skb->nf_trace = !!value8; |
447 | break; |
448 | #ifdef CONFIG_NETWORK_SECMARK |
449 | case NFT_META_SECMARK: |
450 | skb->secmark = value; |
451 | break; |
452 | #endif |
453 | default: |
454 | WARN_ON(1); |
455 | } |
456 | } |
457 | EXPORT_SYMBOL_GPL(nft_meta_set_eval); |
458 | |
459 | const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { |
460 | [NFTA_META_DREG] = { .type = NLA_U32 }, |
461 | [NFTA_META_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), |
462 | [NFTA_META_SREG] = { .type = NLA_U32 }, |
463 | }; |
464 | EXPORT_SYMBOL_GPL(nft_meta_policy); |
465 | |
466 | int nft_meta_get_init(const struct nft_ctx *ctx, |
467 | const struct nft_expr *expr, |
468 | const struct nlattr * const tb[]) |
469 | { |
470 | struct nft_meta *priv = nft_expr_priv(expr); |
471 | unsigned int len; |
472 | |
473 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
474 | switch (priv->key) { |
475 | case NFT_META_PROTOCOL: |
476 | case NFT_META_IIFTYPE: |
477 | case NFT_META_OIFTYPE: |
478 | len = sizeof(u16); |
479 | break; |
480 | case NFT_META_NFPROTO: |
481 | case NFT_META_L4PROTO: |
482 | case NFT_META_LEN: |
483 | case NFT_META_PRIORITY: |
484 | case NFT_META_MARK: |
485 | case NFT_META_IIF: |
486 | case NFT_META_OIF: |
487 | case NFT_META_SDIF: |
488 | case NFT_META_SKUID: |
489 | case NFT_META_SKGID: |
490 | #ifdef CONFIG_IP_ROUTE_CLASSID |
491 | case NFT_META_RTCLASSID: |
492 | #endif |
493 | #ifdef CONFIG_NETWORK_SECMARK |
494 | case NFT_META_SECMARK: |
495 | #endif |
496 | case NFT_META_PKTTYPE: |
497 | case NFT_META_CPU: |
498 | case NFT_META_IIFGROUP: |
499 | case NFT_META_OIFGROUP: |
500 | #ifdef CONFIG_CGROUP_NET_CLASSID |
501 | case NFT_META_CGROUP: |
502 | #endif |
503 | len = sizeof(u32); |
504 | break; |
505 | case NFT_META_IIFNAME: |
506 | case NFT_META_OIFNAME: |
507 | case NFT_META_IIFKIND: |
508 | case NFT_META_OIFKIND: |
509 | case NFT_META_SDIFNAME: |
510 | len = IFNAMSIZ; |
511 | break; |
512 | case NFT_META_PRANDOM: |
513 | len = sizeof(u32); |
514 | break; |
515 | #ifdef CONFIG_XFRM |
516 | case NFT_META_SECPATH: |
517 | len = sizeof(u8); |
518 | break; |
519 | #endif |
520 | case NFT_META_TIME_NS: |
521 | len = sizeof(u64); |
522 | break; |
523 | case NFT_META_TIME_DAY: |
524 | len = sizeof(u8); |
525 | break; |
526 | case NFT_META_TIME_HOUR: |
527 | len = sizeof(u32); |
528 | break; |
529 | default: |
530 | return -EOPNOTSUPP; |
531 | } |
532 | |
533 | priv->len = len; |
534 | return nft_parse_register_store(ctx, attr: tb[NFTA_META_DREG], dreg: &priv->dreg, |
535 | NULL, type: NFT_DATA_VALUE, len); |
536 | } |
537 | EXPORT_SYMBOL_GPL(nft_meta_get_init); |
538 | |
539 | static int nft_meta_get_validate_sdif(const struct nft_ctx *ctx) |
540 | { |
541 | unsigned int hooks; |
542 | |
543 | switch (ctx->family) { |
544 | case NFPROTO_IPV4: |
545 | case NFPROTO_IPV6: |
546 | case NFPROTO_INET: |
547 | hooks = (1 << NF_INET_LOCAL_IN) | |
548 | (1 << NF_INET_FORWARD); |
549 | break; |
550 | default: |
551 | return -EOPNOTSUPP; |
552 | } |
553 | |
554 | return nft_chain_validate_hooks(chain: ctx->chain, hook_flags: hooks); |
555 | } |
556 | |
557 | static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) |
558 | { |
559 | #ifdef CONFIG_XFRM |
560 | unsigned int hooks; |
561 | |
562 | switch (ctx->family) { |
563 | case NFPROTO_NETDEV: |
564 | hooks = 1 << NF_NETDEV_INGRESS; |
565 | break; |
566 | case NFPROTO_IPV4: |
567 | case NFPROTO_IPV6: |
568 | case NFPROTO_INET: |
569 | hooks = (1 << NF_INET_PRE_ROUTING) | |
570 | (1 << NF_INET_LOCAL_IN) | |
571 | (1 << NF_INET_FORWARD); |
572 | break; |
573 | default: |
574 | return -EOPNOTSUPP; |
575 | } |
576 | |
577 | return nft_chain_validate_hooks(chain: ctx->chain, hook_flags: hooks); |
578 | #else |
579 | return 0; |
580 | #endif |
581 | } |
582 | |
583 | static int nft_meta_get_validate(const struct nft_ctx *ctx, |
584 | const struct nft_expr *expr, |
585 | const struct nft_data **data) |
586 | { |
587 | const struct nft_meta *priv = nft_expr_priv(expr); |
588 | |
589 | switch (priv->key) { |
590 | case NFT_META_SECPATH: |
591 | return nft_meta_get_validate_xfrm(ctx); |
592 | case NFT_META_SDIF: |
593 | case NFT_META_SDIFNAME: |
594 | return nft_meta_get_validate_sdif(ctx); |
595 | default: |
596 | break; |
597 | } |
598 | |
599 | return 0; |
600 | } |
601 | |
602 | int nft_meta_set_validate(const struct nft_ctx *ctx, |
603 | const struct nft_expr *expr, |
604 | const struct nft_data **data) |
605 | { |
606 | struct nft_meta *priv = nft_expr_priv(expr); |
607 | unsigned int hooks; |
608 | |
609 | if (priv->key != NFT_META_PKTTYPE) |
610 | return 0; |
611 | |
612 | switch (ctx->family) { |
613 | case NFPROTO_BRIDGE: |
614 | hooks = 1 << NF_BR_PRE_ROUTING; |
615 | break; |
616 | case NFPROTO_NETDEV: |
617 | hooks = 1 << NF_NETDEV_INGRESS; |
618 | break; |
619 | case NFPROTO_IPV4: |
620 | case NFPROTO_IPV6: |
621 | case NFPROTO_INET: |
622 | hooks = 1 << NF_INET_PRE_ROUTING; |
623 | break; |
624 | default: |
625 | return -EOPNOTSUPP; |
626 | } |
627 | |
628 | return nft_chain_validate_hooks(chain: ctx->chain, hook_flags: hooks); |
629 | } |
630 | EXPORT_SYMBOL_GPL(nft_meta_set_validate); |
631 | |
632 | int nft_meta_set_init(const struct nft_ctx *ctx, |
633 | const struct nft_expr *expr, |
634 | const struct nlattr * const tb[]) |
635 | { |
636 | struct nft_meta *priv = nft_expr_priv(expr); |
637 | unsigned int len; |
638 | int err; |
639 | |
640 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
641 | switch (priv->key) { |
642 | case NFT_META_MARK: |
643 | case NFT_META_PRIORITY: |
644 | #ifdef CONFIG_NETWORK_SECMARK |
645 | case NFT_META_SECMARK: |
646 | #endif |
647 | len = sizeof(u32); |
648 | break; |
649 | case NFT_META_NFTRACE: |
650 | len = sizeof(u8); |
651 | break; |
652 | case NFT_META_PKTTYPE: |
653 | len = sizeof(u8); |
654 | break; |
655 | default: |
656 | return -EOPNOTSUPP; |
657 | } |
658 | |
659 | priv->len = len; |
660 | err = nft_parse_register_load(attr: tb[NFTA_META_SREG], sreg: &priv->sreg, len); |
661 | if (err < 0) |
662 | return err; |
663 | |
664 | if (priv->key == NFT_META_NFTRACE) |
665 | static_branch_inc(&nft_trace_enabled); |
666 | |
667 | return 0; |
668 | } |
669 | EXPORT_SYMBOL_GPL(nft_meta_set_init); |
670 | |
671 | int nft_meta_get_dump(struct sk_buff *skb, |
672 | const struct nft_expr *expr, bool reset) |
673 | { |
674 | const struct nft_meta *priv = nft_expr_priv(expr); |
675 | |
676 | if (nla_put_be32(skb, attrtype: NFTA_META_KEY, htonl(priv->key))) |
677 | goto nla_put_failure; |
678 | if (nft_dump_register(skb, attr: NFTA_META_DREG, reg: priv->dreg)) |
679 | goto nla_put_failure; |
680 | return 0; |
681 | |
682 | nla_put_failure: |
683 | return -1; |
684 | } |
685 | EXPORT_SYMBOL_GPL(nft_meta_get_dump); |
686 | |
687 | int nft_meta_set_dump(struct sk_buff *skb, |
688 | const struct nft_expr *expr, bool reset) |
689 | { |
690 | const struct nft_meta *priv = nft_expr_priv(expr); |
691 | |
692 | if (nla_put_be32(skb, attrtype: NFTA_META_KEY, htonl(priv->key))) |
693 | goto nla_put_failure; |
694 | if (nft_dump_register(skb, attr: NFTA_META_SREG, reg: priv->sreg)) |
695 | goto nla_put_failure; |
696 | |
697 | return 0; |
698 | |
699 | nla_put_failure: |
700 | return -1; |
701 | } |
702 | EXPORT_SYMBOL_GPL(nft_meta_set_dump); |
703 | |
704 | void nft_meta_set_destroy(const struct nft_ctx *ctx, |
705 | const struct nft_expr *expr) |
706 | { |
707 | const struct nft_meta *priv = nft_expr_priv(expr); |
708 | |
709 | if (priv->key == NFT_META_NFTRACE) |
710 | static_branch_dec(&nft_trace_enabled); |
711 | } |
712 | EXPORT_SYMBOL_GPL(nft_meta_set_destroy); |
713 | |
714 | static int nft_meta_get_offload(struct nft_offload_ctx *ctx, |
715 | struct nft_flow_rule *flow, |
716 | const struct nft_expr *expr) |
717 | { |
718 | const struct nft_meta *priv = nft_expr_priv(expr); |
719 | struct nft_offload_reg *reg = &ctx->regs[priv->dreg]; |
720 | |
721 | switch (priv->key) { |
722 | case NFT_META_PROTOCOL: |
723 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto, |
724 | sizeof(__u16), reg); |
725 | nft_offload_set_dependency(ctx, type: NFT_OFFLOAD_DEP_NETWORK); |
726 | break; |
727 | case NFT_META_L4PROTO: |
728 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto, |
729 | sizeof(__u8), reg); |
730 | nft_offload_set_dependency(ctx, type: NFT_OFFLOAD_DEP_TRANSPORT); |
731 | break; |
732 | case NFT_META_IIF: |
733 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta, |
734 | ingress_ifindex, sizeof(__u32), reg); |
735 | break; |
736 | case NFT_META_IIFTYPE: |
737 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta, |
738 | ingress_iftype, sizeof(__u16), reg); |
739 | break; |
740 | default: |
741 | return -EOPNOTSUPP; |
742 | } |
743 | |
744 | return 0; |
745 | } |
746 | |
747 | bool nft_meta_get_reduce(struct nft_regs_track *track, |
748 | const struct nft_expr *expr) |
749 | { |
750 | const struct nft_meta *priv = nft_expr_priv(expr); |
751 | const struct nft_meta *meta; |
752 | |
753 | if (!nft_reg_track_cmp(track, expr, dreg: priv->dreg)) { |
754 | nft_reg_track_update(track, expr, dreg: priv->dreg, len: priv->len); |
755 | return false; |
756 | } |
757 | |
758 | meta = nft_expr_priv(expr: track->regs[priv->dreg].selector); |
759 | if (priv->key != meta->key || |
760 | priv->dreg != meta->dreg) { |
761 | nft_reg_track_update(track, expr, dreg: priv->dreg, len: priv->len); |
762 | return false; |
763 | } |
764 | |
765 | if (!track->regs[priv->dreg].bitwise) |
766 | return true; |
767 | |
768 | return nft_expr_reduce_bitwise(track, expr); |
769 | } |
770 | EXPORT_SYMBOL_GPL(nft_meta_get_reduce); |
771 | |
772 | static const struct nft_expr_ops nft_meta_get_ops = { |
773 | .type = &nft_meta_type, |
774 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), |
775 | .eval = nft_meta_get_eval, |
776 | .init = nft_meta_get_init, |
777 | .dump = nft_meta_get_dump, |
778 | .reduce = nft_meta_get_reduce, |
779 | .validate = nft_meta_get_validate, |
780 | .offload = nft_meta_get_offload, |
781 | }; |
782 | |
783 | static bool nft_meta_set_reduce(struct nft_regs_track *track, |
784 | const struct nft_expr *expr) |
785 | { |
786 | int i; |
787 | |
788 | for (i = 0; i < NFT_REG32_NUM; i++) { |
789 | if (!track->regs[i].selector) |
790 | continue; |
791 | |
792 | if (track->regs[i].selector->ops != &nft_meta_get_ops) |
793 | continue; |
794 | |
795 | __nft_reg_track_cancel(track, dreg: i); |
796 | } |
797 | |
798 | return false; |
799 | } |
800 | |
801 | static const struct nft_expr_ops nft_meta_set_ops = { |
802 | .type = &nft_meta_type, |
803 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), |
804 | .eval = nft_meta_set_eval, |
805 | .init = nft_meta_set_init, |
806 | .destroy = nft_meta_set_destroy, |
807 | .dump = nft_meta_set_dump, |
808 | .reduce = nft_meta_set_reduce, |
809 | .validate = nft_meta_set_validate, |
810 | }; |
811 | |
812 | static const struct nft_expr_ops * |
813 | nft_meta_select_ops(const struct nft_ctx *ctx, |
814 | const struct nlattr * const tb[]) |
815 | { |
816 | if (tb[NFTA_META_KEY] == NULL) |
817 | return ERR_PTR(error: -EINVAL); |
818 | |
819 | if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) |
820 | return ERR_PTR(error: -EINVAL); |
821 | |
822 | #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) && IS_MODULE(CONFIG_NFT_BRIDGE_META) |
823 | if (ctx->family == NFPROTO_BRIDGE) |
824 | return ERR_PTR(-EAGAIN); |
825 | #endif |
826 | if (tb[NFTA_META_DREG]) |
827 | return &nft_meta_get_ops; |
828 | |
829 | if (tb[NFTA_META_SREG]) |
830 | return &nft_meta_set_ops; |
831 | |
832 | return ERR_PTR(error: -EINVAL); |
833 | } |
834 | |
835 | static int nft_meta_inner_init(const struct nft_ctx *ctx, |
836 | const struct nft_expr *expr, |
837 | const struct nlattr * const tb[]) |
838 | { |
839 | struct nft_meta *priv = nft_expr_priv(expr); |
840 | unsigned int len; |
841 | |
842 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
843 | switch (priv->key) { |
844 | case NFT_META_PROTOCOL: |
845 | len = sizeof(u16); |
846 | break; |
847 | case NFT_META_L4PROTO: |
848 | len = sizeof(u32); |
849 | break; |
850 | default: |
851 | return -EOPNOTSUPP; |
852 | } |
853 | priv->len = len; |
854 | |
855 | return nft_parse_register_store(ctx, attr: tb[NFTA_META_DREG], dreg: &priv->dreg, |
856 | NULL, type: NFT_DATA_VALUE, len); |
857 | } |
858 | |
859 | void nft_meta_inner_eval(const struct nft_expr *expr, |
860 | struct nft_regs *regs, |
861 | const struct nft_pktinfo *pkt, |
862 | struct nft_inner_tun_ctx *tun_ctx) |
863 | { |
864 | const struct nft_meta *priv = nft_expr_priv(expr); |
865 | u32 *dest = ®s->data[priv->dreg]; |
866 | |
867 | switch (priv->key) { |
868 | case NFT_META_PROTOCOL: |
869 | nft_reg_store16(dreg: dest, val: (__force u16)tun_ctx->llproto); |
870 | break; |
871 | case NFT_META_L4PROTO: |
872 | if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) |
873 | goto err; |
874 | |
875 | nft_reg_store8(dreg: dest, val: tun_ctx->l4proto); |
876 | break; |
877 | default: |
878 | WARN_ON_ONCE(1); |
879 | goto err; |
880 | } |
881 | return; |
882 | |
883 | err: |
884 | regs->verdict.code = NFT_BREAK; |
885 | } |
886 | EXPORT_SYMBOL_GPL(nft_meta_inner_eval); |
887 | |
888 | static const struct nft_expr_ops nft_meta_inner_ops = { |
889 | .type = &nft_meta_type, |
890 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), |
891 | .init = nft_meta_inner_init, |
892 | .dump = nft_meta_get_dump, |
893 | /* direct call to nft_meta_inner_eval(). */ |
894 | }; |
895 | |
896 | struct nft_expr_type nft_meta_type __read_mostly = { |
897 | .name = "meta" , |
898 | .select_ops = nft_meta_select_ops, |
899 | .inner_ops = &nft_meta_inner_ops, |
900 | .policy = nft_meta_policy, |
901 | .maxattr = NFTA_META_MAX, |
902 | .owner = THIS_MODULE, |
903 | }; |
904 | |
905 | #ifdef CONFIG_NETWORK_SECMARK |
906 | struct nft_secmark { |
907 | u32 secid; |
908 | char *ctx; |
909 | }; |
910 | |
911 | static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = { |
912 | [NFTA_SECMARK_CTX] = { .type = NLA_STRING, .len = NFT_SECMARK_CTX_MAXLEN }, |
913 | }; |
914 | |
915 | static int nft_secmark_compute_secid(struct nft_secmark *priv) |
916 | { |
917 | u32 tmp_secid = 0; |
918 | int err; |
919 | |
920 | err = security_secctx_to_secid(secdata: priv->ctx, strlen(priv->ctx), secid: &tmp_secid); |
921 | if (err) |
922 | return err; |
923 | |
924 | if (!tmp_secid) |
925 | return -ENOENT; |
926 | |
927 | err = security_secmark_relabel_packet(secid: tmp_secid); |
928 | if (err) |
929 | return err; |
930 | |
931 | priv->secid = tmp_secid; |
932 | return 0; |
933 | } |
934 | |
935 | static void nft_secmark_obj_eval(struct nft_object *obj, struct nft_regs *regs, |
936 | const struct nft_pktinfo *pkt) |
937 | { |
938 | const struct nft_secmark *priv = nft_obj_data(obj); |
939 | struct sk_buff *skb = pkt->skb; |
940 | |
941 | skb->secmark = priv->secid; |
942 | } |
943 | |
944 | static int nft_secmark_obj_init(const struct nft_ctx *ctx, |
945 | const struct nlattr * const tb[], |
946 | struct nft_object *obj) |
947 | { |
948 | struct nft_secmark *priv = nft_obj_data(obj); |
949 | int err; |
950 | |
951 | if (tb[NFTA_SECMARK_CTX] == NULL) |
952 | return -EINVAL; |
953 | |
954 | priv->ctx = nla_strdup(nla: tb[NFTA_SECMARK_CTX], GFP_KERNEL); |
955 | if (!priv->ctx) |
956 | return -ENOMEM; |
957 | |
958 | err = nft_secmark_compute_secid(priv); |
959 | if (err) { |
960 | kfree(objp: priv->ctx); |
961 | return err; |
962 | } |
963 | |
964 | security_secmark_refcount_inc(); |
965 | |
966 | return 0; |
967 | } |
968 | |
969 | static int nft_secmark_obj_dump(struct sk_buff *skb, struct nft_object *obj, |
970 | bool reset) |
971 | { |
972 | struct nft_secmark *priv = nft_obj_data(obj); |
973 | int err; |
974 | |
975 | if (nla_put_string(skb, attrtype: NFTA_SECMARK_CTX, str: priv->ctx)) |
976 | return -1; |
977 | |
978 | if (reset) { |
979 | err = nft_secmark_compute_secid(priv); |
980 | if (err) |
981 | return err; |
982 | } |
983 | |
984 | return 0; |
985 | } |
986 | |
987 | static void nft_secmark_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj) |
988 | { |
989 | struct nft_secmark *priv = nft_obj_data(obj); |
990 | |
991 | security_secmark_refcount_dec(); |
992 | |
993 | kfree(objp: priv->ctx); |
994 | } |
995 | |
996 | static const struct nft_object_ops nft_secmark_obj_ops = { |
997 | .type = &nft_secmark_obj_type, |
998 | .size = sizeof(struct nft_secmark), |
999 | .init = nft_secmark_obj_init, |
1000 | .eval = nft_secmark_obj_eval, |
1001 | .dump = nft_secmark_obj_dump, |
1002 | .destroy = nft_secmark_obj_destroy, |
1003 | }; |
1004 | struct nft_object_type nft_secmark_obj_type __read_mostly = { |
1005 | .type = NFT_OBJECT_SECMARK, |
1006 | .ops = &nft_secmark_obj_ops, |
1007 | .maxattr = NFTA_SECMARK_MAX, |
1008 | .policy = nft_secmark_policy, |
1009 | .owner = THIS_MODULE, |
1010 | }; |
1011 | #endif /* CONFIG_NETWORK_SECMARK */ |
1012 | |