]>
Commit | Line | Data |
---|---|---|
e281b198 JE |
1 | /* |
2 | * "TEE" target extension for Xtables | |
3 | * Copyright © Sebastian Claßen, 2007 | |
4 | * Jan Engelhardt, 2007-2010 | |
5 | * | |
6 | * based on ipt_ROUTE.c from Cédric de Launois | |
7 | * <delaunois@info.ucl.be> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * version 2 or later, as published by the Free Software Foundation. | |
12 | */ | |
13 | #include <linux/ip.h> | |
14 | #include <linux/module.h> | |
cd58bcd9 | 15 | #include <linux/percpu.h> |
e281b198 JE |
16 | #include <linux/route.h> |
17 | #include <linux/skbuff.h> | |
18 | #include <net/checksum.h> | |
19 | #include <net/icmp.h> | |
20 | #include <net/ip.h> | |
21 | #include <net/ipv6.h> | |
22 | #include <net/ip6_route.h> | |
23 | #include <net/route.h> | |
24 | #include <linux/netfilter/x_tables.h> | |
25 | #include <linux/netfilter/xt_TEE.h> | |
26 | ||
27 | #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) | |
28 | # define WITH_CONNTRACK 1 | |
29 | # include <net/netfilter/nf_conntrack.h> | |
30 | #endif | |
31 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
32 | # define WITH_IPV6 1 | |
33 | #endif | |
34 | ||
35 | static const union nf_inet_addr tee_zero_address; | |
cd58bcd9 | 36 | static DEFINE_PER_CPU(bool, tee_active); |
e281b198 JE |
37 | |
38 | static struct net *pick_net(struct sk_buff *skb) | |
39 | { | |
40 | #ifdef CONFIG_NET_NS | |
41 | const struct dst_entry *dst; | |
42 | ||
43 | if (skb->dev != NULL) | |
44 | return dev_net(skb->dev); | |
45 | dst = skb_dst(skb); | |
46 | if (dst != NULL && dst->dev != NULL) | |
47 | return dev_net(dst->dev); | |
48 | #endif | |
49 | return &init_net; | |
50 | } | |
51 | ||
52 | static bool tee_tg_route_oif(struct flowi *f, struct net *net, | |
53 | const struct xt_tee_tginfo *info) | |
54 | { | |
55 | const struct net_device *dev; | |
56 | ||
57 | if (*info->oif != '\0') | |
58 | return true; | |
59 | dev = dev_get_by_name(net, info->oif); | |
60 | if (dev == NULL) | |
61 | return false; | |
62 | f->oif = dev->ifindex; | |
63 | return true; | |
64 | } | |
65 | ||
66 | static bool | |
67 | tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) | |
68 | { | |
69 | const struct iphdr *iph = ip_hdr(skb); | |
70 | struct net *net = pick_net(skb); | |
71 | struct rtable *rt; | |
72 | struct flowi fl; | |
73 | ||
74 | memset(&fl, 0, sizeof(fl)); | |
75 | if (!tee_tg_route_oif(&fl, net, info)) | |
76 | return false; | |
77 | fl.nl_u.ip4_u.daddr = info->gw.ip; | |
78 | fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); | |
79 | fl.nl_u.ip4_u.scope = RT_SCOPE_UNIVERSE; | |
80 | if (ip_route_output_key(net, &rt, &fl) != 0) | |
81 | return false; | |
82 | ||
83 | dst_release(skb_dst(skb)); | |
84 | skb_dst_set(skb, &rt->u.dst); | |
85 | skb->dev = rt->u.dst.dev; | |
86 | skb->protocol = htons(ETH_P_IP); | |
87 | return true; | |
88 | } | |
89 | ||
90 | static unsigned int | |
91 | tee_tg4(struct sk_buff *skb, const struct xt_target_param *par) | |
92 | { | |
93 | const struct xt_tee_tginfo *info = par->targinfo; | |
94 | struct iphdr *iph; | |
95 | ||
cd58bcd9 JE |
96 | if (percpu_read(tee_active)) |
97 | return XT_CONTINUE; | |
e281b198 JE |
98 | /* |
99 | * Copy the skb, and route the copy. Will later return %XT_CONTINUE for | |
100 | * the original skb, which should continue on its way as if nothing has | |
101 | * happened. The copy should be independently delivered to the TEE | |
102 | * --gateway. | |
103 | */ | |
104 | skb = pskb_copy(skb, GFP_ATOMIC); | |
105 | if (skb == NULL) | |
106 | return XT_CONTINUE; | |
107 | ||
108 | #ifdef WITH_CONNTRACK | |
109 | /* Avoid counting cloned packets towards the original connection. */ | |
110 | nf_conntrack_put(skb->nfct); | |
111 | skb->nfct = &nf_conntrack_untracked.ct_general; | |
112 | skb->nfctinfo = IP_CT_NEW; | |
113 | nf_conntrack_get(skb->nfct); | |
114 | #endif | |
115 | /* | |
116 | * If we are in PREROUTING/INPUT, the checksum must be recalculated | |
117 | * since the length could have changed as a result of defragmentation. | |
118 | * | |
119 | * We also decrease the TTL to mitigate potential TEE loops | |
120 | * between two hosts. | |
121 | * | |
122 | * Set %IP_DF so that the original source is notified of a potentially | |
123 | * decreased MTU on the clone route. IPv6 does this too. | |
124 | */ | |
125 | iph = ip_hdr(skb); | |
126 | iph->frag_off |= htons(IP_DF); | |
127 | if (par->hooknum == NF_INET_PRE_ROUTING || | |
128 | par->hooknum == NF_INET_LOCAL_IN) | |
129 | --iph->ttl; | |
130 | ip_send_check(iph); | |
131 | ||
cd58bcd9 JE |
132 | if (tee_tg_route4(skb, info)) { |
133 | percpu_write(tee_active, true); | |
134 | ip_local_out(skb); | |
135 | percpu_write(tee_active, false); | |
136 | } else { | |
e281b198 | 137 | kfree_skb(skb); |
cd58bcd9 | 138 | } |
e281b198 JE |
139 | return XT_CONTINUE; |
140 | } | |
141 | ||
142 | #ifdef WITH_IPV6 | |
143 | static bool | |
144 | tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info) | |
145 | { | |
146 | const struct ipv6hdr *iph = ipv6_hdr(skb); | |
147 | struct net *net = pick_net(skb); | |
148 | struct dst_entry *dst; | |
149 | struct flowi fl; | |
150 | ||
151 | memset(&fl, 0, sizeof(fl)); | |
152 | if (!tee_tg_route_oif(&fl, net, info)) | |
153 | return false; | |
154 | fl.nl_u.ip6_u.daddr = info->gw.in6; | |
155 | fl.nl_u.ip6_u.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) | | |
156 | (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]; | |
157 | dst = ip6_route_output(net, NULL, &fl); | |
158 | if (dst == NULL) | |
159 | return false; | |
160 | ||
161 | dst_release(skb_dst(skb)); | |
162 | skb_dst_set(skb, dst); | |
163 | skb->dev = dst->dev; | |
164 | skb->protocol = htons(ETH_P_IPV6); | |
165 | return true; | |
166 | } | |
167 | ||
168 | static unsigned int | |
169 | tee_tg6(struct sk_buff *skb, const struct xt_target_param *par) | |
170 | { | |
171 | const struct xt_tee_tginfo *info = par->targinfo; | |
172 | ||
cd58bcd9 JE |
173 | if (percpu_read(tee_active)) |
174 | return XT_CONTINUE; | |
e281b198 JE |
175 | skb = pskb_copy(skb, GFP_ATOMIC); |
176 | if (skb == NULL) | |
177 | return XT_CONTINUE; | |
178 | ||
179 | #ifdef WITH_CONNTRACK | |
180 | nf_conntrack_put(skb->nfct); | |
181 | skb->nfct = &nf_conntrack_untracked.ct_general; | |
182 | skb->nfctinfo = IP_CT_NEW; | |
183 | nf_conntrack_get(skb->nfct); | |
184 | #endif | |
185 | if (par->hooknum == NF_INET_PRE_ROUTING || | |
186 | par->hooknum == NF_INET_LOCAL_IN) { | |
187 | struct ipv6hdr *iph = ipv6_hdr(skb); | |
188 | --iph->hop_limit; | |
189 | } | |
cd58bcd9 JE |
190 | if (tee_tg_route6(skb, info)) { |
191 | percpu_write(tee_active, true); | |
192 | ip6_local_out(skb); | |
193 | percpu_write(tee_active, false); | |
194 | } else { | |
e281b198 | 195 | kfree_skb(skb); |
cd58bcd9 | 196 | } |
e281b198 JE |
197 | return XT_CONTINUE; |
198 | } | |
199 | #endif /* WITH_IPV6 */ | |
200 | ||
201 | static int tee_tg_check(const struct xt_tgchk_param *par) | |
202 | { | |
203 | const struct xt_tee_tginfo *info = par->targinfo; | |
204 | ||
205 | if (info->oif[sizeof(info->oif)-1] != '\0') | |
206 | return -EINVAL; | |
207 | /* 0.0.0.0 and :: not allowed */ | |
208 | return (memcmp(&info->gw, &tee_zero_address, | |
209 | sizeof(tee_zero_address)) == 0) ? -EINVAL : 0; | |
210 | } | |
211 | ||
212 | static struct xt_target tee_tg_reg[] __read_mostly = { | |
213 | { | |
214 | .name = "TEE", | |
215 | .revision = 1, | |
216 | .family = NFPROTO_IPV4, | |
217 | .target = tee_tg4, | |
218 | .targetsize = sizeof(struct xt_tee_tginfo), | |
219 | .checkentry = tee_tg_check, | |
220 | .me = THIS_MODULE, | |
221 | }, | |
222 | #ifdef WITH_IPV6 | |
223 | { | |
224 | .name = "TEE", | |
225 | .revision = 1, | |
226 | .family = NFPROTO_IPV6, | |
227 | .target = tee_tg6, | |
228 | .targetsize = sizeof(struct xt_tee_tginfo), | |
229 | .checkentry = tee_tg_check, | |
230 | .me = THIS_MODULE, | |
231 | }, | |
232 | #endif | |
233 | }; | |
234 | ||
235 | static int __init tee_tg_init(void) | |
236 | { | |
237 | return xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); | |
238 | } | |
239 | ||
240 | static void __exit tee_tg_exit(void) | |
241 | { | |
242 | xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); | |
243 | } | |
244 | ||
245 | module_init(tee_tg_init); | |
246 | module_exit(tee_tg_exit); | |
247 | MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>"); | |
248 | MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); | |
249 | MODULE_DESCRIPTION("Xtables: Reroute packet copy"); | |
250 | MODULE_LICENSE("GPL"); | |
251 | MODULE_ALIAS("ipt_TEE"); | |
252 | MODULE_ALIAS("ip6t_TEE"); |