From patchwork Wed Mar 31 10:38:51 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jan Engelhardt X-Patchwork-Id: 49125 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 6C26DB7BEE for ; Wed, 31 Mar 2010 21:39:18 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933241Ab0CaKjF (ORCPT ); Wed, 31 Mar 2010 06:39:05 -0400 Received: from borg.medozas.de ([188.40.89.202]:55976 "EHLO borg.medozas.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933184Ab0CaKjD (ORCPT ); Wed, 31 Mar 2010 06:39:03 -0400 Received: by borg.medozas.de (Postfix, from userid 25121) id 2E6CFF0C26FEE; Wed, 31 Mar 2010 12:39:02 +0200 (CEST) From: Jan Engelhardt To: kaber@trash.net Cc: netfilter-devel@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH 3/5] netfilter: xtables: inclusion of xt_TEE Date: Wed, 31 Mar 2010 12:38:51 +0200 Message-Id: <1270031934-15940-4-git-send-email-jengelh@medozas.de> X-Mailer: git-send-email 1.7.0.2 In-Reply-To: <1270031934-15940-1-git-send-email-jengelh@medozas.de> References: <1270031934-15940-1-git-send-email-jengelh@medozas.de> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org xt_TEE can be used to clone and reroute a packet. This can for example be used to copy traffic at a router for logging purposes to another dedicated machine. References: http://www.gossamer-threads.com/lists/iptables/devel/68781 Signed-off-by: Jan Engelhardt --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_TEE.h | 8 + net/ipv4/ip_output.c | 1 + net/ipv6/ip6_output.c | 1 + net/netfilter/Kconfig | 7 + net/netfilter/Makefile | 1 + net/netfilter/xt_TEE.c | 272 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 291 insertions(+), 0 deletions(-) create mode 100644 include/linux/netfilter/xt_TEE.h create mode 100644 net/netfilter/xt_TEE.c diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index a5a63e4..48767cd 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -16,6 +16,7 @@ header-y += xt_RATEEST.h header-y += xt_SECMARK.h header-y += xt_TCPMSS.h header-y += xt_TCPOPTSTRIP.h +header-y += xt_TEE.h header-y += xt_TPROXY.h header-y += xt_comment.h header-y += xt_connbytes.h diff --git a/include/linux/netfilter/xt_TEE.h b/include/linux/netfilter/xt_TEE.h new file mode 100644 index 0000000..83fa768 --- /dev/null +++ b/include/linux/netfilter/xt_TEE.h @@ -0,0 +1,8 @@ +#ifndef _XT_TEE_TARGET_H +#define _XT_TEE_TARGET_H + +struct xt_tee_tginfo { + union nf_inet_addr gw; +}; + +#endif /* _XT_TEE_TARGET_H */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index f09135e..0abfdde 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -309,6 +309,7 @@ int ip_output(struct sk_buff *skb) ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } +EXPORT_SYMBOL_GPL(ip_output); int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7e10f62..307d8bf 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -176,6 +176,7 @@ int ip6_output(struct sk_buff *skb) ip6_finish_output, !(IP6CB(skb)->flags & IPSKB_REROUTED)); } +EXPORT_SYMBOL_GPL(ip6_output); /* * xmit an sk_buff (used by TCP) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 8055786..673a6c8 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -502,6 +502,13 @@ config NETFILTER_XT_TARGET_RATEEST To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_TARGET_TEE + tristate '"TEE" - packet cloning to alternate destiantion' + depends on NETFILTER_ADVANCED + ---help--- + This option adds a "TEE" target with which a packet can be cloned and + this clone be rerouted to another nexthop. + config NETFILTER_XT_TARGET_TPROXY tristate '"TPROXY" target support (EXPERIMENTAL)' depends on EXPERIMENTAL diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index cd31afe..14e3a8f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) += xt_TPROXY.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o +obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o # matches diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c new file mode 100644 index 0000000..96dd746 --- /dev/null +++ b/net/netfilter/xt_TEE.c @@ -0,0 +1,272 @@ +/* + * "TEE" target extension for Xtables + * Copyright © Sebastian Claßen , 2007 + * Jan Engelhardt , 2007 - 2010 + * + * based on ipt_ROUTE.c from Cédric de Launois + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 or later, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +# define WITH_CONNTRACK 1 +# include +static struct nf_conn tee_track; +#endif +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# define WITH_IPV6 1 +#endif + +static const union nf_inet_addr tee_zero_address; + +/* + * Try to route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gateway : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: false - if an error occured + * true - if the packet was succesfully routed to the + * destination desired + */ +static bool +tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) +{ + const struct iphdr *iph = ip_hdr(skb); + struct rtable *rt; + struct flowi fl; + int err; + + memset(&fl, 0, sizeof(fl)); + fl.iif = skb->skb_iif; + fl.mark = skb->mark; + fl.nl_u.ip4_u.daddr = info->gw.ip; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + fl.nl_u.ip4_u.scope = RT_SCOPE_UNIVERSE; + + /* Trying to route the packet using the standard routing table. */ + err = ip_route_output_key(dev_net(skb->dev), &rt, &fl); + if (err != 0) + return false; + + dst_release(skb_dst(skb)); + skb_dst_set(skb, &rt->u.dst); + skb->dev = rt->u.dst.dev; + skb->protocol = htons(ETH_P_IP); + IPCB(skb)->flags |= IPSKB_REROUTED; + return true; +} + +/* + * To detect and deter routed packet loopback when using the --tee option, we + * take a page out of the raw.patch book: on the copied skb, we set up a fake + * ->nfct entry, pointing to the local &route_tee_track. We skip routing + * packets when we see they already have that ->nfct. + */ +static unsigned int +tee_tg4(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + struct iphdr *iph; + +#ifdef WITH_CONNTRACK + if (skb->nfct == &tee_track.ct_general) + /* + * Loopback - a packet we already routed, is to be + * routed another time. Avoid that, now. + */ + return NF_DROP; +#endif + /* + * Copy the skb, and route the copy. Will later return %XT_CONTINUE for + * the original skb, which should continue on its way as if nothing has + * happened. The copy should be independently delivered to the TEE + * --gateway. + */ + skb = skb_copy(skb, GFP_ATOMIC); + if (skb == NULL) + return XT_CONTINUE; + /* + * If we are in PREROUTING/INPUT, the checksum must be recalculated + * since the length could have changed as a result of defragmentation. + * + * We also decrease the TTL to mitigate potential TEE loops + * between two hosts. + * + * Set %IP_DF so that the original source is notified of a potentially + * decreased MTU on the clone route. IPv6 does this too. + */ + iph = ip_hdr(skb); + iph->frag_off |= htons(IP_DF); + if (par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_IN) + --iph->ttl; + ip_send_check(iph); + +#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 + /* + * Xtables is not reentrant currently, so a choice has to be made: + * 1. return absolute verdict for the original and let the cloned + * packet travel through the chains + * 2. let the original continue travelling and not pass the clone + * to Xtables. + * #2 is chosen. Normally, we would use ip_local_out for the clone. + * Because iph->check is already correct and we don't pass it to + * Xtables anyway, a shortcut to dst_output [forwards to ip_output] can + * be taken. %IPSKB_REROUTED needs to be set so that ip_output does not + * invoke POSTROUTING on the cloned packet. + */ + IPCB(skb)->flags |= IPSKB_REROUTED; + if (tee_tg_route4(skb, info)) + ip_output(skb); + + return XT_CONTINUE; +} + +#ifdef WITH_IPV6 +static bool +tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct dst_entry *dst; + struct flowi fl; + + memset(&fl, 0, sizeof(fl)); + fl.iif = skb->skb_iif; + fl.mark = skb->mark; + fl.nl_u.ip6_u.daddr = info->gw.in6; + fl.nl_u.ip6_u.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) | + (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]; + + dst = ip6_route_output(dev_net(skb->dev), NULL, &fl); + if (dst == NULL) + return false; + + dst_release(skb_dst(skb)); + skb_dst_set(skb, dst); + skb->dev = dst->dev; + skb->protocol = htons(ETH_P_IPV6); + IP6CB(skb)->flags |= IPSKB_REROUTED; + return true; +} + +static unsigned int +tee_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + +#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 (par->hooknum == NF_INET_PRE_ROUTING || + par->hooknum == NF_INET_LOCAL_IN) { + struct ipv6hdr *iph = ipv6_hdr(skb); + --iph->hop_limit; + } + IP6CB(skb)->flags |= IPSKB_REROUTED; + if (tee_tg_route6(skb, info)) + ip6_output(skb); + + return XT_CONTINUE; +} +#endif /* WITH_IPV6 */ + +static int tee_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_tee_tginfo *info = par->targinfo; + + /* 0.0.0.0 and :: not allowed */ + return (memcmp(&info->gw, &tee_zero_address, + sizeof(tee_zero_address)) == 0) ? -EINVAL : 0; +} + +static struct xt_target tee_tg_reg[] __read_mostly = { + { + .name = "TEE", + .revision = 0, + .family = NFPROTO_IPV4, + .target = tee_tg4, + .targetsize = sizeof(struct xt_tee_tginfo), + .checkentry = tee_tg_check, + .me = THIS_MODULE, + }, +#ifdef WITH_IPV6 + { + .name = "TEE", + .revision = 0, + .family = NFPROTO_IPV6, + .target = tee_tg6, + .targetsize = sizeof(struct xt_tee_tginfo), + .checkentry = tee_tg_check, + .me = THIS_MODULE, + }, +#endif +}; + +static int __init tee_tg_init(void) +{ +#ifdef WITH_CONNTRACK + /* + * Set up fake conntrack (stolen from raw.patch): + * - to never be deleted, not in any hashes + */ + atomic_set(&tee_track.ct_general.use, 1); + + /* - and look it like as a confirmed connection */ + set_bit(IPS_CONFIRMED_BIT, &tee_track.status); + + /* Initialize fake conntrack so that NAT will skip it */ + tee_track.status |= IPS_NAT_DONE_MASK; +#endif + return xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); +} + +static void __exit tee_tg_exit(void) +{ + xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); +} + +module_init(tee_tg_init); +module_exit(tee_tg_exit); +MODULE_AUTHOR("Sebastian Claßen "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_DESCRIPTION("Xtables: Reroute packet copy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_TEE"); +MODULE_ALIAS("ip6t_TEE");