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