Merge branch 'TEE6'

This commit is contained in:
Jan Engelhardt
2009-01-10 14:03:04 +01:00
2 changed files with 177 additions and 42 deletions

View File

@@ -1,7 +1,7 @@
/* /*
* "TEE" target extension for iptables * "TEE" target extension for iptables
* Copyright © Sebastian Claßen <sebastian.classen [at] freenet.ag>, 2007 * Copyright © Sebastian Claßen <sebastian.classen [at] freenet.ag>, 2007
* Jan Engelhardt <jengelh [at] medozas de>, 2007 - 2008 * Jan Engelhardt <jengelh [at] medozas de>, 2007 - 2009
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License; either * modify it under the terms of the GNU General Public License; either
@@ -70,6 +70,35 @@ static int tee_tg_parse(int c, char **argv, int invert, unsigned int *flags,
return false; return false;
} }
static int tee_tg6_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_tee_tginfo *info = (void *)(*target)->data;
const struct in6_addr *ia;
switch (c) {
case 'g':
if (*flags & FLAG_GATEWAY)
exit_error(PARAMETER_PROBLEM,
"Cannot specify --gw more than once");
if (check_inverse(optarg, &invert, NULL, 0))
exit_error(PARAMETER_PROBLEM,
"Unexpected \"!\" after --gateway");
ia = numeric_to_ip6addr(optarg);
if (ia == NULL)
exit_error(PARAMETER_PROBLEM,
"Invalid IP address %s", optarg);
memcpy(&info->gw, ia, sizeof(*ia));
*flags |= FLAG_GATEWAY;
return true;
}
return false;
}
static void tee_tg_check(unsigned int flags) static void tee_tg_check(unsigned int flags)
{ {
if (flags == 0) if (flags == 0)
@@ -88,6 +117,17 @@ static void tee_tg_print(const void *ip, const struct xt_entry_target *target,
printf("TEE gw:%s ", ipaddr_to_anyname(&info->gw.in)); printf("TEE gw:%s ", ipaddr_to_anyname(&info->gw.in));
} }
static void tee_tg6_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
const struct xt_tee_tginfo *info = (const void *)target->data;
if (numeric)
printf("TEE gw:%s ", ip6addr_to_numeric(&info->gw.in6));
else
printf("TEE gw:%s ", ip6addr_to_anyname(&info->gw.in6));
}
static void tee_tg_save(const void *ip, const struct xt_entry_target *target) static void tee_tg_save(const void *ip, const struct xt_entry_target *target)
{ {
const struct xt_tee_tginfo *info = (const void *)target->data; const struct xt_tee_tginfo *info = (const void *)target->data;
@@ -95,6 +135,13 @@ static void tee_tg_save(const void *ip, const struct xt_entry_target *target)
printf("--gateway %s ", ipaddr_to_numeric(&info->gw.in)); printf("--gateway %s ", ipaddr_to_numeric(&info->gw.in));
} }
static void tee_tg6_save(const void *ip, const struct xt_entry_target *target)
{
const struct xt_tee_tginfo *info = (const void *)target->data;
printf("--gateway %s ", ip6addr_to_numeric(&info->gw.in6));
}
static struct xtables_target tee_tg_reg = { static struct xtables_target tee_tg_reg = {
.name = "TEE", .name = "TEE",
.version = XTABLES_VERSION, .version = XTABLES_VERSION,
@@ -110,7 +157,23 @@ static struct xtables_target tee_tg_reg = {
.extra_opts = tee_tg_opts, .extra_opts = tee_tg_opts,
}; };
static struct xtables_target tee_tg6_reg = {
.name = "TEE",
.version = XTABLES_VERSION,
.revision = 0,
.family = PF_INET6,
.size = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
.help = tee_tg_help,
.parse = tee_tg6_parse,
.final_check = tee_tg_check,
.print = tee_tg6_print,
.save = tee_tg6_save,
.extra_opts = tee_tg_opts,
};
static __attribute__((constructor)) void tee_tg_ldr(void) static __attribute__((constructor)) void tee_tg_ldr(void)
{ {
xtables_register_target(&tee_tg_reg); xtables_register_target(&tee_tg_reg);
xtables_register_target(&tee_tg6_reg);
} }

View File

@@ -1,7 +1,7 @@
/* /*
* "TEE" target extension for Xtables * "TEE" target extension for Xtables
* Copyright © Sebastian Claßen <sebastian.classen [at] freenet de>, 2007 * Copyright © Sebastian Claßen <sebastian.classen [at] freenet de>, 2007
* Jan Engelhardt <jengelh [at] medozas de>, 2007 * Jan Engelhardt <jengelh [at] medozas de>, 2007 - 2008
* *
* based on ipt_ROUTE.c from Cédric de Launois * based on ipt_ROUTE.c from Cédric de Launois
* <delaunois [at] info ucl ac be> * <delaunois [at] info ucl ac be>
@@ -17,6 +17,7 @@
#include <net/checksum.h> #include <net/checksum.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ip6_route.h>
#include <net/route.h> #include <net/route.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
@@ -29,7 +30,7 @@ static struct nf_conn tee_track;
#include "compat_xtables.h" #include "compat_xtables.h"
#include "xt_TEE.h" #include "xt_TEE.h"
static const union nf_inet_addr zero_address; static const union nf_inet_addr tee_zero_address;
/* /*
* Try to route the packet according to the routing keys specified in * Try to route the packet according to the routing keys specified in
@@ -47,21 +48,16 @@ static const union nf_inet_addr zero_address;
* true - if the packet was succesfully routed to the * true - if the packet was succesfully routed to the
* destination desired * destination desired
*/ */
static bool tee_routing(struct sk_buff *skb, static bool
const struct xt_tee_tginfo *info) tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info)
{ {
int err; int err;
struct rtable *rt; struct rtable *rt;
struct iphdr *iph = ip_hdr(skb); struct flowi fl;
struct flowi fl = {
.nl_u = { memset(&fl, 0, sizeof(fl));
.ip4_u = { fl.nl_u.ip4_u.daddr = info->gw.ip;
.daddr = info->gw.ip, fl.nl_u.ip4_u.scope = RT_SCOPE_UNIVERSE;
.tos = RT_TOS(iph->tos),
.scope = RT_SCOPE_UNIVERSE,
}
}
};
/* Trying to route the packet using the standard routing table. */ /* Trying to route the packet using the standard routing table. */
err = ip_route_output_key(&init_net, &rt, &fl); err = ip_route_output_key(&init_net, &rt, &fl);
@@ -72,22 +68,14 @@ static bool tee_routing(struct sk_buff *skb,
return false; return false;
} }
/* Drop old route. */
dst_release(skb->dst); dst_release(skb->dst);
skb->dst = NULL;
/*
* Success if no oif specified or if the oif correspond to the
* one desired.
* [SC]: always the case, because we have no oif.
*/
skb->dst = &rt->u.dst; skb->dst = &rt->u.dst;
skb->dev = skb->dst->dev; skb->dev = skb->dst->dev;
skb->protocol = htons(ETH_P_IP); skb->protocol = htons(ETH_P_IP);
return true; return true;
} }
static bool dev_hh_avail(const struct net_device *dev) static inline bool dev_hh_avail(const struct net_device *dev)
{ {
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23)
return dev->hard_header != NULL; return dev->hard_header != NULL;
@@ -103,14 +91,14 @@ static bool dev_hh_avail(const struct net_device *dev)
* POST: the packet is sent with the link layer header pushed * POST: the packet is sent with the link layer header pushed
* the packet is destroyed * the packet is destroyed
*/ */
static void tee_ip_direct_send(struct sk_buff *skb) static void tee_tg_send(struct sk_buff *skb)
{ {
const struct dst_entry *dst = skb->dst; const struct dst_entry *dst = skb->dst;
const struct net_device *dev = dst->dev; const struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev); unsigned int hh_len = LL_RESERVED_SPACE(dev);
/* Be paranoid, rather than too clever. */ /* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len) && dev_hh_avail(dev)) { if (unlikely(skb_headroom(skb) < hh_len && dev_hh_avail(dev))) {
struct sk_buff *skb2; struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
@@ -142,7 +130,7 @@ static void tee_ip_direct_send(struct sk_buff *skb)
* packets when we see they already have that ->nfct. * packets when we see they already have that ->nfct.
*/ */
static unsigned int static unsigned int
tee_tg(struct sk_buff **pskb, const struct xt_target_param *par) tee_tg4(struct sk_buff **pskb, const struct xt_target_param *par)
{ {
const struct xt_tee_tginfo *info = par->targinfo; const struct xt_tee_tginfo *info = par->targinfo;
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
@@ -200,8 +188,78 @@ tee_tg(struct sk_buff **pskb, const struct xt_target_param *par)
nf_conntrack_get(skb->nfct); nf_conntrack_get(skb->nfct);
#endif #endif
if (tee_routing(skb, info)) /*
tee_ip_direct_send(skb); * Normally, we would just use ip_local_out. Because iph->check is
* already correct, we could take a shortcut and call dst_output
* [forwards to ip_output] directly. ip_output however will invoke
* Netfilter hooks and cause reentrancy. So we skip that too and go
* directly to ip_finish_output. Since we should not do XFRM, control
* passes to ip_finish_output2. That function is not exported, so it is
* copied here as tee_ip_direct_send.
*
* We do no XFRM on the cloned packet on purpose! The choice of
* iptables match options will control whether the raw packet or the
* transformed version is cloned.
*
* Also on purpose, no fragmentation is done, to preserve the
* packet as best as possible.
*/
if (tee_tg_route4(skb, info))
tee_tg_send(skb);
return XT_CONTINUE;
}
static bool
tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info)
{
struct dst_entry *dst;
struct flowi fl;
memset(&fl, 0, sizeof(fl));
fl.nl_u.ip6_u.daddr = info->gw.in6;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 25)
dst = ip6_route_output(NULL, &fl);
#else
dst = ip6_route_output(dev_net(skb->dev), NULL, &fl);
#endif
if (dst == NULL) {
if (net_ratelimit())
printk(KERN_ERR "ip6_route_output failed for tee\n");
return false;
}
dst_release(skb->dst);
skb->dst = dst;
skb->dev = skb->dst->dev;
skb->protocol = htons(ETH_P_IPV6);
return true;
}
static unsigned int
tee_tg6(struct sk_buff **pskb, const struct xt_target_param *par)
{
const struct xt_tee_tginfo *info = par->targinfo;
struct sk_buff *skb = *pskb;
/* Try silence. */
#ifdef WITH_CONNTRACK
if (skb->nfct == &tee_track.ct_general)
return NF_DROP;
#endif
if ((skb = skb_copy(skb, GFP_ATOMIC)) == NULL)
return XT_CONTINUE;
#ifdef WITH_CONNTRACK
nf_conntrack_put(skb->nfct);
skb->nfct = &tee_track.ct_general;
skb->nfctinfo = IP_CT_NEW;
nf_conntrack_get(skb->nfct);
#endif
if (tee_tg_route6(skb, info))
tee_tg_send(skb);
return XT_CONTINUE; return XT_CONTINUE;
} }
@@ -211,18 +269,31 @@ static bool tee_tg_check(const struct xt_tgchk_param *par)
const struct xt_tee_tginfo *info = par->targinfo; const struct xt_tee_tginfo *info = par->targinfo;
/* 0.0.0.0 and :: not allowed */ /* 0.0.0.0 and :: not allowed */
return memcmp(&info->gw, &zero_address, sizeof(zero_address)) != 0; return memcmp(&info->gw, &tee_zero_address,
sizeof(tee_zero_address)) != 0;
} }
static struct xt_target tee_tg_reg __read_mostly = { static struct xt_target tee_tg_reg[] __read_mostly = {
.name = "TEE", {
.revision = 0, .name = "TEE",
.family = NFPROTO_IPV4, .revision = 0,
.table = "mangle", .family = NFPROTO_IPV4,
.target = tee_tg, .table = "mangle",
.targetsize = sizeof(struct xt_tee_tginfo), .target = tee_tg4,
.checkentry = tee_tg_check, .targetsize = sizeof(struct xt_tee_tginfo),
.me = THIS_MODULE, .checkentry = tee_tg_check,
.me = THIS_MODULE,
},
{
.name = "TEE",
.revision = 0,
.family = NFPROTO_IPV6,
.table = "mangle",
.target = tee_tg6,
.targetsize = sizeof(struct xt_tee_tginfo),
.checkentry = tee_tg_check,
.me = THIS_MODULE,
},
}; };
static int __init tee_tg_init(void) static int __init tee_tg_init(void)
@@ -241,19 +312,20 @@ static int __init tee_tg_init(void)
tee_track.status |= IPS_NAT_DONE_MASK; tee_track.status |= IPS_NAT_DONE_MASK;
#endif #endif
return xt_register_target(&tee_tg_reg); return xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
} }
static void __exit tee_tg_exit(void) static void __exit tee_tg_exit(void)
{ {
xt_unregister_target(&tee_tg_reg); xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
/* [SC]: shoud not we cleanup tee_track here? */ /* [SC]: shoud not we cleanup tee_track here? */
} }
module_init(tee_tg_init); module_init(tee_tg_init);
module_exit(tee_tg_exit); module_exit(tee_tg_exit);
MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>"); MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>");
MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>"); MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
MODULE_DESCRIPTION("Xtables: Reroute packet copy"); MODULE_DESCRIPTION("Xtables: Reroute packet copy");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_TEE"); MODULE_ALIAS("ipt_TEE");
MODULE_ALIAS("ip6t_TEE");