Commit | Line | Data |
---|---|---|
bbde9fc1 PNA |
1 | /* |
2 | * (C) 2007 by Sebastian Claßen <sebastian.classen@freenet.ag> | |
3 | * (C) 2007-2010 by Jan Engelhardt <jengelh@medozas.de> | |
4 | * | |
5 | * Extracted from xt_TEE.c | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 or later, as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include <linux/module.h> | |
12 | #include <linux/percpu.h> | |
13 | #include <linux/skbuff.h> | |
a82b0e63 | 14 | #include <linux/netfilter.h> |
bbde9fc1 PNA |
15 | #include <net/ipv6.h> |
16 | #include <net/ip6_route.h> | |
17 | #include <net/netfilter/ipv6/nf_dup_ipv6.h> | |
18 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) | |
19 | #include <net/netfilter/nf_conntrack.h> | |
20 | #endif | |
21 | ||
206e8c00 EB |
22 | static bool nf_dup_ipv6_route(struct net *net, struct sk_buff *skb, |
23 | const struct in6_addr *gw, int oif) | |
bbde9fc1 PNA |
24 | { |
25 | const struct ipv6hdr *iph = ipv6_hdr(skb); | |
bbde9fc1 PNA |
26 | struct dst_entry *dst; |
27 | struct flowi6 fl6; | |
28 | ||
29 | memset(&fl6, 0, sizeof(fl6)); | |
30 | if (oif != -1) | |
31 | fl6.flowi6_oif = oif; | |
32 | ||
33 | fl6.daddr = *gw; | |
59e26423 PNA |
34 | fl6.flowlabel = (__force __be32)(((iph->flow_lbl[0] & 0xF) << 16) | |
35 | (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]); | |
bbde9fc1 PNA |
36 | dst = ip6_route_output(net, NULL, &fl6); |
37 | if (dst->error) { | |
38 | dst_release(dst); | |
39 | return false; | |
40 | } | |
41 | skb_dst_drop(skb); | |
42 | skb_dst_set(skb, dst); | |
43 | skb->dev = dst->dev; | |
44 | skb->protocol = htons(ETH_P_IPV6); | |
45 | ||
46 | return true; | |
47 | } | |
48 | ||
206e8c00 | 49 | void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, |
bbde9fc1 PNA |
50 | const struct in6_addr *gw, int oif) |
51 | { | |
d877f071 | 52 | if (this_cpu_read(nf_skb_duplicated)) |
bbde9fc1 PNA |
53 | return; |
54 | skb = pskb_copy(skb, GFP_ATOMIC); | |
55 | if (skb == NULL) | |
56 | return; | |
57 | ||
58 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) | |
59 | nf_conntrack_put(skb->nfct); | |
60 | skb->nfct = &nf_ct_untracked_get()->ct_general; | |
61 | skb->nfctinfo = IP_CT_NEW; | |
62 | nf_conntrack_get(skb->nfct); | |
63 | #endif | |
64 | if (hooknum == NF_INET_PRE_ROUTING || | |
65 | hooknum == NF_INET_LOCAL_IN) { | |
66 | struct ipv6hdr *iph = ipv6_hdr(skb); | |
67 | --iph->hop_limit; | |
68 | } | |
206e8c00 | 69 | if (nf_dup_ipv6_route(net, skb, gw, oif)) { |
bbde9fc1 | 70 | __this_cpu_write(nf_skb_duplicated, true); |
79288330 | 71 | ip6_local_out(skb->sk, skb); |
bbde9fc1 PNA |
72 | __this_cpu_write(nf_skb_duplicated, false); |
73 | } else { | |
74 | kfree_skb(skb); | |
75 | } | |
76 | } | |
77 | EXPORT_SYMBOL_GPL(nf_dup_ipv6); | |
78 | ||
79 | MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>"); | |
80 | MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); | |
81 | MODULE_DESCRIPTION("nf_dup_ipv6: IPv6 packet duplication"); | |
82 | MODULE_LICENSE("GPL"); |