1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * (C) 2007 by Sebastian Claßen <sebastian.classen@freenet.ag> |
4 | * (C) 2007-2010 by Jan Engelhardt <jengelh@medozas.de> |
5 | * |
6 | * Extracted from xt_TEE.c |
7 | */ |
8 | #include <linux/module.h> |
9 | #include <linux/percpu.h> |
10 | #include <linux/skbuff.h> |
11 | #include <linux/netfilter.h> |
12 | #include <net/ipv6.h> |
13 | #include <net/ip6_route.h> |
14 | #include <net/netfilter/ipv6/nf_dup_ipv6.h> |
15 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
16 | #include <net/netfilter/nf_conntrack.h> |
17 | #endif |
18 | |
19 | static bool nf_dup_ipv6_route(struct net *net, struct sk_buff *skb, |
20 | const struct in6_addr *gw, int oif) |
21 | { |
22 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
23 | struct dst_entry *dst; |
24 | struct flowi6 fl6; |
25 | |
26 | memset(&fl6, 0, sizeof(fl6)); |
27 | if (oif != -1) |
28 | fl6.flowi6_oif = oif; |
29 | |
30 | fl6.daddr = *gw; |
31 | fl6.flowlabel = (__force __be32)(((iph->flow_lbl[0] & 0xF) << 16) | |
32 | (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]); |
33 | fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; |
34 | dst = ip6_route_output(net, NULL, fl6: &fl6); |
35 | if (dst->error) { |
36 | dst_release(dst); |
37 | return false; |
38 | } |
39 | skb_dst_drop(skb); |
40 | skb_dst_set(skb, dst); |
41 | skb->dev = dst->dev; |
42 | skb->protocol = htons(ETH_P_IPV6); |
43 | |
44 | return true; |
45 | } |
46 | |
47 | void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, |
48 | const struct in6_addr *gw, int oif) |
49 | { |
50 | if (this_cpu_read(nf_skb_duplicated)) |
51 | return; |
52 | skb = pskb_copy(skb, GFP_ATOMIC); |
53 | if (skb == NULL) |
54 | return; |
55 | |
56 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
57 | nf_reset_ct(skb); |
58 | nf_ct_set(skb, NULL, info: IP_CT_UNTRACKED); |
59 | #endif |
60 | if (hooknum == NF_INET_PRE_ROUTING || |
61 | hooknum == NF_INET_LOCAL_IN) { |
62 | struct ipv6hdr *iph = ipv6_hdr(skb); |
63 | --iph->hop_limit; |
64 | } |
65 | if (nf_dup_ipv6_route(net, skb, gw, oif)) { |
66 | __this_cpu_write(nf_skb_duplicated, true); |
67 | ip6_local_out(net, sk: skb->sk, skb); |
68 | __this_cpu_write(nf_skb_duplicated, false); |
69 | } else { |
70 | kfree_skb(skb); |
71 | } |
72 | } |
73 | EXPORT_SYMBOL_GPL(nf_dup_ipv6); |
74 | |
75 | MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>" ); |
76 | MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>" ); |
77 | MODULE_DESCRIPTION("nf_dup_ipv6: IPv6 packet duplication" ); |
78 | MODULE_LICENSE("GPL" ); |
79 | |