Initial commit.

Populate the iptables-addons repository with two modules, xt_TARPIT
and xt_TEE, as a starting point.

Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>
This commit is contained in:
Jan Engelhardt
2008-01-29 03:57:08 +01:00
commit 7a981b17b5
16 changed files with 894 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
.*.cmd
*.ko
*.la
*.lo
*.loT
*.mod.c
*.o
.deps
.libs
.tmp_versions
Makefile
Makefile.in
Module.symvers
/aclocal.m4
/autom4te*.cache
/compile
/config.*
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1

58
INSTALL Normal file
View File

@@ -0,0 +1,58 @@
Prerequirements
===============
* iptables-devel 1.4.1
* kernel-source
Compiling
=========
./configure [options]
--with-kbuild=
Specifies the path to the kernel build output directory. We
need it for building the kernel extensions. For example, on
openSUSE:
--with-kbuild=/usr/src/linux-obj/x86_64/default
--with-ksource=
Specifies the path to the kernel source directory. This is
currently needed for building the userspace extensions because
we use unsanitized kernel headers, but the option MAY
DISAPPEAR IN FUTURE.
--with-ksource=/usr/src/linux
--with-iptables=
Specifies the path to the directory where we may find
xtables.h, should it not be within the standard C compiler
include path, or if you want to override it. The directory
will be checked for xtables.h and include/xtables.h. (This is
to support the following specs:)
--with-iptables=/usr/src/iptables
--with-iptables=/usr/src/iptables/include
--with-iptables=/opt/iptables/include
--with-iptdir=
Specifies the path to where the newly built extensions should
be installed when `make install` is run. It uses the same
default as the iptables package, ${libexecdir}/iptables.
Note to distribution packagers
==============================
Except for --with-kbuild, distributions should not have a need to
supply any other flags (besides --prefix=/usr and perhaps
--libdir=/usr/lib64, etc.) to configure when all prerequired packages
are installed. If iptables-devel is installed, necessary headers
should be in /usr/include, so --with-iptables is not needed.

4
Makefile.am Normal file
View File

@@ -0,0 +1,4 @@
# -*- Makefile -*-
AUTOMAKE_OPTIONS = foreign subdir-objects
SUBDIRS = extensions

7
README Normal file
View File

@@ -0,0 +1,7 @@
iptables-addons
===============
iptables-addons is what previously has been patch-o-matic and
patch-o-matic-ng. Extensions that do not need immediate kernel
patching are collected here in this repository and can immediately be
built against a kernel and iptables.

4
autogen.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
autoreconf -fi;
rm -Rf autom4te*.cache;

62
configure.ac Normal file
View File

@@ -0,0 +1,62 @@
AC_INIT([iptables-addons], [1.4.1])
AC_CONFIG_HEADERS([config.h])
AC_PROG_INSTALL
AM_INIT_AUTOMAKE
AC_PROG_CC
AM_PROG_CC_C_O
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
kbuilddir="/lib/modules/$(uname -r)/build";
ksourcedir="/lib/modules/$(uname -r)/source";
AC_ARG_WITH([kbuild],
AS_HELP_STRING([--with-kbuild=PATH],
[Path to kernel build directory [[/lib/modules/CURRENT/build]]]),
[kbuilddir="$withval"])
AC_ARG_WITH([ksource],
AS_HELP_STRING([--with-ksource=PATH],
[Path to kernel source directory [[/lib/modules/CURRENT/source]]]),
[ksourcedir="$withval"])
AC_ARG_WITH([iptables],
AS_HELP_STRING([--with-iptables=PATH],
[Path to the iptables includes [[PREFIX/include]]]),
[iptables_location="$withval"])
AC_ARG_WITH([iptdir],
AS_HELP_STRING([--with-iptdir=PATH],
[Path to iptables modules [[LIBEXECDIR/iptables]]]),
[iptdir="$withval"],
[iptdir='${libexecdir}/iptables'])
AC_CHECK_HEADER([netinet/ip6.h], [], [AC_MSG_ERROR(but we need that for IPv6)])
AC_MSG_CHECKING([xtables.h presence])
if [[ -n "$iptables_location" ]]; then
if [[ -f "$iptables_location/xtables.h" ]]; then
AC_MSG_RESULT([$iptables_location/xtables.h])
iptables_CFLAGS="-I$iptables_location";
elif [[ -f "$iptables_location/include/xtables.h" ]]; then
AC_MSG_RESULT([$iptables_location/include/xtables.h])
iptables_CFLAGS="-I$iptables_location/include";
fi;
fi;
if [[ -z "$iptables_CFLAGS" ]]; then
if [[ -f "$includedir/xtables.h" ]]; then
AC_MSG_RESULT([$includedir/xtables.h])
else
AC_MSG_RESULT([no])
fi;
fi;
regular_CFLAGS="-D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 \
-D_REENTRANT -Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
-Winline -pipe -DIPTABLES_VERSION=\\\"$PACKAGE_VERSION\\\" \
-DIPT_LIB_DIR=\\\"\${iptdir}\\\" -DIP6T_LIB_DIR=\\\"\${iptdir}\\\"";
kinclude_CFLAGS="-I\"$kbuilddir/include\" -I\"$ksourcedir/include\"";
AC_SUBST([regular_CFLAGS iptables_CFLAGS kinclude_CFLAGS])
AC_SUBST([kbuilddir])
AC_SUBST([ksourcedir])
AC_SUBST([iptdir])
AC_OUTPUT([Makefile extensions/Makefile])

4
extensions/Kbuild Normal file
View File

@@ -0,0 +1,4 @@
# -*- Makefile -*-
obj-m += xt_TARPIT.o
obj-m += xt_TEE.o

34
extensions/Makefile.am Normal file
View File

@@ -0,0 +1,34 @@
# -*- Makefile -*-
AUTOMAKE_OPTIONS = foreign subdir-objects
abssrcdir = $(shell readlink -f ${srcdir})
regular_CFLAGS := @regular_CFLAGS@
iptables_CFLAGS := @iptables_CFLAGS@
kinclude_CFLAGS := @kinclude_CFLAGS@
AM_CFLAGS = ${regular_CFLAGS} ${iptables_CFLAGS} ${kinclude_CFLAGS} \
-D_INIT=$*_init
AM_LDFLAGS = -module -avoid-version
ipt_LTLIBRARIES = \
libxt_TARPIT.la \
libxt_TEE.la
#
# Call out to kbuild
#
.PHONY: modules modules_install clean_modules
all-local: modules
install-exec-local: modules_install
clean-local: clean_modules
modules:
make -C ${kbuilddir} M=${abssrcdir} modules;
modules_install:
make -C ${kbuilddir} M=${abssrcdir} INSTALL_MOD_PATH=${DESTDIR} modules_install;
clean_modules:
make -C ${kbuilddir} M=${abssrcdir} clean;

34
extensions/libxt_TARPIT.c Normal file
View File

@@ -0,0 +1,34 @@
#include <stdio.h>
#include <getopt.h>
#include <xtables.h>
static void tarpit_tg_help(void)
{
printf("TARPIT takes no options\n\n");
}
static int tarpit_tg_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
return 0;
}
static void tarpit_tg_check(unsigned int flags)
{
}
static struct xtables_target tarpit_tg_reg = {
.version = IPTABLES_VERSION,
.name = "TARPIT",
.family = AF_INET,
.size = XT_ALIGN(0),
.userspacesize = XT_ALIGN(0),
.help = tarpit_tg_help,
.parse = tarpit_tg_parse,
.final_check = tarpit_tg_check,
};
static void _init(void)
{
xtables_register_target(&tarpit_tg_reg);
}

View File

@@ -0,0 +1,33 @@
+Captures and holds incoming TCP connections using no local per-connection
+resources. Connections are accepted, but immediately switched to the persist
+state (0 byte window), in which the remote side stops sending data and asks to
+continue every 60-240 seconds. Attempts to close the connection are ignored,
+forcing the remote side to time out the connection in 12-24 minutes.
+
+This offers similar functionality to LaBrea
+<http://www.hackbusters.net/LaBrea/> but does not require dedicated hardware or
+IPs. Any TCP port that you would normally DROP or REJECT can instead become a
+tarpit.
+
+To tarpit connections to TCP port 80 destined for the current machine:
+.IP
+-A INPUT -p tcp -m tcp --dport 80 -j TARPIT
+.P
+To significantly slow down Code Red/Nimda-style scans of unused address space,
+forward unused ip addresses to a Linux box not acting as a router (e.g. "ip
+route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), enable IP forwarding on
+the Linux box, and add:
+.IP
+-A FORWARD -p tcp -j TARPIT
+.IP
+-A FORWARD -j DROP
+.TP
+NOTE:
+If you use the conntrack module while you are using TARPIT, you should also use
+the NOTRACK target, or the kernel will unnecessarily allocate resources for
+each TARPITted connection. To TARPIT incoming connections to the standard IRC
+port while using conntrack, you could:
+.IP
+-t raw -A PREROUTING -p tcp --dport 6667 -j NOTRACK
+.IP
+-A INPUT -p tcp --dport 6667 -j TARPIT

111
extensions/libxt_TEE.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* libxt_TEE
*
* Copyright © Sebastian Claßen <sebastian.classen@freenet.ag>, 2007
* Copyright © CC Computer Consultants GmbH, 2007 - 2008
* Jan Engelhardt <jengelh@computergmbh.de>
*/
#include <sys/socket.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <xtables.h>
#include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h>
#include "xt_TEE.h"
enum {
FLAG_GATEWAY = 1 << 0,
};
static const struct option tee_tg_opts[] = {
{.name = "gateway", .has_arg = true, .val = 'g'},
{},
};
static void tee_tg_help(void)
{
printf(
"TEE target options:\n"
" --gateway IPADDR Route packet via the gateway given by address\n"
"\n");
}
static int tee_tg_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 in_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_ipaddr(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)
{
if (flags == 0)
exit_error(PARAMETER_PROBLEM, "TEE target: "
"--gateway parameter required");
}
static void tee_tg_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 ", ipaddr_to_anyname(&info->gw.in));
else
printf("TEE gw:%s ", ipaddr_to_numeric(&info->gw.in));
}
static void tee_tg_save(const void *ip, const struct xt_entry_target *target)
{
const struct xt_tee_tginfo *info = (const void *)target->data;
printf("--gateway %s ", ipaddr_to_numeric(&info->gw.in));
}
static struct xtables_target tee_tg_reg = {
.name = "TEE",
.version = IPTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
.help = tee_tg_help,
.parse = tee_tg_parse,
.final_check = tee_tg_check,
.print = tee_tg_print,
.save = tee_tg_save,
.extra_opts = tee_tg_opts,
};
static void _init(void)
{
xtables_register_target(&tee_tg_reg);
}

View File

@@ -0,0 +1,16 @@
config NETFILTER_XT_TARGET_TARPIT
tristate '"TARPIT" target support'
depends on NETFILTER_XTABLES
---help---
Adds a TARPIT target to iptables, which captures and holds incoming TCP
connections using no local per-connection resources. Connections are
accepted, but immediately switched to the persist state (0 byte
window), in which the remote side stops sending data and asks to
continue every 60-240 seconds. Attempts to close the connection are
ignored, forcing the remote side to time out the connection in 12-24
minutes.
This offers similar functionality to LaBrea
<http://www.hackbusters.net/LaBrea/>, but does not require dedicated
hardware or IPs. Any TCP port that you would normally DROP or REJECT
can instead become a tar pit.

237
extensions/xt_TARPIT.c Normal file
View File

@@ -0,0 +1,237 @@
/*
* Kernel module to capture and hold incoming TCP connections using
* no local per-connection resources.
*
* Based on ipt_REJECT.c and offering functionality similar to
* LaBrea <http://www.hackbusters.net/LaBrea/>.
*
* Copyright (c) 2002 Aaron Hopkins <tools@die.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Goal:
* - Allow incoming TCP connections to be established.
* - Passing data should result in the connection being switched to the
* persist state (0 byte window), in which the remote side stops sending
* data and asks to continue every 60 seconds.
* - Attempts to shut down the connection should be ignored completely, so
* the remote side ends up having to time it out.
*
* This means:
* - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes
* - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing
* - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited
*/
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter/x_tables.h>
#ifdef CONFIG_BRIDGE_NETFILTER
# include <linux/netfilter_bridge.h>
#endif
#include <net/route.h>
#include <net/tcp.h>
static inline void tarpit_tcp(struct sk_buff *oldskb, unsigned int hook)
{
struct tcphdr _otcph, *oth, *tcph;
unsigned int addr_type;
struct sk_buff *nskb;
struct iphdr *niph;
u_int16_t tmp;
/* A truncated TCP header is not going to be useful */
if (oldskb->len < ip_hdrlen(oldskb) + sizeof(struct tcphdr))
return;
oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
sizeof(_otcph), &_otcph);
if (oth == NULL)
return;
/* No replies for RST, FIN or !SYN,!ACK */
if (oth->rst || oth->fin || (!oth->syn && !oth->ack))
return;
/* Rate-limit replies to !SYN,ACKs */
#if 0
if (!oth->syn && oth->ack)
if (!xrlim_allow(&ort->u.dst, HZ))
return;
#endif
/* Check checksum. */
if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
return;
/*
* Copy skb (even if skb is about to be dropped, we cannot just
* clone it because there may be other things, such as tcpdump,
* interested in it)
*/
nskb = skb_copy_expand(oldskb, LL_MAX_HEADER,
skb_tailroom(oldskb), GFP_ATOMIC);
if (nskb == NULL)
return;
/* This packet will not be the same as the other: clear nf fields */
nf_reset(nskb);
nskb->mark = 0;
skb_init_secmark(nskb);
skb_shinfo(nskb)->gso_size = 0;
skb_shinfo(nskb)->gso_segs = 0;
skb_shinfo(nskb)->gso_type = 0;
tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb));
/* Swap source and dest */
niph = ip_hdr(nskb);
niph->daddr = xchg(&niph->saddr, niph->daddr);
tmp = tcph->source;
tcph->source = tcph->dest;
tcph->dest = tmp;
/* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr) / 4;
skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
niph->tot_len = htons(nskb->len);
/* Use supplied sequence number or make a new one */
tcph->seq = oth->ack ? oth->ack_seq : 0;
/* Our SYN-ACKs must have a >0 window */
tcph->window = (oth->syn && !oth->ack) ? htons(5) : 0;
tcph->urg_ptr = 0;
/* Reset flags */
((u_int8_t *)tcph)[13] = 0;
if (oth->syn && oth->ack) {
tcph->rst = true;
tcph->ack_seq = false;
} else {
tcph->syn = oth->syn;
tcph->ack = 1;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn);
}
/* Adjust TCP checksum */
tcph->check = 0;
tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr,
niph->daddr, csum_partial((char *)tcph,
sizeof(struct tcphdr), 0));
/* Set DF, id = 0 */
niph->frag_off = htons(IP_DF);
niph->id = 0;
#ifdef CONFIG_BRIDGE_NETFILTER
if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL &&
nskb->nf_bridge->mask & BRNF_BRIDGED))
#else
if (hook != NF_INET_FORWARD)
#endif
addr_type = RTN_LOCAL;
if (ip_route_me_harder(nskb, addr_type))
goto free_nskb;
nskb->ip_summed = CHECKSUM_NONE;
/* Adjust IP TTL */
niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
/* Adjust IP checksum */
niph->check = 0;
niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl);
/* "Never happens" */
if (nskb->len > dst_mtu(nskb->dst))
goto free_nskb;
nf_ct_attach(nskb, oldskb);
NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
dst_output);
return;
free_nskb:
kfree_skb(nskb);
}
static unsigned int
tarpit_tg(struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, unsigned int hooknum,
const struct xt_target *target, const void *targinfo)
{
const struct iphdr *iph = ip_hdr(skb);
const struct rtable *rt = (const void *)skb->dst;
/* Do we have an input route cache entry? (Not in PREROUTING.) */
if (rt == NULL)
return NF_DROP;
/* No replies to physical multicast/broadcast */
/* skb != PACKET_OTHERHOST handled by ip_rcv() */
if (skb->pkt_type != PACKET_HOST)
return NF_DROP;
/* Now check at the protocol level */
if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
return NF_DROP;
/*
* Our naive response construction does not deal with IP
* options, and probably should not try.
*/
if (ip_hdrlen(skb) != sizeof(struct iphdr))
return NF_DROP;
/* We are not interested in fragments */
if (iph->frag_off & htons(IP_OFFSET))
return NF_DROP;
tarpit_tcp(skb, hooknum);
return NF_DROP;
}
static struct xt_target tarpit_tg_reg __read_mostly = {
.name = "TARPIT",
.family = AF_INET,
.table = "filter",
.hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
.proto = IPPROTO_TCP,
.target = tarpit_tg,
.me = THIS_MODULE,
};
static int __init tarpit_tg_init(void)
{
return xt_register_target(&tarpit_tg_reg);
}
static void __exit tarpit_tg_exit(void)
{
xt_unregister_target(&tarpit_tg_reg);
}
module_init(tarpit_tg_init);
module_exit(tarpit_tg_exit);
MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
MODULE_DESCRIPTION("Xtables: \"TARPIT\", capture and hold TCP connections");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_TARPIT");

View File

@@ -0,0 +1,9 @@
config NETFILTER_XT_TARGET_TEE
tristate '"TEE" target support'
depends on NETFILTER_XTABLES
depends on NETFILTER_ADVANCED
depends on IP_NF_MANGLE || IP6_NF_MANGLE
---help---
This option adds a "TEE" target, which enables you to duplicate
packets and route those duplicates to a different gateway.
The target has to be used inside the mangle table.

248
extensions/xt_TEE.c Normal file
View File

@@ -0,0 +1,248 @@
/*
* This implements the TEE target.
*
* Copyright (C) 2007 Sebastian Claßen <sebastian.classen@freenet.de> and
* CC Computer Consultants GmbH, 2007
*
* based on ipt_ROUTE.c from Cédric de Launois <delaunois@info.ucl.ac.be>
*
* This software is distributed under GNU GPL v2, 1991
*/
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/route.h>
#include <linux/skbuff.h>
#include <net/checksum.h>
#include <net/icmp.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/netfilter/x_tables.h>
#ifdef CONFIG_NETFILTER_XT_TARGET_TEE
# include <linux/netfilter/xt_TEE.h>
#else
# include "xt_TEE.h"
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
# define WITH_CONNTRACK 1
# include <net/netfilter/nf_conntrack.h>
static struct nf_conn tee_track;
#endif
static const union nf_inet_addr 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_routing(struct sk_buff *skb,
const struct xt_tee_tginfo *info)
{
int err;
struct rtable *rt;
struct iphdr *iph = ip_hdr(skb);
struct flowi fl = {
.nl_u = {
.ip4_u = {
.daddr = info->gw.ip,
.tos = RT_TOS(iph->tos),
.scope = RT_SCOPE_UNIVERSE,
}
}
};
/* Trying to route the packet using the standard routing table. */
err = ip_route_output_key(&rt, &fl);
if (err != 0) {
if (net_ratelimit())
pr_debug(KBUILD_MODNAME
": could not route packet (%d)", err);
return false;
}
/* Drop old route. */
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->dev = skb->dst->dev;
skb->protocol = htons(ETH_P_IP);
return true;
}
/*
* Stolen from ip_finish_output2
* PRE : skb->dev is set to the device we are leaving by
* skb->dst is not NULL
* POST: the packet is sent with the link layer header pushed
* the packet is destroyed
*/
static void tee_ip_direct_send(struct sk_buff *skb)
{
const struct dst_entry *dst = skb->dst;
const struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev);
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops != NULL)) {
struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
if (skb2 == NULL) {
kfree_skb(skb);
return;
}
if (skb->sk != NULL)
skb_set_owner_w(skb2, skb->sk);
kfree_skb(skb);
skb = skb2;
}
if (dst->hh != NULL) {
neigh_hh_output(dst->hh, skb);
} else if (dst->neighbour != NULL) {
dst->neighbour->output(skb);
} else {
if (net_ratelimit())
pr_debug(KBUILD_MODNAME "no hdr & no neighbour cache!\n");
kfree_skb(skb);
}
}
/*
* 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_tg(struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, unsigned int hooknum,
const struct xt_target *target, const void *targinfo)
{
const struct xt_tee_tginfo *info = targinfo;
#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.
*/
if (net_ratelimit())
pr_debug(KBUILD_MODNAME "loopback - DROP!\n");
return NF_DROP;
}
#endif
/*
* If we are in INPUT, the checksum must be recalculated since
* the length could have changed as a result of defragmentation.
*/
if (hooknum == NF_INET_LOCAL_IN) {
struct iphdr *iph = ip_hdr(skb);
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
/*
* 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 independantly delivered to the TEE --gw.
*/
skb = skb_copy(skb, GFP_ATOMIC);
if (skb == NULL) {
if (net_ratelimit())
pr_debug(KBUILD_MODNAME "copy failed!\n");
return XT_CONTINUE;
}
#ifdef WITH_CONNTRACK
/*
* Tell conntrack to forget this packet since it may get confused
* when a packet is leaving with dst address == our address.
* Good idea? Dunno. Need advice.
*
* NEW: mark the skb with our &tee_track, so we avoid looping
* on any already routed packet.
*/
nf_conntrack_put(skb->nfct);
skb->nfct = &tee_track.ct_general;
skb->nfctinfo = IP_CT_NEW;
nf_conntrack_get(skb->nfct);
#endif
if (tee_routing(skb, info))
tee_ip_direct_send(skb);
return XT_CONTINUE;
}
static bool tee_tg_check(const char *tablename, const void *entry,
const struct xt_target *target, void *targinfo,
unsigned int hook_mask)
{
const struct xt_tee_tginfo *info = targinfo;
/* 0.0.0.0 and :: not allowed */
return memcmp(&info->gw, &zero_address, sizeof(zero_address)) != 0;
}
static struct xt_target tee_tg_reg __read_mostly = {
.name = "TEE",
.family = AF_INET,
.table = "mangle",
.target = tee_tg,
.targetsize = sizeof(struct xt_tee_tginfo),
.checkentry = tee_tg_check,
.me = THIS_MODULE,
};
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_target(&tee_tg_reg);
}
static void __exit tee_tg_exit(void)
{
xt_unregister_target(&tee_tg_reg);
/* [SC]: shoud not we cleanup tee_track here? */
}
module_init(tee_tg_init);
module_exit(tee_tg_exit);
MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>");
MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
MODULE_DESCRIPTION("Xtables: Reroute packet copy");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_TEE");

8
extensions/xt_TEE.h Normal file
View File

@@ -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 */