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
23static 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, &fl4);
39	if (IS_ERR(rt))
40		return false;
41
42	skb_dst_drop(skb);
43	skb_dst_set(skb, &rt->dst);
44	skb->dev      = rt->dst.dev;
45	skb->protocol = htons(ETH_P_IP);
46
47	return true;
48}
49
50void 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, 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, skb->sk, skb);
89		__this_cpu_write(nf_skb_duplicated, false);
90	} else {
91		kfree_skb(skb);
92	}
93}
94EXPORT_SYMBOL_GPL(nf_dup_ipv4);
95
96MODULE_AUTHOR("Sebastian Cla��en <sebastian.classen@freenet.ag>");
97MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
98MODULE_DESCRIPTION("nf_dup_ipv4: Duplicate IPv4 packet");
99MODULE_LICENSE("GPL");
100