netfilter: xt_TEE: have cloned packet travel through Xtables too
[deliverable/linux.git] / net / netfilter / xt_TEE.c
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>
15 #include <linux/percpu.h>
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;
36 static DEFINE_PER_CPU(bool, tee_active);
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
96 if (percpu_read(tee_active))
97 return XT_CONTINUE;
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
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 {
137 kfree_skb(skb);
138 }
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
173 if (percpu_read(tee_active))
174 return XT_CONTINUE;
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 }
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 {
195 kfree_skb(skb);
196 }
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");
This page took 0.0385 seconds and 6 git commands to generate.