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