[NETFILTER]: xt_conntrack match, revision 1
authorJan Engelhardt <jengelh@computergmbh.de>
Tue, 15 Jan 2008 07:40:53 +0000 (23:40 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2008 23:02:23 +0000 (15:02 -0800)
Introduces the xt_conntrack match revision 1. It uses fixed types, the
new nf_inet_addr and comes with IPv6 support, thereby completely
superseding xt_state.

Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netfilter/xt_conntrack.h
net/netfilter/xt_conntrack.c

index 70b6f718cf4cbe5fa48d51725a7d00f4fdf55054..d2492a3329bed137c9c46f7354a0b1f0599e005f 100644 (file)
@@ -6,7 +6,9 @@
 #define _XT_CONNTRACK_H
 
 #include <linux/netfilter/nf_conntrack_tuple_common.h>
-#include <linux/in.h>
+#ifdef __KERNEL__
+#      include <linux/in.h>
+#endif
 
 #define XT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
 #define XT_CONNTRACK_STATE_INVALID (1 << 0)
@@ -60,4 +62,16 @@ struct xt_conntrack_info
        /* Inverse flags */
        u_int8_t invflags;
 };
+
+struct xt_conntrack_mtinfo1 {
+       union nf_inet_addr origsrc_addr, origsrc_mask;
+       union nf_inet_addr origdst_addr, origdst_mask;
+       union nf_inet_addr replsrc_addr, replsrc_mask;
+       union nf_inet_addr repldst_addr, repldst_mask;
+       u_int32_t expires_min, expires_max;
+       u_int16_t l4proto;
+       u_int8_t state_mask, status_mask;
+       u_int8_t match_flags, invert_flags;
+};
+
 #endif /*_XT_CONNTRACK_H*/
index 3f8bfbaa9b1986009fb680f9c8c8c3d181479711..dc9e7378686436285bbce8583cf3299b11292eec 100644 (file)
@@ -1,15 +1,19 @@
-/* Kernel module to match connection tracking information.
- * Superset of Rusty's minimalistic state match.
+/*
+ *     xt_conntrack - Netfilter module to match connection tracking
+ *     information. (Superset of Rusty's minimalistic state match.)
  *
- * (C) 2001  Marc Boucher (marc@mbsi.ca).
+ *     (C) 2001  Marc Boucher (marc@mbsi.ca).
+ *     Copyright © CC Computer Consultants GmbH, 2007 - 2008
+ *     Jan Engelhardt <jengelh@computergmbh.de>
  *
- * 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.
+ *     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/module.h>
 #include <linux/skbuff.h>
+#include <net/ipv6.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_conntrack.h>
 #include <net/netfilter/nf_conntrack.h>
@@ -18,12 +22,13 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
 MODULE_DESCRIPTION("iptables connection tracking match module");
 MODULE_ALIAS("ipt_conntrack");
+MODULE_ALIAS("ip6t_conntrack");
 
 static bool
-conntrack_mt(const struct sk_buff *skb, const struct net_device *in,
-             const struct net_device *out, const struct xt_match *match,
-             const void *matchinfo, int offset, unsigned int protoff,
-             bool *hotdrop)
+conntrack_mt_v0(const struct sk_buff *skb, const struct net_device *in,
+                const struct net_device *out, const struct xt_match *match,
+                const void *matchinfo, int offset, unsigned int protoff,
+                bool *hotdrop)
 {
        const struct xt_conntrack_info *sinfo = matchinfo;
        const struct nf_conn *ct;
@@ -111,6 +116,134 @@ conntrack_mt(const struct sk_buff *skb, const struct net_device *in,
 #undef FWINV
 }
 
+static bool
+conntrack_addrcmp(const union nf_inet_addr *kaddr,
+                  const union nf_inet_addr *uaddr,
+                  const union nf_inet_addr *umask, unsigned int l3proto)
+{
+       if (l3proto == AF_INET)
+               return (kaddr->ip & umask->ip) == uaddr->ip;
+       else if (l3proto == AF_INET6)
+               return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
+                      &uaddr->in6) == 0;
+       else
+               return false;
+}
+
+static inline bool
+conntrack_mt_origsrc(const struct nf_conn *ct,
+                     const struct xt_conntrack_mtinfo1 *info,
+                     unsigned int family)
+{
+       return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
+              &info->origsrc_addr, &info->origsrc_mask, family);
+}
+
+static inline bool
+conntrack_mt_origdst(const struct nf_conn *ct,
+                     const struct xt_conntrack_mtinfo1 *info,
+                     unsigned int family)
+{
+       return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3,
+              &info->origdst_addr, &info->origdst_mask, family);
+}
+
+static inline bool
+conntrack_mt_replsrc(const struct nf_conn *ct,
+                     const struct xt_conntrack_mtinfo1 *info,
+                     unsigned int family)
+{
+       return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3,
+              &info->replsrc_addr, &info->replsrc_mask, family);
+}
+
+static inline bool
+conntrack_mt_repldst(const struct nf_conn *ct,
+                     const struct xt_conntrack_mtinfo1 *info,
+                     unsigned int family)
+{
+       return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3,
+              &info->repldst_addr, &info->repldst_mask, family);
+}
+
+static bool
+conntrack_mt(const struct sk_buff *skb, const struct net_device *in,
+             const struct net_device *out, const struct xt_match *match,
+             const void *matchinfo, int offset, unsigned int protoff,
+             bool *hotdrop)
+{
+       const struct xt_conntrack_mtinfo1 *info = matchinfo;
+       enum ip_conntrack_info ctinfo;
+       const struct nf_conn *ct;
+       unsigned int statebit;
+
+       ct = nf_ct_get(skb, &ctinfo);
+
+       if (ct == &nf_conntrack_untracked)
+               statebit = XT_CONNTRACK_STATE_UNTRACKED;
+       else if (ct != NULL)
+               statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
+       else
+               statebit = XT_CONNTRACK_STATE_INVALID;
+
+       if (info->match_flags & XT_CONNTRACK_STATE) {
+               if (ct != NULL) {
+                       if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
+                               statebit |= XT_CONNTRACK_STATE_SNAT;
+                       if (test_bit(IPS_DST_NAT_BIT, &ct->status))
+                               statebit |= XT_CONNTRACK_STATE_DNAT;
+               }
+               if ((info->state_mask & statebit) ^
+                   !(info->invert_flags & XT_CONNTRACK_STATE))
+                       return false;
+       }
+
+       if (ct == NULL)
+               return info->match_flags & XT_CONNTRACK_STATE;
+
+       if ((info->match_flags & XT_CONNTRACK_PROTO) &&
+           ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
+           info->l4proto) ^ !(info->invert_flags & XT_CONNTRACK_PROTO)))
+               return false;
+
+       if (info->match_flags & XT_CONNTRACK_ORIGSRC)
+               if (conntrack_mt_origsrc(ct, info, match->family) ^
+                   !(info->invert_flags & XT_CONNTRACK_ORIGSRC))
+                       return false;
+
+       if (info->match_flags & XT_CONNTRACK_ORIGDST)
+               if (conntrack_mt_origdst(ct, info, match->family) ^
+                   !(info->invert_flags & XT_CONNTRACK_ORIGDST))
+                       return false;
+
+       if (info->match_flags & XT_CONNTRACK_REPLSRC)
+               if (conntrack_mt_replsrc(ct, info, match->family) ^
+                   !(info->invert_flags & XT_CONNTRACK_REPLSRC))
+                       return false;
+
+       if (info->match_flags & XT_CONNTRACK_REPLDST)
+               if (conntrack_mt_repldst(ct, info, match->family) ^
+                   !(info->invert_flags & XT_CONNTRACK_REPLDST))
+                       return false;
+
+       if ((info->match_flags & XT_CONNTRACK_STATUS) &&
+           (!!(info->status_mask & ct->status) ^
+           !(info->invert_flags & XT_CONNTRACK_STATUS)))
+               return false;
+
+       if (info->match_flags & XT_CONNTRACK_EXPIRES) {
+               unsigned long expires = 0;
+
+               if (timer_pending(&ct->timeout))
+                       expires = (ct->timeout.expires - jiffies) / HZ;
+               if ((expires >= info->expires_min &&
+                   expires <= info->expires_max) ^
+                   !(info->invert_flags & XT_CONNTRACK_EXPIRES))
+                       return false;
+       }
+       return true;
+}
+
 static bool
 conntrack_mt_check(const char *tablename, const void *ip,
                    const struct xt_match *match, void *matchinfo,
@@ -144,7 +277,7 @@ struct compat_xt_conntrack_info
        u_int8_t                        invflags;
 };
 
-static void conntrack_mt_compat_from_user(void *dst, void *src)
+static void conntrack_mt_compat_from_user_v0(void *dst, void *src)
 {
        const struct compat_xt_conntrack_info *cm = src;
        struct xt_conntrack_info m = {
@@ -161,7 +294,7 @@ static void conntrack_mt_compat_from_user(void *dst, void *src)
        memcpy(dst, &m, sizeof(m));
 }
 
-static int conntrack_mt_compat_to_user(void __user *dst, void *src)
+static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src)
 {
        const struct xt_conntrack_info *m = src;
        struct compat_xt_conntrack_info cm = {
@@ -179,29 +312,53 @@ static int conntrack_mt_compat_to_user(void __user *dst, void *src)
 }
 #endif
 
-static struct xt_match conntrack_mt_reg __read_mostly = {
-       .name           = "conntrack",
-       .match          = conntrack_mt,
-       .checkentry     = conntrack_mt_check,
-       .destroy        = conntrack_mt_destroy,
-       .matchsize      = sizeof(struct xt_conntrack_info),
+static struct xt_match conntrack_mt_reg[] __read_mostly = {
+       {
+               .name       = "conntrack",
+               .revision   = 0,
+               .family     = AF_INET,
+               .match      = conntrack_mt_v0,
+               .checkentry = conntrack_mt_check,
+               .destroy    = conntrack_mt_destroy,
+               .matchsize  = sizeof(struct xt_conntrack_info),
+               .me         = THIS_MODULE,
 #ifdef CONFIG_COMPAT
-       .compatsize     = sizeof(struct compat_xt_conntrack_info),
-       .compat_from_user = conntrack_mt_compat_from_user,
-       .compat_to_user = conntrack_mt_compat_to_user,
+               .compatsize       = sizeof(struct compat_xt_conntrack_info),
+               .compat_from_user = conntrack_mt_compat_from_user_v0,
+               .compat_to_user   = conntrack_mt_compat_to_user_v0,
 #endif
-       .family         = AF_INET,
-       .me             = THIS_MODULE,
+       },
+       {
+               .name       = "conntrack",
+               .revision   = 1,
+               .family     = AF_INET,
+               .matchsize  = sizeof(struct xt_conntrack_mtinfo1),
+               .match      = conntrack_mt,
+               .checkentry = conntrack_mt_check,
+               .destroy    = conntrack_mt_destroy,
+               .me         = THIS_MODULE,
+       },
+       {
+               .name       = "conntrack",
+               .revision   = 1,
+               .family     = AF_INET6,
+               .matchsize  = sizeof(struct xt_conntrack_mtinfo1),
+               .match      = conntrack_mt,
+               .checkentry = conntrack_mt_check,
+               .destroy    = conntrack_mt_destroy,
+               .me         = THIS_MODULE,
+       },
 };
 
 static int __init conntrack_mt_init(void)
 {
-       return xt_register_match(&conntrack_mt_reg);
+       return xt_register_matches(conntrack_mt_reg,
+              ARRAY_SIZE(conntrack_mt_reg));
 }
 
 static void __exit conntrack_mt_exit(void)
 {
-       xt_unregister_match(&conntrack_mt_reg);
+       xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
 }
 
 module_init(conntrack_mt_init);