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