2 * "TEE" target extension for Xtables
3 * Copyright © Sebastian Claßen, 2007
4 * Jan Engelhardt, 2007-2010
6 * based on ipt_ROUTE.c from Cédric de Launois
7 * <delaunois@info.ucl.be>
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.
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>
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>
27 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
28 # define WITH_CONNTRACK 1
29 # include <net/netfilter/nf_conntrack.h>
31 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
35 static const union nf_inet_addr tee_zero_address
;
36 static DEFINE_PER_CPU(bool, tee_active
);
38 static struct net
*pick_net(struct sk_buff
*skb
)
41 const struct dst_entry
*dst
;
44 return dev_net(skb
->dev
);
46 if (dst
!= NULL
&& dst
->dev
!= NULL
)
47 return dev_net(dst
->dev
);
52 static bool tee_tg_route_oif(struct flowi
*f
, struct net
*net
,
53 const struct xt_tee_tginfo
*info
)
55 const struct net_device
*dev
;
57 if (*info
->oif
!= '\0')
59 dev
= dev_get_by_name(net
, info
->oif
);
62 f
->oif
= dev
->ifindex
;
67 tee_tg_route4(struct sk_buff
*skb
, const struct xt_tee_tginfo
*info
)
69 const struct iphdr
*iph
= ip_hdr(skb
);
70 struct net
*net
= pick_net(skb
);
74 memset(&fl
, 0, sizeof(fl
));
75 if (!tee_tg_route_oif(&fl
, net
, info
))
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)
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
);
91 tee_tg4(struct sk_buff
*skb
, const struct xt_target_param
*par
)
93 const struct xt_tee_tginfo
*info
= par
->targinfo
;
96 if (percpu_read(tee_active
))
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
104 skb
= pskb_copy(skb
, GFP_ATOMIC
);
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
);
116 * If we are in PREROUTING/INPUT, the checksum must be recalculated
117 * since the length could have changed as a result of defragmentation.
119 * We also decrease the TTL to mitigate potential TEE loops
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.
126 iph
->frag_off
|= htons(IP_DF
);
127 if (par
->hooknum
== NF_INET_PRE_ROUTING
||
128 par
->hooknum
== NF_INET_LOCAL_IN
)
132 if (tee_tg_route4(skb
, info
)) {
133 percpu_write(tee_active
, true);
135 percpu_write(tee_active
, false);
144 tee_tg_route6(struct sk_buff
*skb
, const struct xt_tee_tginfo
*info
)
146 const struct ipv6hdr
*iph
= ipv6_hdr(skb
);
147 struct net
*net
= pick_net(skb
);
148 struct dst_entry
*dst
;
151 memset(&fl
, 0, sizeof(fl
));
152 if (!tee_tg_route_oif(&fl
, net
, info
))
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
);
161 dst_release(skb_dst(skb
));
162 skb_dst_set(skb
, dst
);
164 skb
->protocol
= htons(ETH_P_IPV6
);
169 tee_tg6(struct sk_buff
*skb
, const struct xt_target_param
*par
)
171 const struct xt_tee_tginfo
*info
= par
->targinfo
;
173 if (percpu_read(tee_active
))
175 skb
= pskb_copy(skb
, GFP_ATOMIC
);
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
);
185 if (par
->hooknum
== NF_INET_PRE_ROUTING
||
186 par
->hooknum
== NF_INET_LOCAL_IN
) {
187 struct ipv6hdr
*iph
= ipv6_hdr(skb
);
190 if (tee_tg_route6(skb
, info
)) {
191 percpu_write(tee_active
, true);
193 percpu_write(tee_active
, false);
199 #endif /* WITH_IPV6 */
201 static int tee_tg_check(const struct xt_tgchk_param
*par
)
203 const struct xt_tee_tginfo
*info
= par
->targinfo
;
205 if (info
->oif
[sizeof(info
->oif
)-1] != '\0')
207 /* 0.0.0.0 and :: not allowed */
208 return (memcmp(&info
->gw
, &tee_zero_address
,
209 sizeof(tee_zero_address
)) == 0) ? -EINVAL
: 0;
212 static struct xt_target tee_tg_reg
[] __read_mostly
= {
216 .family
= NFPROTO_IPV4
,
218 .targetsize
= sizeof(struct xt_tee_tginfo
),
219 .checkentry
= tee_tg_check
,
226 .family
= NFPROTO_IPV6
,
228 .targetsize
= sizeof(struct xt_tee_tginfo
),
229 .checkentry
= tee_tg_check
,
235 static int __init
tee_tg_init(void)
237 return xt_register_targets(tee_tg_reg
, ARRAY_SIZE(tee_tg_reg
));
240 static void __exit
tee_tg_exit(void)
242 xt_unregister_targets(tee_tg_reg
, ARRAY_SIZE(tee_tg_reg
));
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");