[PATCH] netfilter: add encapsulated packet mirroring target

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Users may want to monitor the traffic of multiple remote systems at a
single location while both filtering to monitor only certain traffic, and
preserving the original packet.

This extension for iptables/xtables add the ability to clone and encapsulate
any packet matched with iptables, and send it to a specified remote destination.

Signed-off-by: Colin Zeidler <czeidler@xxxxxxxxxxxxxxxxxx>
---
 include/uapi/linux/netfilter/xt_RMIRROR.h |  16 ++
 net/netfilter/Kconfig                     |   9 ++
 net/netfilter/Makefile                    |   1 +
 net/netfilter/xt_RMIRROR.c                | 246 ++++++++++++++++++++++++++++++
 4 files changed, 272 insertions(+)
 create mode 100644 include/uapi/linux/netfilter/xt_RMIRROR.h
 create mode 100644 net/netfilter/xt_RMIRROR.c

diff --git a/include/uapi/linux/netfilter/xt_RMIRROR.h b/include/uapi/linux/netfilter/xt_RMIRROR.h
new file mode 100644
index 0000000..479bf33
--- /dev/null
+++ b/include/uapi/linux/netfilter/xt_RMIRROR.h
@@ -0,0 +1,16 @@
+/*
+ * Author Colin Zeidler <czeidler@xxxxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ */
+#ifndef _LINUX_NETFILTER_XT_RMIRROR_H
+#define _LINUX_NETFILTER_XT_RMIRROR_H
+
+struct xt_rmirror_tginfo {
+	union nf_inet_addr dst;
+	unsigned int len;
+};
+
+#endif /* _LINUX_NETFILTER_XT_RMIRROR_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index e4a13cc..e7455c5 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -945,6 +945,15 @@ config NETFILTER_XT_TARGET_REDIRECT
 
 	To compile it as a module, choose M here. If unsure, say N.
 
+config NETFILTER_XT_TARGET_RMIRROR
+	tristate '"RMIRROR" - packet mirroring over GRE'
+	depends on NETFILTER_ADVANCED
+	depends on !NF_CONNTRACK || NF_CONNTRACK
+	select NF_DUP_IPV4
+	---help---
+	This option adds a "RMIRROR" target which can clone a packet and
+	encapsulate the clone in a GRE tunnel to a remote destination.
+
 config NETFILTER_XT_TARGET_TEE
 	tristate '"TEE" - packet cloning to alternate destination'
 	depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index f78ed24..12805ad 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_RATEEST) += xt_RATEEST.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_REDIRECT) += xt_REDIRECT.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_RMIRROR) += xt_RMIRROR.o
 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
diff --git a/net/netfilter/xt_RMIRROR.c b/net/netfilter/xt_RMIRROR.c
new file mode 100644
index 0000000..09d9d49
--- /dev/null
+++ b/net/netfilter/xt_RMIRROR.c
@@ -0,0 +1,246 @@
+/*
+ * Author Colin Zeidler <czeidler@xxxxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/percpu.h>
+#include <linux/route.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/gre.h>
+#include <net/checksum.h>
+#include <net/route.h>
+#include <net/neighbour.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/if_ether.h>
+
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
+#define WITH_CONNTRACK 1
+#include <net/netfilter/nf_conntrack.h>
+#endif
+
+#include <linux/netfilter/xt_RMIRROR.h>
+
+static DEFINE_PER_CPU(bool, rmirror_active);
+
+struct rmirror_hdr {
+	struct iphdr ip;
+	struct gre_base_hdr gre;
+};
+
+static struct rmirror_hdr new_rmirror = {
+	.ip = {
+		.version  = 4,
+		.ihl	  = 5,
+		.tos	  = 0,
+		.frag_off = 0,
+		.ttl	  = 0xff,
+		.protocol = 0x2F, // GRE
+	},
+	.gre = {
+		.flags	  = 0,
+		.protocol = htons(0x6558),
+	},
+};
+
+static const char zeromac[6] = { 0, 0, 0, 0, 0, 0};
+
+/* Apply routing information to skb,
+ * so it can be sent with ip_local_out()
+ *
+ * Based on net/netfilter/xt_TEE.c, tee_tg_route4
+ */
+bool rmirror_tg_route4(struct sk_buff *skb, __be32 ip)
+{
+	const struct iphdr *iph = ip_hdr(skb);
+	struct net *net = &init_net;
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	memset(&fl4, 0, sizeof(fl4));
+
+	fl4.daddr = ip;
+	fl4.flowi4_tos = RT_TOS(iph->tos);
+	fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
+	fl4.flowi4_flags = FLOWI_FLAG_KNOWN_NH;
+	rt = ip_route_output_key(net, &fl4);
+	if (IS_ERR(rt))
+		return false;
+
+	skb_dst_drop(skb);
+	skb_dst_set(skb, &rt->dst);
+	skb->dev = rt->dst.dev;
+	skb->protocol = htons(ETH_P_TEB);
+	return true;
+}
+
+/* Create new skb that encapsulates data of original
+ * skb with GRE
+ */
+bool encap_packet4(struct sk_buff *skb, const struct xt_rmirror_tginfo *info)
+{
+	struct rmirror_hdr *rmh;
+	struct iphdr *iph;
+	struct ethhdr *ehdr;
+	struct ethhdr *datamac;
+	bool mac_needed = false;
+
+	/* add extra size as we include MAC Header and IP Header */
+	int extra_header = sizeof(new_rmirror);
+
+	/* If the skb did not have a mac header, i.e. new outgoing skb
+	 * create the mac header for the skb we cloned
+	 * as if it had progressed through the stack
+	 */
+	if (!skb_mac_header_was_set(skb)) {
+		if (!skb->dev) {
+			iph = ip_hdr(skb);
+			rmirror_tg_route4(skb, iph->daddr);
+		}
+		if (skb->dev) {
+			dev_hard_header(skb,
+				skb->dev,
+				ETH_P_IP,
+				&zeromac,
+				NULL,
+				skb->len);
+			mac_needed = true;
+		}
+
+		if (skb_headroom(skb) < extra_header)
+			pskb_expand_head(skb, extra_header, 0, GFP_ATOMIC);
+	} else if (skb_mac_header_was_set(skb)) {
+		ehdr = eth_hdr(skb);
+		extra_header = extra_header + sizeof(*ehdr);
+		if (skb_headroom(skb) < extra_header)
+			pskb_expand_head(skb, extra_header, 0, GFP_ATOMIC);
+		datamac = (struct ethhdr *)skb_push(skb, sizeof(*ehdr));
+		memcpy(datamac, ehdr, sizeof(*ehdr));
+	}
+
+	skb->encapsulation = 1;
+
+	skb_reset_inner_headers(skb);
+
+	rmh = (struct rmirror_hdr *)skb_push(skb, sizeof(*rmh));
+	memcpy(rmh, &new_rmirror, sizeof(*rmh));
+
+	if (info->len > 0)
+		skb_trim(skb, sizeof(*rmh) + info->len);
+
+	iph = &rmh->ip;
+	iph->daddr = info->dst.ip;
+	iph->tot_len = htons(skb->len);
+	__ip_select_ident(&init_net, iph, 1);
+	ip_send_check(iph);
+	skb_reset_network_header(skb);
+
+	return mac_needed;
+}
+
+unsigned int rmirror_tg4(struct sk_buff *skb,
+		const struct xt_action_param *par
+		)
+{
+	const struct xt_rmirror_tginfo *info = par->targinfo;
+	struct dst_entry *inner_dst;
+	__be32 inner_daddr;
+	bool mac_needed;
+
+	if (__this_cpu_read(rmirror_active))
+		return XT_CONTINUE;
+
+	skb = skb_clone(skb, GFP_ATOMIC);
+#ifdef WITH_CONNTRACK
+	nf_reset(skb);
+	nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
+#endif //WITH CONNTRACK
+	inner_daddr = ip_hdr(skb)->daddr;
+
+	mac_needed = encap_packet4(skb, info);
+
+	if (mac_needed)
+		inner_dst = skb_dst(skb);
+
+	// Send the cloned skb
+	if (rmirror_tg_route4(skb, info->dst.ip)) {
+		// set saddr based on output device
+		struct in_device *idev = rcu_dereference(skb->dev->ip_ptr);
+		struct iphdr *iph = ip_hdr(skb);
+
+		iph->saddr = idev->ifa_list->ifa_address;
+
+		if (mac_needed) {
+			struct neighbour *n;
+			struct ethhdr *inner_eh;
+
+			inner_eh = (struct ethhdr *)((char *)iph + sizeof(new_rmirror));
+			n = dst_neigh_lookup(inner_dst, &inner_daddr);
+			if (n)
+				memcpy(inner_eh->h_dest,
+					n->ha,
+					sizeof(char) * 6);
+			else
+				memcpy(inner_eh->h_dest,
+					zeromac,
+					sizeof(char) * 6);
+		}
+
+		__this_cpu_write(rmirror_active, true);
+		ip_local_out(&init_net, skb->sk, skb);
+		__this_cpu_write(rmirror_active, false);
+	} else {
+		kfree_skb(skb);
+	}
+
+	return XT_CONTINUE;
+}
+
+static int rmirror_tg_check(const struct xt_tgchk_param *par)
+{
+	struct xt_rmirror_tginfo *info = par->targinfo;
+
+	return 0;
+}
+
+static struct xt_target rmirror_tg_reg[] __read_mostly = {
+	{
+		.name		= "RMIRROR",
+		.revision	= 0,
+		.family		= NFPROTO_IPV4,
+		.target		= rmirror_tg4,
+		.targetsize = sizeof(struct xt_rmirror_tginfo),
+		.checkentry = rmirror_tg_check,
+		.me			= THIS_MODULE,
+	},
+};
+
+static int __init rmirror_tg_init(void)
+{
+	return xt_register_targets(rmirror_tg_reg, ARRAY_SIZE(rmirror_tg_reg));
+}
+
+static void __exit rmirror_tg_exit(void)
+{
+	xt_unregister_targets(rmirror_tg_reg, ARRAY_SIZE(rmirror_tg_reg));
+}
+
+module_init(rmirror_tg_init);
+module_exit(rmirror_tg_exit);
+
+MODULE_ALIAS("ipt_RMIRROR");
+
+MODULE_AUTHOR("Colin Zeidler, czeidler@xxxxxxxxxxxxxxxxxx");
+MODULE_DESCRIPTION("Xtables: clone and send packet with GRE");
+MODULE_LICENSE("GPL");
+
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux