[NETFILTER]: x_tables: add TRACE target
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Sun, 8 Jul 2007 05:21:23 +0000 (22:21 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Wed, 11 Jul 2007 05:17:14 +0000 (22:17 -0700)
The TRACE target can be used to follow IP and IPv6 packets through
the ruleset.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick NcHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/core/skbuff.c
net/ipv4/ip_output.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/ip6_output.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/xt_TRACE.c [new file with mode: 0644]

index 2d6a14f..625d73b 100644 (file)
@@ -227,6 +227,7 @@ typedef unsigned char *sk_buff_data_t;
  *     @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
@@ -278,7 +279,8 @@ struct sk_buff {
                                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);
index 6a41b96..0583e84 100644 (file)
@@ -428,6 +428,10 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
        n->destructor = NULL;
        C(mark);
        __nf_copy(n, skb);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       C(nf_trace);
+#endif
 #ifdef CONFIG_NET_SCHED
        C(tc_index);
 #ifdef CONFIG_NET_CLS_ACT
@@ -485,6 +489,10 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->destructor = NULL;
        new->mark       = old->mark;
        __nf_copy(new, old);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       new->nf_trace   = old->nf_trace;
+#endif
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
        new->ipvs_property = old->ipvs_property;
 #endif
index a7dd343..c9e2b5e 100644 (file)
@@ -399,6 +399,10 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       to->nf_trace = from->nf_trace;
+#endif
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
        to->ipvs_property = from->ipvs_property;
 #endif
index 7962306..650ab52 100644 (file)
@@ -204,6 +204,112 @@ get_entry(void *base, unsigned int offset)
        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;
+}
+
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+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 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);
+}
+#endif
+
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ipt_do_table(struct sk_buff **pskb,
@@ -261,6 +367,14 @@ ipt_do_table(struct sk_buff **pskb,
 
                        t = ipt_get_target(e);
                        IP_NF_ASSERT(t->u.kernel.target);
+
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+                       /* The packet is traced: log it */
+                       if (unlikely((*pskb)->nf_trace))
+                               trace_packet(*pskb, hook, in, out,
+                                            table->name, private, e);
+#endif
                        /* Standard target? */
                        if (!t->u.kernel.target->target) {
                                int v;
@@ -341,19 +455,6 @@ ipt_do_table(struct sk_buff **pskb,
 #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
index 31dafaf..50d86e9 100644 (file)
@@ -521,6 +521,10 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       to->nf_trace = from->nf_trace;
+#endif
        skb_copy_secmark(to, from);
 }
 
index 7fe4d29..4f93b79 100644 (file)
@@ -241,6 +241,113 @@ get_entry(void *base, unsigned int offset)
        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));
+}
+
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+/* 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 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);
+}
+#endif
+
 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
 unsigned int
 ip6t_do_table(struct sk_buff **pskb,
@@ -298,6 +405,14 @@ ip6t_do_table(struct sk_buff **pskb,
 
                        t = ip6t_get_target(e);
                        IP_NF_ASSERT(t->u.kernel.target);
+
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+                       /* The packet is traced: log it */
+                       if (unlikely((*pskb)->nf_trace))
+                               trace_packet(*pskb, hook, in, out,
+                                            table->name, private, e);
+#endif
                        /* Standard target? */
                        if (!t->u.kernel.target->target) {
                                int v;
@@ -377,19 +492,6 @@ ip6t_do_table(struct sk_buff **pskb,
 #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
index aa567fa..df5e8da 100644 (file)
@@ -343,6 +343,18 @@ config NETFILTER_XT_TARGET_NOTRACK
          If you want to compile it as a module, say M here and read
          <file:Documentation/kbuild/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
index 3cf5b9c..3b79268 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o
 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 --git a/net/netfilter/xt_TRACE.c b/net/netfilter/xt_TRACE.c
new file mode 100644 (file)
index 0000000..b82fc46
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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");
+MODULE_ALIAS("ip6t_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);