]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/netfilter/xt_TEE.c
netfilter: xtables: remove old comments about reentrancy
[net-next-2.6.git] / net / netfilter / xt_TEE.c
CommitLineData
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
35static const union nf_inet_addr tee_zero_address;
cd58bcd9 36static DEFINE_PER_CPU(bool, tee_active);
e281b198
JE
37
38static 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
52static 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
66static bool
67tee_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
90static unsigned int
91tee_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
143static bool
144tee_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
168static unsigned int
169tee_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
201static 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
212static 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
235static int __init tee_tg_init(void)
236{
237 return xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
238}
239
240static void __exit tee_tg_exit(void)
241{
242 xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
243}
244
245module_init(tee_tg_init);
246module_exit(tee_tg_exit);
247MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>");
248MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
249MODULE_DESCRIPTION("Xtables: Reroute packet copy");
250MODULE_LICENSE("GPL");
251MODULE_ALIAS("ipt_TEE");
252MODULE_ALIAS("ip6t_TEE");