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/ip.h> |
9 | #include <linux/module.h> |
10 | #include <linux/percpu.h> |
11 | #include <linux/route.h> |
12 | #include <linux/skbuff.h> |
13 | #include <linux/netfilter.h> |
14 | #include <net/checksum.h> |
15 | #include <net/icmp.h> |
16 | #include <net/ip.h> |
17 | #include <net/route.h> |
18 | #include <net/netfilter/ipv4/nf_dup_ipv4.h> |
19 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
20 | #include <net/netfilter/nf_conntrack.h> |
21 | #endif |
22 | |
23 | static bool nf_dup_ipv4_route(struct net *net, struct sk_buff *skb, |
24 | const struct in_addr *gw, int oif) |
25 | { |
26 | const struct iphdr *iph = ip_hdr(skb); |
27 | struct rtable *rt; |
28 | struct flowi4 fl4; |
29 | |
30 | memset(&fl4, 0, sizeof(fl4)); |
31 | if (oif != -1) |
32 | fl4.flowi4_oif = oif; |
33 | |
34 | fl4.daddr = gw->s_addr; |
35 | fl4.flowi4_tos = RT_TOS(iph->tos); |
36 | fl4.flowi4_scope = RT_SCOPE_UNIVERSE; |
37 | fl4.flowi4_flags = FLOWI_FLAG_KNOWN_NH; |
38 | rt = ip_route_output_key(net, flp: &fl4); |
39 | if (IS_ERR(ptr: rt)) |
40 | return false; |
41 | |
42 | skb_dst_drop(skb); |
43 | skb_dst_set(skb, dst: &rt->dst); |
44 | skb->dev = rt->dst.dev; |
45 | skb->protocol = htons(ETH_P_IP); |
46 | |
47 | return true; |
48 | } |
49 | |
50 | void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum, |
51 | const struct in_addr *gw, int oif) |
52 | { |
53 | struct iphdr *iph; |
54 | |
55 | if (this_cpu_read(nf_skb_duplicated)) |
56 | return; |
57 | /* |
58 | * Copy the skb, and route the copy. Will later return %XT_CONTINUE for |
59 | * the original skb, which should continue on its way as if nothing has |
60 | * happened. The copy should be independently delivered to the gateway. |
61 | */ |
62 | skb = pskb_copy(skb, GFP_ATOMIC); |
63 | if (skb == NULL) |
64 | return; |
65 | |
66 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
67 | /* Avoid counting cloned packets towards the original connection. */ |
68 | nf_reset_ct(skb); |
69 | nf_ct_set(skb, NULL, info: IP_CT_UNTRACKED); |
70 | #endif |
71 | /* |
72 | * If we are in PREROUTING/INPUT, decrease the TTL to mitigate potential |
73 | * loops between two hosts. |
74 | * |
75 | * Set %IP_DF so that the original source is notified of a potentially |
76 | * decreased MTU on the clone route. IPv6 does this too. |
77 | * |
78 | * IP header checksum will be recalculated at ip_local_out. |
79 | */ |
80 | iph = ip_hdr(skb); |
81 | iph->frag_off |= htons(IP_DF); |
82 | if (hooknum == NF_INET_PRE_ROUTING || |
83 | hooknum == NF_INET_LOCAL_IN) |
84 | --iph->ttl; |
85 | |
86 | if (nf_dup_ipv4_route(net, skb, gw, oif)) { |
87 | __this_cpu_write(nf_skb_duplicated, true); |
88 | ip_local_out(net, sk: skb->sk, skb); |
89 | __this_cpu_write(nf_skb_duplicated, false); |
90 | } else { |
91 | kfree_skb(skb); |
92 | } |
93 | } |
94 | EXPORT_SYMBOL_GPL(nf_dup_ipv4); |
95 | |
96 | MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>" ); |
97 | MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>" ); |
98 | MODULE_DESCRIPTION("nf_dup_ipv4: Duplicate IPv4 packet" ); |
99 | MODULE_LICENSE("GPL" ); |
100 | |