Follow packets in rules

Jozsef Kadlecsik kadlec at blackhole.kfki.hu
Mon Jun 18 12:16:40 CEST 2007


Hi Patrick,

On Thu, 14 Jun 2007, Patrick McHardy wrote:

>> An earlier version of the patch did not suffer from backward
>> incompatibility: when we hit a marked packet and a matched rule, it
>> searched the chain name and computed the rule number internally. But it
>> can slow down packet processing if there are many matching rules and
>> large number of rules in the chains so I introduced stored rulenumbers.
>> What about going back to that approach? Tracing packets should not be
>> considered as normal (and thus performance efficient) mode.
>
> Yes, that sounds reasonable. Performance doesn't matter much for this.

Attached you can find the reworked TRACE target, which does not suffer 
from backward incompatibility. Unfortunately I had to steal one unused bit 
from skbuff to be able to mark the packets so that it surely does not 
clash with any rules using the standard "MARK" target. Logging level, 
flags and type are hardcoded. What do you think?

Best regards,
Jozsef
-
E-mail  : kadlec at blackhole.kfki.hu, kadlec at sunserv.kfki.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : KFKI Research Institute for Particle and Nuclear Physics
           H-1525 Budapest 114, POB. 49, Hungary
-------------- next part --------------
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/include/linux/skbuff.h linux-2.6.21-TRACE/include/linux/skbuff.h
--- linux-2.6.21-orig/include/linux/skbuff.h	2007-04-26 05:08:32.000000000 +0200
+++ linux-2.6.21-TRACE/include/linux/skbuff.h	2007-06-14 16:35:25.000000000 +0200
@@ -217,6 +217,7 @@
  *	@mark: Generic packet mark
  *	@nfct: Associated connection, if any
  *	@ipvs_property: skbuff is owned by ipvs
+ *	@nf_trace: netfilter packet trace flag
  *	@nfctinfo: Relationship of this skb to the connection
  *	@nfct_reasm: netfilter conntrack re-assembly pointer
  *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
@@ -285,7 +286,8 @@
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1;
+				ipvs_property:1,
+				nf_trace:1;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/net/ipv4/netfilter/ip_tables.c linux-2.6.21-TRACE/net/ipv4/netfilter/ip_tables.c
--- linux-2.6.21-orig/net/ipv4/netfilter/ip_tables.c	2007-04-26 05:08:32.000000000 +0200
+++ linux-2.6.21-TRACE/net/ipv4/netfilter/ip_tables.c	2007-06-18 11:20:10.000000000 +0200
@@ -210,6 +210,110 @@
 	return (struct ipt_entry *)(base + offset);
 }
 
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
+		if (((__u32 *)ip)[i])
+			return 0;
+
+	return 1;
+}
+
+static const char *hooknames[] = {
+	[NF_IP_PRE_ROUTING] = "PREROUTING",
+	[NF_IP_LOCAL_IN] = "INPUT",
+	[NF_IP_FORWARD] = "FORWARD",
+	[NF_IP_LOCAL_OUT] = "OUTPUT",
+	[NF_IP_POST_ROUTING] = "POSTROUTING",
+};
+
+enum nf_ip_trace_comments {
+	NF_IP_TRACE_COMMENT_RULE,
+	NF_IP_TRACE_COMMENT_RETURN,
+	NF_IP_TRACE_COMMENT_POLICY,
+};
+
+static const char *comments[] = {
+	[NF_IP_TRACE_COMMENT_RULE] = "rule",
+	[NF_IP_TRACE_COMMENT_RETURN] = "return",
+	[NF_IP_TRACE_COMMENT_POLICY] = "policy",
+};
+
+static struct nf_loginfo trace_loginfo = {
+	.type = NF_LOG_TYPE_LOG,
+	.u = {
+		.log = {
+			.level = 4,
+			.logflags = NF_LOG_MASK,
+		},
+	},
+};
+
+static inline int
+get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e,
+		      char *hookname, char **chainname,
+		      char **comment, unsigned int *rulenum)
+{
+	struct ipt_standard_target *t = (void *)ipt_get_target(s);
+	
+	if (strcmp(t->target.u.kernel.target->name, IPT_ERROR_TARGET) == 0) {
+		/* Head of user chain: ERROR target with chainname */
+		*chainname = t->target.data;
+		(*rulenum) = 0;
+	} else if (s == e) {
+		(*rulenum)++;
+
+		if (s->target_offset == sizeof(struct ipt_entry)
+		   && strcmp(t->target.u.kernel.target->name, 
+		   	     IPT_STANDARD_TARGET) == 0
+		   && t->verdict < 0
+	           && unconditional(&s->ip)) {
+	           	/* Tail of chains: STANDARD target (return/policy) */
+	           	*comment = *chainname == hookname 
+	           		? (char *)comments[NF_IP_TRACE_COMMENT_POLICY]
+	           		: (char *)comments[NF_IP_TRACE_COMMENT_RETURN];
+		}
+		return 1;
+	} else
+		(*rulenum)++;
+
+	return 0;
+}
+
+
+static inline void trace_packet(struct sk_buff *skb,
+				unsigned int hook,
+				const struct net_device *in,
+				const struct net_device *out,
+				char *tablename,
+				struct xt_table_info *private,
+				struct ipt_entry *e)
+{
+	void *table_base;
+	struct ipt_entry *root;
+	char *hookname, *chainname, *comment;
+	unsigned int rulenum = 0;
+
+	table_base = (void *)private->entries[smp_processor_id()];
+	root = get_entry(table_base, private->hook_entry[hook]);
+
+	hookname = chainname = (char *)hooknames[hook];
+	comment = (char *)comments[NF_IP_TRACE_COMMENT_RULE];
+
+	IPT_ENTRY_ITERATE(root,
+			  private->size - private->hook_entry[hook],
+			  get_chainname_rulenum,
+			  e, hookname, &chainname, &comment, &rulenum);
+
+	nf_log_packet(AF_INET, hook, skb, in, out, &trace_loginfo,
+		      "TRACE: %s:%s:%s:%u ",
+		      tablename, chainname, comment, rulenum);
+}
+
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ipt_do_table(struct sk_buff **pskb,
@@ -267,6 +371,12 @@
 
 			t = ipt_get_target(e);
 			IP_NF_ASSERT(t->u.kernel.target);
+			
+			/* The packet is traced: log it */
+			if ((*pskb)->nf_trace)
+				trace_packet(*pskb, hook, in, out,
+					     table->name, private, e);
+
 			/* Standard target? */
 			if (!t->u.kernel.target->target) {
 				int v;
@@ -347,19 +457,6 @@
 #endif
 }
 
-/* All zeroes == unconditional rule. */
-static inline int
-unconditional(const struct ipt_ip *ip)
-{
-	unsigned int i;
-
-	for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
-		if (((__u32 *)ip)[i])
-			return 0;
-
-	return 1;
-}
-
 /* Figures out from what hook each rule can be called: returns 0 if
    there are loops.  Puts hook bitmask in comefrom. */
 static int
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/net/ipv6/netfilter/ip6_tables.c linux-2.6.21-TRACE/net/ipv6/netfilter/ip6_tables.c
--- linux-2.6.21-orig/net/ipv6/netfilter/ip6_tables.c	2007-04-26 05:08:32.000000000 +0200
+++ linux-2.6.21-TRACE/net/ipv6/netfilter/ip6_tables.c	2007-06-18 11:22:17.000000000 +0200
@@ -250,6 +250,111 @@
 	return (struct ip6t_entry *)(base + offset);
 }
 
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ipv6); i++)
+		if (((char *)ipv6)[i])
+			break;
+
+	return (i == sizeof(*ipv6));
+}
+
+/* This cries for unification! */
+static const char *hooknames[] = {
+	[NF_IP6_PRE_ROUTING] = "PREROUTING",
+	[NF_IP6_LOCAL_IN] = "INPUT",
+	[NF_IP6_FORWARD] = "FORWARD",
+	[NF_IP6_LOCAL_OUT] = "OUTPUT",
+	[NF_IP6_POST_ROUTING] = "POSTROUTING",
+};
+
+enum nf_ip_trace_comments {
+	NF_IP6_TRACE_COMMENT_RULE,
+	NF_IP6_TRACE_COMMENT_RETURN,
+	NF_IP6_TRACE_COMMENT_POLICY,
+};
+
+static const char *comments[] = {
+	[NF_IP6_TRACE_COMMENT_RULE] = "rule",
+	[NF_IP6_TRACE_COMMENT_RETURN] = "return",
+	[NF_IP6_TRACE_COMMENT_POLICY] = "policy",
+};
+
+static struct nf_loginfo trace_loginfo = {
+	.type = NF_LOG_TYPE_LOG,
+	.u = {
+		.log = {
+			.level = 4,
+			.logflags = NF_LOG_MASK,
+		},
+	},
+};
+
+static inline int
+get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
+		      char *hookname, char **chainname,
+		      char **comment, unsigned int *rulenum)
+{
+	struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
+	
+	if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
+		/* Head of user chain: ERROR target with chainname */
+		*chainname = t->target.data;
+		(*rulenum) = 0;
+	} else if (s == e) {
+		(*rulenum)++;
+
+		if (s->target_offset == sizeof(struct ip6t_entry)
+		   && strcmp(t->target.u.kernel.target->name, 
+		   	     IP6T_STANDARD_TARGET) == 0
+		   && t->verdict < 0
+	           && unconditional(&s->ipv6)) {
+	           	/* Tail of chains: STANDARD target (return/policy) */
+	           	*comment = *chainname == hookname 
+	           		? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
+	           		: (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
+		}
+		return 1;
+	} else
+		(*rulenum)++;
+
+	return 0;
+}
+
+
+static inline void trace_packet(struct sk_buff *skb,
+				unsigned int hook,
+				const struct net_device *in,
+				const struct net_device *out,
+				char *tablename,
+				struct xt_table_info *private,
+				struct ip6t_entry *e)
+{
+	void *table_base;
+	struct ip6t_entry *root;
+	char *hookname, *chainname, *comment;
+	unsigned int rulenum = 0;
+
+	table_base = (void *)private->entries[smp_processor_id()];
+	root = get_entry(table_base, private->hook_entry[hook]);
+
+	hookname = chainname = (char *)hooknames[hook];
+	comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
+
+	IP6T_ENTRY_ITERATE(root,
+			   private->size - private->hook_entry[hook],
+			   get_chainname_rulenum,
+			   e, hookname, &chainname, &comment, &rulenum);
+
+	nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
+		      "TRACE: %s:%s:%s:%u ",
+		      tablename, chainname, comment, rulenum);
+}
+
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ip6t_do_table(struct sk_buff **pskb,
@@ -307,6 +412,12 @@
 
 			t = ip6t_get_target(e);
 			IP_NF_ASSERT(t->u.kernel.target);
+
+			/* The packet is traced: log it */
+			if ((*pskb)->nf_trace)
+				trace_packet(*pskb, hook, in, out,
+					     table->name, private, e);
+
 			/* Standard target? */
 			if (!t->u.kernel.target->target) {
 				int v;
@@ -386,19 +497,6 @@
 #endif
 }
 
-/* All zeroes == unconditional rule. */
-static inline int
-unconditional(const struct ip6t_ip6 *ipv6)
-{
-	unsigned int i;
-
-	for (i = 0; i < sizeof(*ipv6); i++)
-		if (((char *)ipv6)[i])
-			break;
-
-	return (i == sizeof(*ipv6));
-}
-
 /* Figures out from what hook each rule can be called: returns 0 if
    there are loops.  Puts hook bitmask in comefrom. */
 static int
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/net/netfilter/Kconfig linux-2.6.21-TRACE/net/netfilter/Kconfig
--- linux-2.6.21-orig/net/netfilter/Kconfig	2007-04-26 05:08:32.000000000 +0200
+++ linux-2.6.21-TRACE/net/netfilter/Kconfig	2007-06-14 22:06:57.000000000 +0200
@@ -376,6 +376,18 @@
 	  If you want to compile it as a module, say M here and read
 	  <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_TARGET_TRACE
+	tristate  '"TRACE" target support'
+	depends on NETFILTER_XTABLES
+	depends on IP_NF_RAW || IP6_NF_RAW
+	help
+	  The TRACE target allows you to mark packets so that the kernel
+	  will log every rule which match the packets as those traverse
+	  the tables, chains, rules.
+	
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
 config NETFILTER_XT_TARGET_SECMARK
 	tristate '"SECMARK" target support'
 	depends on NETFILTER_XTABLES && NETWORK_SECMARK
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/net/netfilter/Makefile linux-2.6.21-TRACE/net/netfilter/Makefile
--- linux-2.6.21-orig/net/netfilter/Makefile	2007-04-26 05:08:32.000000000 +0200
+++ linux-2.6.21-TRACE/net/netfilter/Makefile	2007-06-14 22:04:39.000000000 +0200
@@ -44,6 +44,7 @@
 obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
diff -urN --exclude-from=/usr/src/diff.exclude linux-2.6.21-orig/net/netfilter/xt_TRACE.c linux-2.6.21-TRACE/net/netfilter/xt_TRACE.c
--- linux-2.6.21-orig/net/netfilter/xt_TRACE.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.21-TRACE/net/netfilter/xt_TRACE.c	2007-06-14 22:04:12.000000000 +0200
@@ -0,0 +1,52 @@
+/* This is a module which is used to mark packets for tracing.
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter/x_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_TRACE");
+
+static unsigned int
+target(struct sk_buff **pskb,
+       const struct net_device *in,
+       const struct net_device *out,
+       unsigned int hooknum,
+       const struct xt_target *target,
+       const void *targinfo)
+{
+	(*pskb)->nf_trace = 1;
+	return XT_CONTINUE;
+}
+
+static struct xt_target xt_trace_target[] = {
+	{
+		.name		= "TRACE",
+		.family		= AF_INET,
+		.target		= target,
+		.table		= "raw",
+		.me		= THIS_MODULE,
+	},
+	{
+		.name		= "TRACE",
+		.family		= AF_INET6,
+		.target		= target,
+		.table		= "raw",
+		.me		= THIS_MODULE,
+	},
+};
+
+static int __init xt_trace_init(void)
+{
+	return xt_register_targets(xt_trace_target,
+				   ARRAY_SIZE(xt_trace_target));
+}
+
+static void __exit xt_trace_fini(void)
+{
+	xt_unregister_targets(xt_trace_target, ARRAY_SIZE(xt_trace_target));
+}
+
+module_init(xt_trace_init);
+module_exit(xt_trace_fini);
-------------- next part --------------
diff -urN --exclude=.svn iptables/extensions/.TRACE-test iptables-trace/extensions/.TRACE-test
--- iptables/extensions/.TRACE-test	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/.TRACE-test	2007-06-14 22:30:03.000000000 +0200
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/netfilter/xt_TRACE.c ] && echo TRACE
diff -urN --exclude=.svn iptables/extensions/.TRACE-test6 iptables-trace/extensions/.TRACE-test6
--- iptables/extensions/.TRACE-test6	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/.TRACE-test6	2007-06-18 11:40:26.000000000 +0200
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/netfilter/xt_TRACE.c ] && echo TRACE
diff -urN --exclude=.svn iptables/extensions/libip6t_TRACE.c iptables-trace/extensions/libip6t_TRACE.c
--- iptables/extensions/libip6t_TRACE.c	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/libip6t_TRACE.c	2007-06-18 11:41:40.000000000 +0200
@@ -0,0 +1,63 @@
+/* Shared library add-on to ip6tables to add TRACE target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+	printf(
+"TRACE target v%s takes no options\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+	{ 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+	return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct ip6tables_target trace 
+= {	.next = NULL,
+	.name = "TRACE",
+	.version = IPTABLES_VERSION,
+	.size = IP6T_ALIGN(0),
+	.userspacesize = IP6T_ALIGN(0),
+	.help = &help,
+	.init = &init,
+	.parse = &parse,
+	.final_check = &final_check,
+	.print = NULL, /* print */
+	.save = NULL, /* save */
+	.extra_opts = opts
+};
+
+void _init(void)
+{
+	register_target6(&trace);
+}
diff -urN --exclude=.svn iptables/extensions/libip6t_TRACE.man iptables-trace/extensions/libip6t_TRACE.man
--- iptables/extensions/libip6t_TRACE.man	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/libip6t_TRACE.man	2007-06-18 11:51:13.000000000 +0200
@@ -0,0 +1,10 @@
+This target marks packes so that the kernel will log every rule which match 
+the packets as those traverse the tables, chains, rules. (The ip6t_LOG module 
+is required for the logging.) The packets are logged with the string prefix: 
+"TRACE: tablename:chainname:type:rulenum " where type can be "rule" for 
+plain rule, "return" for implicit rule at the end of a user defined chain 
+and "policy" for the policy of the built in chains. 
+.br
+It can only be used in the
+.BR raw
+table.
diff -urN --exclude=.svn iptables/extensions/libipt_TRACE.c iptables-trace/extensions/libipt_TRACE.c
--- iptables/extensions/libipt_TRACE.c	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/libipt_TRACE.c	2007-06-14 22:20:12.000000000 +0200
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add TRACE target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+	printf(
+"TRACE target v%s takes no options\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+	{ 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+	return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_target trace 
+= {	.next = NULL,
+	.name = "TRACE",
+	.version = IPTABLES_VERSION,
+	.size = IPT_ALIGN(0),
+	.userspacesize = IPT_ALIGN(0),
+	.help = &help,
+	.init = &init,
+	.parse = &parse,
+	.final_check = &final_check,
+	.print = NULL, /* print */
+	.save = NULL, /* save */
+	.extra_opts = opts
+};
+
+void _init(void)
+{
+	register_target(&trace);
+}
diff -urN --exclude=.svn iptables/extensions/libipt_TRACE.man iptables-trace/extensions/libipt_TRACE.man
--- iptables/extensions/libipt_TRACE.man	1970-01-01 01:00:00.000000000 +0100
+++ iptables-trace/extensions/libipt_TRACE.man	2007-06-18 11:50:49.000000000 +0200
@@ -0,0 +1,10 @@
+This target marks packes so that the kernel will log every rule which match 
+the packets as those traverse the tables, chains, rules. (The ipt_LOG module 
+is required for the logging.) The packets are logged with the string prefix: 
+"TRACE: tablename:chainname:type:rulenum " where type can be "rule" for 
+plain rule, "return" for implicit rule at the end of a user defined chain 
+and "policy" for the policy of the built in chains. 
+.br
+It can only be used in the
+.BR raw
+table.


More information about the netfilter-devel mailing list