Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Fri, 4 Oct 2013 17:26:38 +0000 (13:26 -0400)
committerDavid S. Miller <davem@davemloft.net>
Fri, 4 Oct 2013 17:26:38 +0000 (13:26 -0400)
Pablo Neira Ayuso says:

====================
The following patchset contains Netfilter updates for your net-next tree,
mostly ipset improvements and enhancements features, they are:

* Don't call ip_nest_end needlessly in the error path from me, suggested
  by Pablo Neira Ayuso, from Jozsef Kadlecsik.

* Fixed sparse warnings about shadowed variable and missing rcu annotation
  and fix of "may be used uninitialized" warnings, also from Jozsef.

* Renamed simple macro names to avoid namespace issues, reported by David
  Laight, again from Jozsef.

* Use fix sized type for timeout in the extension part, and cosmetic
  ordering of matches and targets separatedly in xt_set.c, from Jozsef.

* Support package fragments for IPv4 protos without ports from Anders K.
  Pedersen. For example this allows a hash:ip,port ipset containing the
  entry 192.168.0.1,gre:0 to match all package fragments for PPTP VPN
  tunnels to/from the host. Without this patch only the first package
  fragment (with fragment offset 0) was matched.

* Introduced a new operation to get both setname and family, from Jozsef.
  ip[6]tables set match and SET target need to know the family of the set
  in order to reject adding rules which refer to a set with a non-mathcing
  family. Currently such rules are silently accepted and then ignored
  instead of generating an error message to the user.

* Reworked extensions support in ipset types from Jozsef. The approach of
  defining structures with all variations is not manageable as the
  number of extensions grows. Therefore a blob for the extensions is
  introduced, somewhat similar to conntrack. The support of extensions
  which need a per data destroy function is added as well.

* When an element timed out in a list:set type of set, the garbage
  collector skipped the checking of the next element. So the purging
  was delayed to the next run of the gc, fixed by Jozsef.

* A small Kconfig fix: NETFILTER_NETLINK cannot be selected and
  ipset requires it.

* hash:net,net type from Oliver Smith. The type provides the ability to
  store pairs of subnets in a set.

* Comment for ipset entries from Oliver Smith. This makes possible to
  annotate entries in a set with comments, for example:

  ipset n foo hash:net,net comment
  ipset a foo 10.0.0.0/21,192.168.1.0/24 comment "office nets A and B"

* Fix of hash types resizing with comment extension from Jozsef.

* Fix of new extensions for list:set type when an element is added
  into a slot from where another element was pushed away from Jozsef.

* Introduction of a common function for the listing of the element
  extensions from Jozsef.

* Net namespace support for ipset from Vitaly Lavrov.

* hash:net,port,net type from Oliver Smith, which makes possible
  to store the triples of two subnets and a protocol, port pair in
  a set.

* Get xt_TCPMSS working with net namespace, by Gao feng.

* Use the proper net netnamespace to allocate skbs, also by Gao feng.

* A couple of cleanups for the conntrack SIP helper, by Holger
  Eitzenberger.

* Extend cttimeout to allow setting default conntrack timeouts via
  nfnetlink, so we can get rid of all our sysctl/proc interfaces in
  the future for timeout tuning, from me.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
33 files changed:
include/linux/netfilter/ipset/ip_set.h
include/linux/netfilter/ipset/ip_set_comment.h [new file with mode: 0644]
include/linux/netfilter/ipset/ip_set_timeout.h
include/linux/netfilter/nf_conntrack_sip.h
include/uapi/linux/netfilter/ipset/ip_set.h
include/uapi/linux/netfilter/nfnetlink_cttimeout.h
net/netfilter/ipset/Kconfig
net/netfilter/ipset/Makefile
net/netfilter/ipset/ip_set_bitmap_gen.h
net/netfilter/ipset/ip_set_bitmap_ip.c
net/netfilter/ipset/ip_set_bitmap_ipmac.c
net/netfilter/ipset/ip_set_bitmap_port.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipset/ip_set_getport.c
net/netfilter/ipset/ip_set_hash_gen.h
net/netfilter/ipset/ip_set_hash_ip.c
net/netfilter/ipset/ip_set_hash_ipport.c
net/netfilter/ipset/ip_set_hash_ipportip.c
net/netfilter/ipset/ip_set_hash_ipportnet.c
net/netfilter/ipset/ip_set_hash_net.c
net/netfilter/ipset/ip_set_hash_netiface.c
net/netfilter/ipset/ip_set_hash_netnet.c [new file with mode: 0644]
net/netfilter/ipset/ip_set_hash_netport.c
net/netfilter/ipset/ip_set_hash_netportnet.c [new file with mode: 0644]
net/netfilter/ipset/ip_set_list_set.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_nat_sip.c
net/netfilter/nfnetlink_cttimeout.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue_core.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_set.c
net/sched/em_ipset.c

index 9ac9fbde7b61a38795d631ab545b9bd3140fe6f6..7967516adc0d1ba2335111e863cc8c1899050bc3 100644 (file)
@@ -49,31 +49,68 @@ enum ip_set_feature {
 
 /* Set extensions */
 enum ip_set_extension {
-       IPSET_EXT_NONE = 0,
-       IPSET_EXT_BIT_TIMEOUT = 1,
+       IPSET_EXT_BIT_TIMEOUT = 0,
        IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
-       IPSET_EXT_BIT_COUNTER = 2,
+       IPSET_EXT_BIT_COUNTER = 1,
        IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER),
-};
-
-/* Extension offsets */
-enum ip_set_offset {
-       IPSET_OFFSET_TIMEOUT = 0,
-       IPSET_OFFSET_COUNTER,
-       IPSET_OFFSET_MAX,
+       IPSET_EXT_BIT_COMMENT = 2,
+       IPSET_EXT_COMMENT = (1 << IPSET_EXT_BIT_COMMENT),
+       /* Mark set with an extension which needs to call destroy */
+       IPSET_EXT_BIT_DESTROY = 7,
+       IPSET_EXT_DESTROY = (1 << IPSET_EXT_BIT_DESTROY),
 };
 
 #define SET_WITH_TIMEOUT(s)    ((s)->extensions & IPSET_EXT_TIMEOUT)
 #define SET_WITH_COUNTER(s)    ((s)->extensions & IPSET_EXT_COUNTER)
+#define SET_WITH_COMMENT(s)    ((s)->extensions & IPSET_EXT_COMMENT)
+
+/* Extension id, in size order */
+enum ip_set_ext_id {
+       IPSET_EXT_ID_COUNTER = 0,
+       IPSET_EXT_ID_TIMEOUT,
+       IPSET_EXT_ID_COMMENT,
+       IPSET_EXT_ID_MAX,
+};
+
+/* Extension type */
+struct ip_set_ext_type {
+       /* Destroy extension private data (can be NULL) */
+       void (*destroy)(void *ext);
+       enum ip_set_extension type;
+       enum ipset_cadt_flags flag;
+       /* Size and minimal alignment */
+       u8 len;
+       u8 align;
+};
+
+extern const struct ip_set_ext_type ip_set_extensions[];
 
 struct ip_set_ext {
-       unsigned long timeout;
        u64 packets;
        u64 bytes;
+       u32 timeout;
+       char *comment;
+};
+
+struct ip_set_counter {
+       atomic64_t bytes;
+       atomic64_t packets;
+};
+
+struct ip_set_comment {
+       char *str;
 };
 
 struct ip_set;
 
+#define ext_timeout(e, s)      \
+(unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT])
+#define ext_counter(e, s)      \
+(struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER])
+#define ext_comment(e, s)      \
+(struct ip_set_comment *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COMMENT])
+
+
 typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
                           const struct ip_set_ext *ext,
                           struct ip_set_ext *mext, u32 cmdflags);
@@ -147,7 +184,8 @@ struct ip_set_type {
        u8 revision_min, revision_max;
 
        /* Create set */
-       int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags);
+       int (*create)(struct net *net, struct ip_set *set,
+                     struct nlattr *tb[], u32 flags);
 
        /* Attribute policies */
        const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1];
@@ -179,14 +217,45 @@ struct ip_set {
        u8 revision;
        /* Extensions */
        u8 extensions;
+       /* Default timeout value, if enabled */
+       u32 timeout;
+       /* Element data size */
+       size_t dsize;
+       /* Offsets to extensions in elements */
+       size_t offset[IPSET_EXT_ID_MAX];
        /* The type specific data */
        void *data;
 };
 
-struct ip_set_counter {
-       atomic64_t bytes;
-       atomic64_t packets;
-};
+static inline void
+ip_set_ext_destroy(struct ip_set *set, void *data)
+{
+       /* Check that the extension is enabled for the set and
+        * call it's destroy function for its extension part in data.
+        */
+       if (SET_WITH_COMMENT(set))
+               ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(
+                       ext_comment(data, set));
+}
+
+static inline int
+ip_set_put_flags(struct sk_buff *skb, struct ip_set *set)
+{
+       u32 cadt_flags = 0;
+
+       if (SET_WITH_TIMEOUT(set))
+               if (unlikely(nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                                          htonl(set->timeout))))
+                       return -EMSGSIZE;
+       if (SET_WITH_COUNTER(set))
+               cadt_flags |= IPSET_FLAG_WITH_COUNTERS;
+       if (SET_WITH_COMMENT(set))
+               cadt_flags |= IPSET_FLAG_WITH_COMMENT;
+
+       if (!cadt_flags)
+               return 0;
+       return nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(cadt_flags));
+}
 
 static inline void
 ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter)
@@ -248,12 +317,13 @@ ip_set_init_counter(struct ip_set_counter *counter,
 }
 
 /* register and unregister set references */
-extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
-extern void ip_set_put_byindex(ip_set_id_t index);
-extern const char *ip_set_name_byindex(ip_set_id_t index);
-extern ip_set_id_t ip_set_nfnl_get(const char *name);
-extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index);
-extern void ip_set_nfnl_put(ip_set_id_t index);
+extern ip_set_id_t ip_set_get_byname(struct net *net,
+                                    const char *name, struct ip_set **set);
+extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
+extern const char *ip_set_name_byindex(struct net *net, ip_set_id_t index);
+extern ip_set_id_t ip_set_nfnl_get(struct net *net, const char *name);
+extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
+extern void ip_set_nfnl_put(struct net *net, ip_set_id_t index);
 
 /* API for iptables set match, and SET target */
 
@@ -272,6 +342,8 @@ extern void *ip_set_alloc(size_t size);
 extern void ip_set_free(void *members);
 extern int ip_set_get_ipaddr4(struct nlattr *nla,  __be32 *ipaddr);
 extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
+extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[],
+                             size_t len);
 extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                                 struct ip_set_ext *ext);
 
@@ -389,13 +461,40 @@ bitmap_bytes(u32 a, u32 b)
 }
 
 #include <linux/netfilter/ipset/ip_set_timeout.h>
+#include <linux/netfilter/ipset/ip_set_comment.h>
 
-#define IP_SET_INIT_KEXT(skb, opt, map)                        \
+static inline int
+ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set,
+                     const void *e, bool active)
+{
+       if (SET_WITH_TIMEOUT(set)) {
+               unsigned long *timeout = ext_timeout(e, set);
+
+               if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                       htonl(active ? ip_set_timeout_get(timeout)
+                               : *timeout)))
+                       return -EMSGSIZE;
+       }
+       if (SET_WITH_COUNTER(set) &&
+           ip_set_put_counter(skb, ext_counter(e, set)))
+               return -EMSGSIZE;
+       if (SET_WITH_COMMENT(set) &&
+           ip_set_put_comment(skb, ext_comment(e, set)))
+               return -EMSGSIZE;
+       return 0;
+}
+
+#define IP_SET_INIT_KEXT(skb, opt, set)                        \
        { .bytes = (skb)->len, .packets = 1,            \
-         .timeout = ip_set_adt_opt_timeout(opt, map) }
+         .timeout = ip_set_adt_opt_timeout(opt, set) }
 
-#define IP_SET_INIT_UEXT(map)                          \
+#define IP_SET_INIT_UEXT(set)                          \
        { .bytes = ULLONG_MAX, .packets = ULLONG_MAX,   \
-         .timeout = (map)->timeout }
+         .timeout = (set)->timeout }
+
+#define IP_SET_INIT_CIDR(a, b) ((a) ? (a) : (b))
+
+#define IPSET_CONCAT(a, b)             a##b
+#define IPSET_TOKEN(a, b)              IPSET_CONCAT(a, b)
 
 #endif /*_IP_SET_H */
diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h
new file mode 100644 (file)
index 0000000..21217ea
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _IP_SET_COMMENT_H
+#define _IP_SET_COMMENT_H
+
+/* Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+
+static inline char*
+ip_set_comment_uget(struct nlattr *tb)
+{
+       return nla_data(tb);
+}
+
+static inline void
+ip_set_init_comment(struct ip_set_comment *comment,
+                   const struct ip_set_ext *ext)
+{
+       size_t len = ext->comment ? strlen(ext->comment) : 0;
+
+       if (unlikely(comment->str)) {
+               kfree(comment->str);
+               comment->str = NULL;
+       }
+       if (!len)
+               return;
+       if (unlikely(len > IPSET_MAX_COMMENT_SIZE))
+               len = IPSET_MAX_COMMENT_SIZE;
+       comment->str = kzalloc(len + 1, GFP_ATOMIC);
+       if (unlikely(!comment->str))
+               return;
+       strlcpy(comment->str, ext->comment, len + 1);
+}
+
+static inline int
+ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment)
+{
+       if (!comment->str)
+               return 0;
+       return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str);
+}
+
+static inline void
+ip_set_comment_free(struct ip_set_comment *comment)
+{
+       if (unlikely(!comment->str))
+               return;
+       kfree(comment->str);
+       comment->str = NULL;
+}
+
+#endif
+#endif
index 3aac04167ca70699a60d63c50486eff9d0cc910a..83c2f9e0886cc537f57bf1db5cbfc035fbe93596 100644 (file)
@@ -23,8 +23,8 @@
 /* Set is defined with timeout support: timeout value may be 0 */
 #define IPSET_NO_TIMEOUT       UINT_MAX
 
-#define ip_set_adt_opt_timeout(opt, map)       \
-((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout)
+#define ip_set_adt_opt_timeout(opt, set)       \
+((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (set)->timeout)
 
 static inline unsigned int
 ip_set_timeout_uget(struct nlattr *tb)
index 5cac0207b95da1129ce0a3776465888da860284c..d5af3c27fb7de0385b11396ac241991bacb3684e 100644 (file)
@@ -107,55 +107,64 @@ enum sdp_header_types {
        SDP_HDR_MEDIA,
 };
 
-extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
-                                      unsigned int protoff,
-                                      unsigned int dataoff,
-                                      const char **dptr,
-                                      unsigned int *datalen);
-extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb,
-                                         unsigned int protoff, s16 off);
-extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
-                                             unsigned int protoff,
-                                             unsigned int dataoff,
-                                             const char **dptr,
-                                             unsigned int *datalen,
-                                             struct nf_conntrack_expect *exp,
-                                             unsigned int matchoff,
-                                             unsigned int matchlen);
-extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb,
-                                           unsigned int protoff,
-                                           unsigned int dataoff,
-                                           const char **dptr,
-                                           unsigned int *datalen,
-                                           unsigned int sdpoff,
-                                           enum sdp_header_types type,
-                                           enum sdp_header_types term,
-                                           const union nf_inet_addr *addr);
-extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb,
-                                           unsigned int protoff,
-                                           unsigned int dataoff,
-                                           const char **dptr,
-                                           unsigned int *datalen,
-                                           unsigned int matchoff,
-                                           unsigned int matchlen,
-                                           u_int16_t port);
-extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
-                                              unsigned int protoff,
-                                              unsigned int dataoff,
-                                              const char **dptr,
-                                              unsigned int *datalen,
-                                              unsigned int sdpoff,
-                                              const union nf_inet_addr *addr);
-extern unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb,
-                                            unsigned int protoff,
-                                            unsigned int dataoff,
-                                            const char **dptr,
-                                            unsigned int *datalen,
-                                            struct nf_conntrack_expect *rtp_exp,
-                                            struct nf_conntrack_expect *rtcp_exp,
-                                            unsigned int mediaoff,
-                                            unsigned int medialen,
-                                            union nf_inet_addr *rtp_addr);
+struct nf_nat_sip_hooks {
+       unsigned int (*msg)(struct sk_buff *skb,
+                           unsigned int protoff,
+                           unsigned int dataoff,
+                           const char **dptr,
+                           unsigned int *datalen);
+
+       void (*seq_adjust)(struct sk_buff *skb,
+                          unsigned int protoff, s16 off);
+
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+                              unsigned int dataoff,
+                              const char **dptr,
+                              unsigned int *datalen,
+                              struct nf_conntrack_expect *exp,
+                              unsigned int matchoff,
+                              unsigned int matchlen);
+
+       unsigned int (*sdp_addr)(struct sk_buff *skb,
+                                unsigned int protoff,
+                                unsigned int dataoff,
+                                const char **dptr,
+                                unsigned int *datalen,
+                                unsigned int sdpoff,
+                                enum sdp_header_types type,
+                                enum sdp_header_types term,
+                                const union nf_inet_addr *addr);
+
+       unsigned int (*sdp_port)(struct sk_buff *skb,
+                                unsigned int protoff,
+                                unsigned int dataoff,
+                                const char **dptr,
+                                unsigned int *datalen,
+                                unsigned int matchoff,
+                                unsigned int matchlen,
+                                u_int16_t port);
+
+       unsigned int (*sdp_session)(struct sk_buff *skb,
+                                   unsigned int protoff,
+                                   unsigned int dataoff,
+                                   const char **dptr,
+                                   unsigned int *datalen,
+                                   unsigned int sdpoff,
+                                   const union nf_inet_addr *addr);
+
+       unsigned int (*sdp_media)(struct sk_buff *skb,
+                                 unsigned int protoff,
+                                 unsigned int dataoff,
+                                 const char **dptr,
+                                 unsigned int *datalen,
+                                 struct nf_conntrack_expect *rtp_exp,
+                                 struct nf_conntrack_expect *rtcp_exp,
+                                 unsigned int mediaoff,
+                                 unsigned int medialen,
+                                 union nf_inet_addr *rtp_addr);
+};
+extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
 
 int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr,
                         unsigned int datalen, unsigned int *matchoff,
index 8024cdf13b700560e9bd0c1f3df1bacbdd90b900..25d3b2f79c022e92cfd8f851e0f9ae8a2f7b731a 100644 (file)
 #ifndef _UAPI_IP_SET_H
 #define _UAPI_IP_SET_H
 
-
 #include <linux/types.h>
 
 /* The protocol version */
 #define IPSET_PROTOCOL         6
 
+/* The maximum permissible comment length we will accept over netlink */
+#define IPSET_MAX_COMMENT_SIZE 255
+
 /* The max length of strings including NUL: set and type identifiers */
 #define IPSET_MAXNAMELEN       32
 
@@ -110,6 +112,7 @@ enum {
        IPSET_ATTR_IFACE,
        IPSET_ATTR_BYTES,
        IPSET_ATTR_PACKETS,
+       IPSET_ATTR_COMMENT,
        __IPSET_ATTR_ADT_MAX,
 };
 #define IPSET_ATTR_ADT_MAX     (__IPSET_ATTR_ADT_MAX - 1)
@@ -140,6 +143,7 @@ enum ipset_errno {
        IPSET_ERR_IPADDR_IPV4,
        IPSET_ERR_IPADDR_IPV6,
        IPSET_ERR_COUNTER,
+       IPSET_ERR_COMMENT,
 
        /* Type specific error codes */
        IPSET_ERR_TYPE_SPECIFIC = 4352,
@@ -176,6 +180,8 @@ enum ipset_cadt_flags {
        IPSET_FLAG_NOMATCH      = (1 << IPSET_FLAG_BIT_NOMATCH),
        IPSET_FLAG_BIT_WITH_COUNTERS = 3,
        IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
+       IPSET_FLAG_BIT_WITH_COMMENT = 4,
+       IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT),
        IPSET_FLAG_CADT_MAX     = 15,
 };
 
@@ -250,6 +256,14 @@ struct ip_set_req_get_set {
 #define IP_SET_OP_GET_BYINDEX  0x00000007      /* Get set name by index */
 /* Uses ip_set_req_get_set */
 
+#define IP_SET_OP_GET_FNAME    0x00000008      /* Get set index and family */
+struct ip_set_req_get_set_family {
+       unsigned int op;
+       unsigned int version;
+       unsigned int family;
+       union ip_set_name_index set;
+};
+
 #define IP_SET_OP_VERSION      0x00000100      /* Ask kernel version */
 struct ip_set_req_version {
        unsigned int op;
index a2810a7c5e3002d63c9651e25830431a39d51abb..1ab0b97b3a1e68336485b2679045024e86ff5444 100644 (file)
@@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types {
        IPCTNL_MSG_TIMEOUT_NEW,
        IPCTNL_MSG_TIMEOUT_GET,
        IPCTNL_MSG_TIMEOUT_DELETE,
+       IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
+       IPCTNL_MSG_TIMEOUT_DEFAULT_GET,
 
        IPCTNL_MSG_TIMEOUT_MAX
 };
index ba36c283d8372dfb3c890994c57c6bb0d46266b0..a2d6263b6c648e97c663025660ad47320c2c08ff 100644 (file)
@@ -1,7 +1,7 @@
 menuconfig IP_SET
        tristate "IP set support"
        depends on INET && NETFILTER
-       depends on NETFILTER_NETLINK
+       select NETFILTER_NETLINK
        help
          This option adds IP set support to the kernel.
          In order to define and use the sets, you need the userspace utility
@@ -90,6 +90,15 @@ config IP_SET_HASH_IPPORTNET
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_SET_HASH_NETPORTNET
+       tristate "hash:net,port,net set support"
+       depends on IP_SET
+       help
+         This option adds the hash:net,port,net set type support, by which
+         one can store two IPv4/IPv6 subnets, and a protocol/port in a set.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_SET_HASH_NET
        tristate "hash:net set support"
        depends on IP_SET
@@ -99,6 +108,15 @@ config IP_SET_HASH_NET
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_SET_HASH_NETNET
+       tristate "hash:net,net set support"
+       depends on IP_SET
+       help
+         This option adds the hash:net,net  set type support, by which
+         one can store IPv4/IPv6 network address/prefix pairs in a set.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_SET_HASH_NETPORT
        tristate "hash:net,port set support"
        depends on IP_SET
index 6e965ecd5444696b4dc7e09bb98b1d09420ce9ab..44b2d38476faeb75a95c5ac8348330a99aa866fd 100644 (file)
@@ -20,6 +20,8 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
 obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
 obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o
 obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o
+obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o
+obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o
 
 # list types
 obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o
index 25243379b887e5724d147cac0afedab58a1dec61..a13e15be7911cfcd2e3103f9790beaeb869bd478 100644 (file)
@@ -8,38 +8,32 @@
 #ifndef __IP_SET_BITMAP_IP_GEN_H
 #define __IP_SET_BITMAP_IP_GEN_H
 
-#define CONCAT(a, b)           a##b
-#define TOKEN(a,b)             CONCAT(a, b)
-
-#define mtype_do_test          TOKEN(MTYPE, _do_test)
-#define mtype_gc_test          TOKEN(MTYPE, _gc_test)
-#define mtype_is_filled                TOKEN(MTYPE, _is_filled)
-#define mtype_do_add           TOKEN(MTYPE, _do_add)
-#define mtype_do_del           TOKEN(MTYPE, _do_del)
-#define mtype_do_list          TOKEN(MTYPE, _do_list)
-#define mtype_do_head          TOKEN(MTYPE, _do_head)
-#define mtype_adt_elem         TOKEN(MTYPE, _adt_elem)
-#define mtype_add_timeout      TOKEN(MTYPE, _add_timeout)
-#define mtype_gc_init          TOKEN(MTYPE, _gc_init)
-#define mtype_kadt             TOKEN(MTYPE, _kadt)
-#define mtype_uadt             TOKEN(MTYPE, _uadt)
-#define mtype_destroy          TOKEN(MTYPE, _destroy)
-#define mtype_flush            TOKEN(MTYPE, _flush)
-#define mtype_head             TOKEN(MTYPE, _head)
-#define mtype_same_set         TOKEN(MTYPE, _same_set)
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_test             TOKEN(MTYPE, _test)
-#define mtype_add              TOKEN(MTYPE, _add)
-#define mtype_del              TOKEN(MTYPE, _del)
-#define mtype_list             TOKEN(MTYPE, _list)
-#define mtype_gc               TOKEN(MTYPE, _gc)
+#define mtype_do_test          IPSET_TOKEN(MTYPE, _do_test)
+#define mtype_gc_test          IPSET_TOKEN(MTYPE, _gc_test)
+#define mtype_is_filled                IPSET_TOKEN(MTYPE, _is_filled)
+#define mtype_do_add           IPSET_TOKEN(MTYPE, _do_add)
+#define mtype_ext_cleanup      IPSET_TOKEN(MTYPE, _ext_cleanup)
+#define mtype_do_del           IPSET_TOKEN(MTYPE, _do_del)
+#define mtype_do_list          IPSET_TOKEN(MTYPE, _do_list)
+#define mtype_do_head          IPSET_TOKEN(MTYPE, _do_head)
+#define mtype_adt_elem         IPSET_TOKEN(MTYPE, _adt_elem)
+#define mtype_add_timeout      IPSET_TOKEN(MTYPE, _add_timeout)
+#define mtype_gc_init          IPSET_TOKEN(MTYPE, _gc_init)
+#define mtype_kadt             IPSET_TOKEN(MTYPE, _kadt)
+#define mtype_uadt             IPSET_TOKEN(MTYPE, _uadt)
+#define mtype_destroy          IPSET_TOKEN(MTYPE, _destroy)
+#define mtype_flush            IPSET_TOKEN(MTYPE, _flush)
+#define mtype_head             IPSET_TOKEN(MTYPE, _head)
+#define mtype_same_set         IPSET_TOKEN(MTYPE, _same_set)
+#define mtype_elem             IPSET_TOKEN(MTYPE, _elem)
+#define mtype_test             IPSET_TOKEN(MTYPE, _test)
+#define mtype_add              IPSET_TOKEN(MTYPE, _add)
+#define mtype_del              IPSET_TOKEN(MTYPE, _del)
+#define mtype_list             IPSET_TOKEN(MTYPE, _list)
+#define mtype_gc               IPSET_TOKEN(MTYPE, _gc)
 #define mtype                  MTYPE
 
-#define ext_timeout(e, m)      \
-       (unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, m)      \
-       (struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER])
-#define get_ext(map, id)       ((map)->extensions + (map)->dsize * (id))
+#define get_ext(set, map, id)  ((map)->extensions + (set)->dsize * (id))
 
 static void
 mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
@@ -49,10 +43,21 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&map->gc);
        map->gc.data = (unsigned long) set;
        map->gc.function = gc;
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
+static void
+mtype_ext_cleanup(struct ip_set *set)
+{
+       struct mtype *map = set->data;
+       u32 id;
+
+       for (id = 0; id < map->elements; id++)
+               if (test_bit(id, map->members))
+                       ip_set_ext_destroy(set, get_ext(set, map, id));
+}
+
 static void
 mtype_destroy(struct ip_set *set)
 {
@@ -62,8 +67,11 @@ mtype_destroy(struct ip_set *set)
                del_timer_sync(&map->gc);
 
        ip_set_free(map->members);
-       if (map->dsize)
+       if (set->dsize) {
+               if (set->extensions & IPSET_EXT_DESTROY)
+                       mtype_ext_cleanup(set);
                ip_set_free(map->extensions);
+       }
        kfree(map);
 
        set->data = NULL;
@@ -74,6 +82,8 @@ mtype_flush(struct ip_set *set)
 {
        struct mtype *map = set->data;
 
+       if (set->extensions & IPSET_EXT_DESTROY)
+               mtype_ext_cleanup(set);
        memset(map->members, 0, map->memsize);
 }
 
@@ -91,12 +101,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
                          htonl(sizeof(*map) +
                                map->memsize +
-                               map->dsize * map->elements)) ||
-           (SET_WITH_TIMEOUT(set) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
-           (SET_WITH_COUNTER(set) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))))
+                               set->dsize * map->elements)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -111,16 +118,16 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       void *x = get_ext(map, e->id);
-       int ret = mtype_do_test(e, map);
+       void *x = get_ext(set, map, e->id);
+       int ret = mtype_do_test(e, map, set->dsize);
 
        if (ret <= 0)
                return ret;
        if (SET_WITH_TIMEOUT(set) &&
-           ip_set_timeout_expired(ext_timeout(x, map)))
+           ip_set_timeout_expired(ext_timeout(x, set)))
                return 0;
        if (SET_WITH_COUNTER(set))
-               ip_set_update_counter(ext_counter(x, map), ext, mext, flags);
+               ip_set_update_counter(ext_counter(x, set), ext, mext, flags);
        return 1;
 }
 
@@ -130,26 +137,30 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       void *x = get_ext(map, e->id);
-       int ret = mtype_do_add(e, map, flags);
+       void *x = get_ext(set, map, e->id);
+       int ret = mtype_do_add(e, map, flags, set->dsize);
 
        if (ret == IPSET_ADD_FAILED) {
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(x, map)))
+                   ip_set_timeout_expired(ext_timeout(x, set)))
                        ret = 0;
                else if (!(flags & IPSET_FLAG_EXIST))
                        return -IPSET_ERR_EXIST;
+               /* Element is re-added, cleanup extensions */
+               ip_set_ext_destroy(set, x);
        }
 
        if (SET_WITH_TIMEOUT(set))
 #ifdef IP_SET_BITMAP_STORED_TIMEOUT
-               mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
+               mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret);
 #else
-               ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
+               ip_set_timeout_set(ext_timeout(x, set), ext->timeout);
 #endif
 
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(x, map), ext);
+               ip_set_init_counter(ext_counter(x, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(x, set), ext);
        return 0;
 }
 
@@ -159,16 +170,27 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       const void *x = get_ext(map, e->id);
+       void *x = get_ext(set, map, e->id);
 
-       if (mtype_do_del(e, map) ||
-           (SET_WITH_TIMEOUT(set) &&
-            ip_set_timeout_expired(ext_timeout(x, map))))
+       if (mtype_do_del(e, map))
+               return -IPSET_ERR_EXIST;
+
+       ip_set_ext_destroy(set, x);
+       if (SET_WITH_TIMEOUT(set) &&
+           ip_set_timeout_expired(ext_timeout(x, set)))
                return -IPSET_ERR_EXIST;
 
        return 0;
 }
 
+#ifndef IP_SET_BITMAP_STORED_TIMEOUT
+static inline bool
+mtype_is_filled(const struct mtype_elem *x)
+{
+       return true;
+}
+#endif
+
 static int
 mtype_list(const struct ip_set *set,
           struct sk_buff *skb, struct netlink_callback *cb)
@@ -183,13 +205,13 @@ mtype_list(const struct ip_set *set,
                return -EMSGSIZE;
        for (; cb->args[2] < map->elements; cb->args[2]++) {
                id = cb->args[2];
-               x = get_ext(map, id);
+               x = get_ext(set, map, id);
                if (!test_bit(id, map->members) ||
                    (SET_WITH_TIMEOUT(set) &&
 #ifdef IP_SET_BITMAP_STORED_TIMEOUT
                     mtype_is_filled((const struct mtype_elem *) x) &&
 #endif
-                    ip_set_timeout_expired(ext_timeout(x, map))))
+                    ip_set_timeout_expired(ext_timeout(x, set))))
                        continue;
                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
                if (!nested) {
@@ -199,23 +221,10 @@ mtype_list(const struct ip_set *set,
                        } else
                                goto nla_put_failure;
                }
-               if (mtype_do_list(skb, map, id))
+               if (mtype_do_list(skb, map, id, set->dsize))
                        goto nla_put_failure;
-               if (SET_WITH_TIMEOUT(set)) {
-#ifdef IP_SET_BITMAP_STORED_TIMEOUT
-                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_stored(map, id,
-                                                       ext_timeout(x, map)))))
-                               goto nla_put_failure;
-#else
-                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_get(
-                                                       ext_timeout(x, map)))))
-                               goto nla_put_failure;
-#endif
-               }
-               if (SET_WITH_COUNTER(set) &&
-                   ip_set_put_counter(skb, ext_counter(x, map)))
+               if (ip_set_put_extensions(skb, set, x,
+                   mtype_is_filled((const struct mtype_elem *) x)))
                        goto nla_put_failure;
                ipset_nest_end(skb, nested);
        }
@@ -228,11 +237,11 @@ mtype_list(const struct ip_set *set,
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
-       ipset_nest_end(skb, adt);
        if (unlikely(id == first)) {
                cb->args[2] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, adt);
        return 0;
 }
 
@@ -241,21 +250,23 @@ mtype_gc(unsigned long ul_set)
 {
        struct ip_set *set = (struct ip_set *) ul_set;
        struct mtype *map = set->data;
-       const void *x;
+       void *x;
        u32 id;
 
        /* We run parallel with other readers (test element)
         * but adding/deleting new entries is locked out */
        read_lock_bh(&set->lock);
        for (id = 0; id < map->elements; id++)
-               if (mtype_gc_test(id, map)) {
-                       x = get_ext(map, id);
-                       if (ip_set_timeout_expired(ext_timeout(x, map)))
+               if (mtype_gc_test(id, map, set->dsize)) {
+                       x = get_ext(set, map, id);
+                       if (ip_set_timeout_expired(ext_timeout(x, set))) {
                                clear_bit(id, map->members);
+                               ip_set_ext_destroy(set, x);
+                       }
                }
        read_unlock_bh(&set->lock);
 
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
index f1a8128bef01c3f555c23264683d9f1688b06509..6f1f9f4948084e40375ab722565e7bf20b2d972d 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip");
 
 #define MTYPE          bitmap_ip
@@ -44,10 +45,7 @@ struct bitmap_ip {
        u32 elements;           /* number of max elements in the set */
        u32 hosts;              /* number of hosts in a subnet */
        size_t memsize;         /* members size */
-       size_t dsize;           /* extensions struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        u8 netmask;             /* subnet netmask */
-       u32 timeout;            /* timeout parameter */
        struct timer_list gc;   /* garbage collection */
 };
 
@@ -65,20 +63,21 @@ ip_to_id(const struct bitmap_ip *m, u32 ip)
 /* Common functions */
 
 static inline int
-bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
+bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e,
+                 struct bitmap_ip *map, size_t dsize)
 {
        return !!test_bit(e->id, map->members);
 }
 
 static inline int
-bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map)
+bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize)
 {
        return !!test_bit(id, map->members);
 }
 
 static inline int
 bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map,
-                u32 flags)
+                u32 flags, size_t dsize)
 {
        return !!test_and_set_bit(e->id, map->members);
 }
@@ -90,7 +89,8 @@ bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
 }
 
 static inline int
-bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id)
+bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id,
+                 size_t dsize)
 {
        return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
                        htonl(map->first_ip + id * map->hosts));
@@ -113,7 +113,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_ip *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ip_adt_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        u32 ip;
 
        ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
@@ -131,9 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        struct bitmap_ip *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       u32 ip, ip_to;
+       u32 ip = 0, ip_to = 0;
        struct bitmap_ip_adt_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -200,7 +200,7 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
        return x->first_ip == y->first_ip &&
               x->last_ip == y->last_ip &&
               x->netmask == y->netmask &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -209,25 +209,6 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
 struct bitmap_ip_elem {
 };
 
-/* Timeout variant */
-
-struct bitmap_ipt_elem {
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_ipc_elem {
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_ipct_elem {
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -240,8 +221,8 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
        map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -252,7 +233,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
        map->elements = elements;
        map->hosts = hosts;
        map->netmask = netmask;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_IPV4;
@@ -261,10 +242,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
 }
 
 static int
-bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+                u32 flags)
 {
        struct bitmap_ip *map;
-       u32 first_ip, last_ip, hosts, cadt_flags = 0;
+       u32 first_ip = 0, last_ip = 0, hosts;
        u64 elements;
        u8 netmask = 32;
        int ret;
@@ -336,61 +318,15 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 
        map->memsize = bitmap_bytes(0, elements - 1);
        set->variant = &bitmap_ip;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_ipct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_ipct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipct_elem, counter);
-
-                       if (!init_map_ip(set, map, first_ip, last_ip,
-                                        elements, hosts, netmask)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-
-                       map->timeout = ip_set_timeout_uget(
-                               tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-
-                       bitmap_ip_gc_init(set, bitmap_ip_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_ipc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipc_elem, counter);
-
-                       if (!init_map_ip(set, map, first_ip, last_ip,
-                                        elements, hosts, netmask)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_ipt_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_ipt_elem, timeout);
-
-               if (!init_map_ip(set, map, first_ip, last_ip,
-                                elements, hosts, netmask)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
-
+       set->dsize = ip_set_elem_len(set, tb, 0);
+       if (!init_map_ip(set, map, first_ip, last_ip,
+                        elements, hosts, netmask)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_ip_gc_init(set, bitmap_ip_gc);
-       } else {
-               map->dsize = 0;
-               if (!init_map_ip(set, map, first_ip, last_ip,
-                                elements, hosts, netmask)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
        }
        return 0;
 }
@@ -401,8 +337,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
        .features       = IPSET_TYPE_IP,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_IPV4,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_ip_create,
        .create_policy  = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
@@ -420,6 +356,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 3b30e0bef890424abbb3bbf71bd2df147a52eab2..740eabededd9754b7db95a9d46d48f8a1f283bf7 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip,mac");
 
 #define MTYPE          bitmap_ipmac
@@ -48,11 +49,8 @@ struct bitmap_ipmac {
        u32 first_ip;           /* host byte order, included in range */
        u32 last_ip;            /* host byte order, included in range */
        u32 elements;           /* number of max elements in the set */
-       u32 timeout;            /* timeout value */
-       struct timer_list gc;   /* garbage collector */
        size_t memsize;         /* members size */
-       size_t dsize;           /* size of element */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
+       struct timer_list gc;   /* garbage collector */
 };
 
 /* ADT structure for generic function args */
@@ -82,13 +80,13 @@ get_elem(void *extensions, u16 id, size_t dsize)
 
 static inline int
 bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
-                    const struct bitmap_ipmac *map)
+                    const struct bitmap_ipmac *map, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem;
 
        if (!test_bit(e->id, map->members))
                return 0;
-       elem = get_elem(map->extensions, e->id, map->dsize);
+       elem = get_elem(map->extensions, e->id, dsize);
        if (elem->filled == MAC_FILLED)
                return e->ether == NULL ||
                       ether_addr_equal(e->ether, elem->ether);
@@ -97,13 +95,13 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
 }
 
 static inline int
-bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map)
+bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem;
 
        if (!test_bit(id, map->members))
                return 0;
-       elem = get_elem(map->extensions, id, map->dsize);
+       elem = get_elem(map->extensions, id, dsize);
        /* Timer not started for the incomplete elements */
        return elem->filled == MAC_FILLED;
 }
@@ -117,13 +115,13 @@ bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem)
 static inline int
 bitmap_ipmac_add_timeout(unsigned long *timeout,
                         const struct bitmap_ipmac_adt_elem *e,
-                        const struct ip_set_ext *ext,
+                        const struct ip_set_ext *ext, struct ip_set *set,
                         struct bitmap_ipmac *map, int mode)
 {
        u32 t = ext->timeout;
 
        if (mode == IPSET_ADD_START_STORED_TIMEOUT) {
-               if (t == map->timeout)
+               if (t == set->timeout)
                        /* Timeout was not specified, get stored one */
                        t = *timeout;
                ip_set_timeout_set(timeout, t);
@@ -142,11 +140,11 @@ bitmap_ipmac_add_timeout(unsigned long *timeout,
 
 static inline int
 bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e,
-                   struct bitmap_ipmac *map, u32 flags)
+                   struct bitmap_ipmac *map, u32 flags, size_t dsize)
 {
        struct bitmap_ipmac_elem *elem;
 
-       elem = get_elem(map->extensions, e->id, map->dsize);
+       elem = get_elem(map->extensions, e->id, dsize);
        if (test_and_set_bit(e->id, map->members)) {
                if (elem->filled == MAC_FILLED) {
                        if (e->ether && (flags & IPSET_FLAG_EXIST))
@@ -178,22 +176,12 @@ bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e,
        return !test_and_clear_bit(e->id, map->members);
 }
 
-static inline unsigned long
-ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout)
-{
-       const struct bitmap_ipmac_elem *elem =
-               get_elem(map->extensions, id, map->dsize);
-
-       return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) :
-                                           *timeout;
-}
-
 static inline int
 bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map,
-                    u32 id)
+                    u32 id, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem =
-               get_elem(map->extensions, id, map->dsize);
+               get_elem(map->extensions, id, dsize);
 
        return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
                               htonl(map->first_ip + id)) ||
@@ -216,7 +204,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_ipmac *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ipmac_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        u32 ip;
 
        /* MAC can be src only */
@@ -245,8 +233,8 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct bitmap_ipmac *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ipmac_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
-       u32 ip;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0;
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -285,43 +273,12 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
 
        return x->first_ip == y->first_ip &&
               x->last_ip == y->last_ip &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
 /* Plain variant */
 
-/* Timeout variant */
-
-struct bitmap_ipmact_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_ipmacc_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_ipmacct_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip,mac type of sets */
@@ -330,11 +287,11 @@ static bool
 init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
               u32 first_ip, u32 last_ip, u32 elements)
 {
-       map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
+       map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -343,7 +300,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
        map->first_ip = first_ip;
        map->last_ip = last_ip;
        map->elements = elements;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_IPV4;
@@ -352,10 +309,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
 }
 
 static int
-bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
+bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
                    u32 flags)
 {
-       u32 first_ip, last_ip, cadt_flags = 0;
+       u32 first_ip = 0, last_ip = 0;
        u64 elements;
        struct bitmap_ipmac *map;
        int ret;
@@ -399,57 +356,15 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
 
        map->memsize = bitmap_bytes(0, elements - 1);
        set->variant = &bitmap_ipmac;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_ipmacct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_ipmacct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipmacct_elem, counter);
-
-                       if (!init_map_ipmac(set, map, first_ip, last_ip,
-                                           elements)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-                       map->timeout = ip_set_timeout_uget(
-                               tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_ipmacc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipmacc_elem, counter);
-
-                       if (!init_map_ipmac(set, map, first_ip, last_ip,
-                                           elements)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_ipmact_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_ipmact_elem, timeout);
-
-               if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
+       set->dsize = ip_set_elem_len(set, tb,
+                                    sizeof(struct bitmap_ipmac_elem));
+       if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
-       } else {
-               map->dsize = sizeof(struct bitmap_ipmac_elem);
-
-               if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-               set->variant = &bitmap_ipmac;
        }
        return 0;
 }
@@ -460,8 +375,8 @@ static struct ip_set_type bitmap_ipmac_type = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_MAC,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_IPV4,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_ipmac_create,
        .create_policy  = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
@@ -478,6 +393,7 @@ static struct ip_set_type bitmap_ipmac_type = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 8207d1fda5288dcf252a96b9db60ed5fd543144b..e7603c5b53d737b9de6248bfd0b87d0005c242fd 100644 (file)
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:port");
 
 #define MTYPE          bitmap_port
@@ -38,9 +39,6 @@ struct bitmap_port {
        u16 last_port;          /* host byte order, included in range */
        u32 elements;           /* number of max elements in the set */
        size_t memsize;         /* members size */
-       size_t dsize;           /* extensions struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
-       u32 timeout;            /* timeout parameter */
        struct timer_list gc;   /* garbage collection */
 };
 
@@ -59,20 +57,20 @@ port_to_id(const struct bitmap_port *m, u16 port)
 
 static inline int
 bitmap_port_do_test(const struct bitmap_port_adt_elem *e,
-                   const struct bitmap_port *map)
+                   const struct bitmap_port *map, size_t dsize)
 {
        return !!test_bit(e->id, map->members);
 }
 
 static inline int
-bitmap_port_gc_test(u16 id, const struct bitmap_port *map)
+bitmap_port_gc_test(u16 id, const struct bitmap_port *map, size_t dsize)
 {
        return !!test_bit(id, map->members);
 }
 
 static inline int
 bitmap_port_do_add(const struct bitmap_port_adt_elem *e,
-                  struct bitmap_port *map, u32 flags)
+                  struct bitmap_port *map, u32 flags, size_t dsize)
 {
        return !!test_and_set_bit(e->id, map->members);
 }
@@ -85,7 +83,8 @@ bitmap_port_do_del(const struct bitmap_port_adt_elem *e,
 }
 
 static inline int
-bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id)
+bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id,
+                   size_t dsize)
 {
        return nla_put_net16(skb, IPSET_ATTR_PORT,
                             htons(map->first_port + id));
@@ -106,7 +105,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_port *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_port_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        __be16 __port;
        u16 port = 0;
 
@@ -131,7 +130,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
        struct bitmap_port *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_port_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port;       /* wraparound */
        u16 port_to;
        int ret = 0;
@@ -191,7 +190,7 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 
        return x->first_port == y->first_port &&
               x->last_port == y->last_port &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -200,25 +199,6 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 struct bitmap_port_elem {
 };
 
-/* Timeout variant */
-
-struct bitmap_portt_elem {
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_portc_elem {
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_portct_elem {
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -230,8 +210,8 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
        map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * map->elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * map->elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -239,7 +219,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
        }
        map->first_port = first_port;
        map->last_port = last_port;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_UNSPEC;
@@ -248,11 +228,11 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
 }
 
 static int
-bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+                  u32 flags)
 {
        struct bitmap_port *map;
        u16 first_port, last_port;
-       u32 cadt_flags = 0;
 
        if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -276,53 +256,14 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        map->elements = last_port - first_port + 1;
        map->memsize = map->elements * sizeof(unsigned long);
        set->variant = &bitmap_port;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_portct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_portct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_portct_elem, counter);
-                       if (!init_map_port(set, map, first_port, last_port)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-
-                       map->timeout =
-                               ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       bitmap_port_gc_init(set, bitmap_port_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_portc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_portc_elem, counter);
-                       if (!init_map_port(set, map, first_port, last_port)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_portt_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_portt_elem, timeout);
-               if (!init_map_port(set, map, first_port, last_port)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
+       set->dsize = ip_set_elem_len(set, tb, 0);
+       if (!init_map_port(set, map, first_port, last_port)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_port_gc_init(set, bitmap_port_gc);
-       } else {
-               map->dsize = 0;
-               if (!init_map_port(set, map, first_port, last_port)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
        }
        return 0;
 }
@@ -333,8 +274,8 @@ static struct ip_set_type bitmap_port_type = {
        .features       = IPSET_TYPE_PORT,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_port_create,
        .create_policy  = {
                [IPSET_ATTR_PORT]       = { .type = NLA_U16 },
@@ -349,6 +290,7 @@ static struct ip_set_type bitmap_port_type = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index f2e30fb31e78efa405156ca99ba9aeaded3ea49c..dc9284bdd2dd134fa4f96c7d96bcb845dc3f0269 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/spinlock.h>
 #include <linux/rculist.h>
 #include <net/netlink.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include <linux/netfilter.h>
 #include <linux/netfilter/x_tables.h>
@@ -27,8 +29,17 @@ static LIST_HEAD(ip_set_type_list);          /* all registered set types */
 static DEFINE_MUTEX(ip_set_type_mutex);                /* protects ip_set_type_list */
 static DEFINE_RWLOCK(ip_set_ref_lock);         /* protects the set refs */
 
-static struct ip_set * __rcu *ip_set_list;     /* all individual sets */
-static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */
+struct ip_set_net {
+       struct ip_set * __rcu *ip_set_list;     /* all individual sets */
+       ip_set_id_t     ip_set_max;     /* max number of sets */
+       int             is_deleted;     /* deleted by ip_set_net_exit */
+};
+static int ip_set_net_id __read_mostly;
+
+static inline struct ip_set_net *ip_set_pernet(struct net *net)
+{
+       return net_generic(net, ip_set_net_id);
+}
 
 #define IP_SET_INC     64
 #define STREQ(a, b)    (strncmp(a, b, IPSET_MAXNAMELEN) == 0)
@@ -45,8 +56,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
 /* When the nfnl mutex is held: */
 #define nfnl_dereference(p)            \
        rcu_dereference_protected(p, 1)
-#define nfnl_set(id)                   \
-       nfnl_dereference(ip_set_list)[id]
+#define nfnl_set(inst, id)                     \
+       nfnl_dereference((inst)->ip_set_list)[id]
 
 /*
  * The set types are implemented in modules and registered set types
@@ -315,6 +326,60 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
 
+typedef void (*destroyer)(void *);
+/* ipset data extension types, in size order */
+
+const struct ip_set_ext_type ip_set_extensions[] = {
+       [IPSET_EXT_ID_COUNTER] = {
+               .type   = IPSET_EXT_COUNTER,
+               .flag   = IPSET_FLAG_WITH_COUNTERS,
+               .len    = sizeof(struct ip_set_counter),
+               .align  = __alignof__(struct ip_set_counter),
+       },
+       [IPSET_EXT_ID_TIMEOUT] = {
+               .type   = IPSET_EXT_TIMEOUT,
+               .len    = sizeof(unsigned long),
+               .align  = __alignof__(unsigned long),
+       },
+       [IPSET_EXT_ID_COMMENT] = {
+               .type    = IPSET_EXT_COMMENT | IPSET_EXT_DESTROY,
+               .flag    = IPSET_FLAG_WITH_COMMENT,
+               .len     = sizeof(struct ip_set_comment),
+               .align   = __alignof__(struct ip_set_comment),
+               .destroy = (destroyer) ip_set_comment_free,
+       },
+};
+EXPORT_SYMBOL_GPL(ip_set_extensions);
+
+static inline bool
+add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[])
+{
+       return ip_set_extensions[id].flag ?
+               (flags & ip_set_extensions[id].flag) :
+               !!tb[IPSET_ATTR_TIMEOUT];
+}
+
+size_t
+ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len)
+{
+       enum ip_set_ext_id id;
+       size_t offset = 0;
+       u32 cadt_flags = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS])
+               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+       for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
+               if (!add_extension(id, cadt_flags, tb))
+                       continue;
+               offset += ALIGN(len + offset, ip_set_extensions[id].align);
+               set->offset[id] = offset;
+               set->extensions |= ip_set_extensions[id].type;
+               offset += ip_set_extensions[id].len;
+       }
+       return len + offset;
+}
+EXPORT_SYMBOL_GPL(ip_set_elem_len);
+
 int
 ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                      struct ip_set_ext *ext)
@@ -334,6 +399,12 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                        ext->packets = be64_to_cpu(nla_get_be64(
                                                   tb[IPSET_ATTR_PACKETS]));
        }
+       if (tb[IPSET_ATTR_COMMENT]) {
+               if (!(set->extensions & IPSET_EXT_COMMENT))
+                       return -IPSET_ERR_COMMENT;
+               ext->comment = ip_set_comment_uget(tb[IPSET_ATTR_COMMENT]);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ip_set_get_extensions);
@@ -374,13 +445,14 @@ __ip_set_put(struct ip_set *set)
  */
 
 static inline struct ip_set *
-ip_set_rcu_get(ip_set_id_t index)
+ip_set_rcu_get(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        rcu_read_lock();
        /* ip_set_list itself needs to be protected */
-       set = rcu_dereference(ip_set_list)[index];
+       set = rcu_dereference(inst->ip_set_list)[index];
        rcu_read_unlock();
 
        return set;
@@ -390,7 +462,8 @@ int
 ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
            const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret = 0;
 
        BUG_ON(set == NULL);
@@ -428,7 +501,8 @@ int
 ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret;
 
        BUG_ON(set == NULL);
@@ -450,7 +524,8 @@ int
 ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret = 0;
 
        BUG_ON(set == NULL);
@@ -474,14 +549,15 @@ EXPORT_SYMBOL_GPL(ip_set_del);
  *
  */
 ip_set_id_t
-ip_set_get_byname(const char *name, struct ip_set **set)
+ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
 {
        ip_set_id_t i, index = IPSET_INVALID_ID;
        struct ip_set *s;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        rcu_read_lock();
-       for (i = 0; i < ip_set_max; i++) {
-               s = rcu_dereference(ip_set_list)[i];
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = rcu_dereference(inst->ip_set_list)[i];
                if (s != NULL && STREQ(s->name, name)) {
                        __ip_set_get(s);
                        index = i;
@@ -501,17 +577,26 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname);
  * to be valid, after calling this function.
  *
  */
-void
-ip_set_put_byindex(ip_set_id_t index)
+
+static inline void
+__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
 {
        struct ip_set *set;
 
        rcu_read_lock();
-       set = rcu_dereference(ip_set_list)[index];
+       set = rcu_dereference(inst->ip_set_list)[index];
        if (set != NULL)
                __ip_set_put(set);
        rcu_read_unlock();
 }
+
+void
+ip_set_put_byindex(struct net *net, ip_set_id_t index)
+{
+       struct ip_set_net *inst = ip_set_pernet(net);
+
+       __ip_set_put_byindex(inst, index);
+}
 EXPORT_SYMBOL_GPL(ip_set_put_byindex);
 
 /*
@@ -522,9 +607,9 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex);
  *
  */
 const char *
-ip_set_name_byindex(ip_set_id_t index)
+ip_set_name_byindex(struct net *net, ip_set_id_t index)
 {
-       const struct ip_set *set = ip_set_rcu_get(index);
+       const struct ip_set *set = ip_set_rcu_get(net, index);
 
        BUG_ON(set == NULL);
        BUG_ON(set->ref == 0);
@@ -546,14 +631,15 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex);
  * The nfnl mutex is used in the function.
  */
 ip_set_id_t
-ip_set_nfnl_get(const char *name)
+ip_set_nfnl_get(struct net *net, const char *name)
 {
        ip_set_id_t i, index = IPSET_INVALID_ID;
        struct ip_set *s;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       for (i = 0; i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s != NULL && STREQ(s->name, name)) {
                        __ip_set_get(s);
                        index = i;
@@ -573,15 +659,16 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get);
  * The nfnl mutex is used in the function.
  */
 ip_set_id_t
-ip_set_nfnl_get_byindex(ip_set_id_t index)
+ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
-       if (index > ip_set_max)
+       if (index > inst->ip_set_max)
                return IPSET_INVALID_ID;
 
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       set = nfnl_set(index);
+       set = nfnl_set(inst, index);
        if (set)
                __ip_set_get(set);
        else
@@ -600,13 +687,17 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex);
  * The nfnl mutex is used in the function.
  */
 void
-ip_set_nfnl_put(ip_set_id_t index)
+ip_set_nfnl_put(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
+
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       set = nfnl_set(index);
-       if (set != NULL)
-               __ip_set_put(set);
+       if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
+               set = nfnl_set(inst, index);
+               if (set != NULL)
+                       __ip_set_put(set);
+       }
        nfnl_unlock(NFNL_SUBSYS_IPSET);
 }
 EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@@ -664,14 +755,14 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = {
 };
 
 static struct ip_set *
-find_set_and_id(const char *name, ip_set_id_t *id)
+find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id)
 {
        struct ip_set *set = NULL;
        ip_set_id_t i;
 
        *id = IPSET_INVALID_ID;
-       for (i = 0; i < ip_set_max; i++) {
-               set = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               set = nfnl_set(inst, i);
                if (set != NULL && STREQ(set->name, name)) {
                        *id = i;
                        break;
@@ -681,22 +772,23 @@ find_set_and_id(const char *name, ip_set_id_t *id)
 }
 
 static inline struct ip_set *
-find_set(const char *name)
+find_set(struct ip_set_net *inst, const char *name)
 {
        ip_set_id_t id;
 
-       return find_set_and_id(name, &id);
+       return find_set_and_id(inst, name, &id);
 }
 
 static int
-find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set)
+find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
+            struct ip_set **set)
 {
        struct ip_set *s;
        ip_set_id_t i;
 
        *index = IPSET_INVALID_ID;
-       for (i = 0;  i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0;  i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s == NULL) {
                        if (*index == IPSET_INVALID_ID)
                                *index = i;
@@ -725,6 +817,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct net *net = sock_net(ctnl);
+       struct ip_set_net *inst = ip_set_pernet(net);
        struct ip_set *set, *clash = NULL;
        ip_set_id_t index = IPSET_INVALID_ID;
        struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {};
@@ -783,7 +877,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                goto put_out;
        }
 
-       ret = set->type->create(set, tb, flags);
+       ret = set->type->create(net, set, tb, flags);
        if (ret != 0)
                goto put_out;
 
@@ -794,7 +888,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
         * by the nfnl mutex. Find the first free index in ip_set_list
         * and check clashing.
         */
-       ret = find_free_id(set->name, &index, &clash);
+       ret = find_free_id(inst, set->name, &index, &clash);
        if (ret == -EEXIST) {
                /* If this is the same set and requested, ignore error */
                if ((flags & IPSET_FLAG_EXIST) &&
@@ -807,9 +901,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                goto cleanup;
        } else if (ret == -IPSET_ERR_MAX_SETS) {
                struct ip_set **list, **tmp;
-               ip_set_id_t i = ip_set_max + IP_SET_INC;
+               ip_set_id_t i = inst->ip_set_max + IP_SET_INC;
 
-               if (i < ip_set_max || i == IPSET_INVALID_ID)
+               if (i < inst->ip_set_max || i == IPSET_INVALID_ID)
                        /* Wraparound */
                        goto cleanup;
 
@@ -817,14 +911,14 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                if (!list)
                        goto cleanup;
                /* nfnl mutex is held, both lists are valid */
-               tmp = nfnl_dereference(ip_set_list);
-               memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max);
-               rcu_assign_pointer(ip_set_list, list);
+               tmp = nfnl_dereference(inst->ip_set_list);
+               memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
+               rcu_assign_pointer(inst->ip_set_list, list);
                /* Make sure all current packets have passed through */
                synchronize_net();
                /* Use new list */
-               index = ip_set_max;
-               ip_set_max = i;
+               index = inst->ip_set_max;
+               inst->ip_set_max = i;
                kfree(tmp);
                ret = 0;
        } else if (ret)
@@ -834,7 +928,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
         * Finally! Add our shiny new set to the list, and be done.
         */
        pr_debug("create: '%s' created with index %u!\n", set->name, index);
-       nfnl_set(index) = set;
+       nfnl_set(inst, index) = set;
 
        return ret;
 
@@ -857,12 +951,12 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
 };
 
 static void
-ip_set_destroy_set(ip_set_id_t index)
+ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index)
 {
-       struct ip_set *set = nfnl_set(index);
+       struct ip_set *set = nfnl_set(inst, index);
 
        pr_debug("set: %s\n",  set->name);
-       nfnl_set(index) = NULL;
+       nfnl_set(inst, index) = NULL;
 
        /* Must call it without holding any lock */
        set->variant->destroy(set);
@@ -875,6 +969,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
               const struct nlmsghdr *nlh,
               const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *s;
        ip_set_id_t i;
        int ret = 0;
@@ -894,21 +989,22 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
         */
        read_lock_bh(&ip_set_ref_lock);
        if (!attr[IPSET_ATTR_SETNAME]) {
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL && s->ref) {
                                ret = -IPSET_ERR_BUSY;
                                goto out;
                        }
                }
                read_unlock_bh(&ip_set_ref_lock);
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL)
-                               ip_set_destroy_set(i);
+                               ip_set_destroy_set(inst, i);
                }
        } else {
-               s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i);
+               s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+                                   &i);
                if (s == NULL) {
                        ret = -ENOENT;
                        goto out;
@@ -918,7 +1014,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
                }
                read_unlock_bh(&ip_set_ref_lock);
 
-               ip_set_destroy_set(i);
+               ip_set_destroy_set(inst, i);
        }
        return 0;
 out:
@@ -943,6 +1039,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh,
             const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *s;
        ip_set_id_t i;
 
@@ -950,13 +1047,13 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
                return -IPSET_ERR_PROTOCOL;
 
        if (!attr[IPSET_ATTR_SETNAME]) {
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL)
                                ip_set_flush_set(s);
                }
        } else {
-               s = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+               s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
                if (s == NULL)
                        return -ENOENT;
 
@@ -982,6 +1079,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set, *s;
        const char *name2;
        ip_set_id_t i;
@@ -992,7 +1090,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME2] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1003,8 +1101,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
        }
 
        name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
-       for (i = 0; i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s != NULL && STREQ(s->name, name2)) {
                        ret = -IPSET_ERR_EXIST_SETNAME2;
                        goto out;
@@ -1031,6 +1129,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *from, *to;
        ip_set_id_t from_id, to_id;
        char from_name[IPSET_MAXNAMELEN];
@@ -1040,11 +1139,13 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME2] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id);
+       from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+                              &from_id);
        if (from == NULL)
                return -ENOENT;
 
-       to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id);
+       to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]),
+                            &to_id);
        if (to == NULL)
                return -IPSET_ERR_EXIST_SETNAME2;
 
@@ -1061,8 +1162,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
 
        write_lock_bh(&ip_set_ref_lock);
        swap(from->ref, to->ref);
-       nfnl_set(from_id) = to;
-       nfnl_set(to_id) = from;
+       nfnl_set(inst, from_id) = to;
+       nfnl_set(inst, to_id) = from;
        write_unlock_bh(&ip_set_ref_lock);
 
        return 0;
@@ -1081,9 +1182,10 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
 static int
 ip_set_dump_done(struct netlink_callback *cb)
 {
+       struct ip_set_net *inst = (struct ip_set_net *)cb->data;
        if (cb->args[2]) {
-               pr_debug("release set %s\n", nfnl_set(cb->args[1])->name);
-               ip_set_put_byindex((ip_set_id_t) cb->args[1]);
+               pr_debug("release set %s\n", nfnl_set(inst, cb->args[1])->name);
+               __ip_set_put_byindex(inst, (ip_set_id_t) cb->args[1]);
        }
        return 0;
 }
@@ -1109,6 +1211,7 @@ dump_init(struct netlink_callback *cb)
        struct nlattr *attr = (void *)nlh + min_len;
        u32 dump_type;
        ip_set_id_t index;
+       struct ip_set_net *inst = (struct ip_set_net *)cb->data;
 
        /* Second pass, so parser can't fail */
        nla_parse(cda, IPSET_ATTR_CMD_MAX,
@@ -1122,7 +1225,7 @@ dump_init(struct netlink_callback *cb)
        if (cda[IPSET_ATTR_SETNAME]) {
                struct ip_set *set;
 
-               set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]),
+               set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
                                      &index);
                if (set == NULL)
                        return -ENOENT;
@@ -1150,6 +1253,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
        unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0;
        u32 dump_type, dump_flags;
        int ret = 0;
+       struct ip_set_net *inst = (struct ip_set_net *)cb->data;
 
        if (!cb->args[0]) {
                ret = dump_init(cb);
@@ -1163,18 +1267,18 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
                }
        }
 
-       if (cb->args[1] >= ip_set_max)
+       if (cb->args[1] >= inst->ip_set_max)
                goto out;
 
        dump_type = DUMP_TYPE(cb->args[0]);
        dump_flags = DUMP_FLAGS(cb->args[0]);
-       max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+       max = dump_type == DUMP_ONE ? cb->args[1] + 1 : inst->ip_set_max;
 dump_last:
        pr_debug("args[0]: %u %u args[1]: %ld\n",
                 dump_type, dump_flags, cb->args[1]);
        for (; cb->args[1] < max; cb->args[1]++) {
                index = (ip_set_id_t) cb->args[1];
-               set = nfnl_set(index);
+               set = nfnl_set(inst, index);
                if (set == NULL) {
                        if (dump_type == DUMP_ONE) {
                                ret = -ENOENT;
@@ -1252,8 +1356,8 @@ next_set:
 release_refcount:
        /* If there was an error or set is done, release set */
        if (ret || !cb->args[2]) {
-               pr_debug("release set %s\n", nfnl_set(index)->name);
-               ip_set_put_byindex(index);
+               pr_debug("release set %s\n", nfnl_set(inst, index)->name);
+               __ip_set_put_byindex(inst, index);
                cb->args[2] = 0;
        }
 out:
@@ -1271,6 +1375,8 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+
        if (unlikely(protocol_failed(attr)))
                return -IPSET_ERR_PROTOCOL;
 
@@ -1278,6 +1384,7 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
                struct netlink_dump_control c = {
                        .dump = ip_set_dump_start,
                        .done = ip_set_dump_done,
+                       .data = (void *)inst
                };
                return netlink_dump_start(ctnl, skb, nlh, &c);
        }
@@ -1356,6 +1463,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        const struct nlattr *nla;
@@ -1374,7 +1482,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
                       attr[IPSET_ATTR_LINENO] == NULL))))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1410,6 +1518,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        const struct nlattr *nla;
@@ -1428,7 +1537,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
                       attr[IPSET_ATTR_LINENO] == NULL))))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1464,6 +1573,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh,
             const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        int ret = 0;
@@ -1474,7 +1584,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
                     !flag_nested(attr[IPSET_ATTR_DATA])))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1499,6 +1609,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        const struct ip_set *set;
        struct sk_buff *skb2;
        struct nlmsghdr *nlh2;
@@ -1508,7 +1619,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1733,8 +1844,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
        unsigned int *op;
        void *data;
        int copylen = *len, ret = 0;
+       struct net *net = sock_net(sk);
+       struct ip_set_net *inst = ip_set_pernet(net);
 
-       if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+       if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
        if (optval != SO_IP_SET)
                return -EBADF;
@@ -1783,22 +1896,39 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
                }
                req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
                nfnl_lock(NFNL_SUBSYS_IPSET);
-               find_set_and_id(req_get->set.name, &id);
+               find_set_and_id(inst, req_get->set.name, &id);
                req_get->set.index = id;
                nfnl_unlock(NFNL_SUBSYS_IPSET);
                goto copy;
        }
+       case IP_SET_OP_GET_FNAME: {
+               struct ip_set_req_get_set_family *req_get = data;
+               ip_set_id_t id;
+
+               if (*len != sizeof(struct ip_set_req_get_set_family)) {
+                       ret = -EINVAL;
+                       goto done;
+               }
+               req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
+               nfnl_lock(NFNL_SUBSYS_IPSET);
+               find_set_and_id(inst, req_get->set.name, &id);
+               req_get->set.index = id;
+               if (id != IPSET_INVALID_ID)
+                       req_get->family = nfnl_set(inst, id)->family;
+               nfnl_unlock(NFNL_SUBSYS_IPSET);
+               goto copy;
+       }
        case IP_SET_OP_GET_BYINDEX: {
                struct ip_set_req_get_set *req_get = data;
                struct ip_set *set;
 
                if (*len != sizeof(struct ip_set_req_get_set) ||
-                   req_get->set.index >= ip_set_max) {
+                   req_get->set.index >= inst->ip_set_max) {
                        ret = -EINVAL;
                        goto done;
                }
                nfnl_lock(NFNL_SUBSYS_IPSET);
-               set = nfnl_set(req_get->set.index);
+               set = nfnl_set(inst, req_get->set.index);
                strncpy(req_get->set.name, set ? set->name : "",
                        IPSET_MAXNAMELEN);
                nfnl_unlock(NFNL_SUBSYS_IPSET);
@@ -1827,49 +1957,82 @@ static struct nf_sockopt_ops so_set __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
-static int __init
-ip_set_init(void)
+static int __net_init
+ip_set_net_init(struct net *net)
 {
+       struct ip_set_net *inst = ip_set_pernet(net);
+
        struct ip_set **list;
-       int ret;
 
-       if (max_sets)
-               ip_set_max = max_sets;
-       if (ip_set_max >= IPSET_INVALID_ID)
-               ip_set_max = IPSET_INVALID_ID - 1;
+       inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX;
+       if (inst->ip_set_max >= IPSET_INVALID_ID)
+               inst->ip_set_max = IPSET_INVALID_ID - 1;
 
-       list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
+       list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL);
        if (!list)
                return -ENOMEM;
+       inst->is_deleted = 0;
+       rcu_assign_pointer(inst->ip_set_list, list);
+       pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+       return 0;
+}
+
+static void __net_exit
+ip_set_net_exit(struct net *net)
+{
+       struct ip_set_net *inst = ip_set_pernet(net);
+
+       struct ip_set *set = NULL;
+       ip_set_id_t i;
+
+       inst->is_deleted = 1; /* flag for ip_set_nfnl_put */
+
+       for (i = 0; i < inst->ip_set_max; i++) {
+               set = nfnl_set(inst, i);
+               if (set != NULL)
+                       ip_set_destroy_set(inst, i);
+       }
+       kfree(rcu_dereference_protected(inst->ip_set_list, 1));
+}
+
+static struct pernet_operations ip_set_net_ops = {
+       .init   = ip_set_net_init,
+       .exit   = ip_set_net_exit,
+       .id     = &ip_set_net_id,
+       .size   = sizeof(struct ip_set_net)
+};
+
 
-       rcu_assign_pointer(ip_set_list, list);
-       ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
+static int __init
+ip_set_init(void)
+{
+       int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
        if (ret != 0) {
                pr_err("ip_set: cannot register with nfnetlink.\n");
-               kfree(list);
                return ret;
        }
        ret = nf_register_sockopt(&so_set);
        if (ret != 0) {
                pr_err("SO_SET registry failed: %d\n", ret);
                nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
-               kfree(list);
                return ret;
        }
-
-       pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+       ret = register_pernet_subsys(&ip_set_net_ops);
+       if (ret) {
+               pr_err("ip_set: cannot register pernet_subsys.\n");
+               nf_unregister_sockopt(&so_set);
+               nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
+               return ret;
+       }
        return 0;
 }
 
 static void __exit
 ip_set_fini(void)
 {
-       struct ip_set **list = rcu_dereference_protected(ip_set_list, 1);
-
-       /* There can't be any existing set */
+       unregister_pernet_subsys(&ip_set_net_ops);
        nf_unregister_sockopt(&so_set);
        nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
-       kfree(list);
        pr_debug("these are the famous last words\n");
 }
 
index dac156f819ac2f2fe25c876d800aadd2c47c0752..29fb01ddff93b0a0da7ad1dc3691a141376f5007 100644 (file)
@@ -102,9 +102,25 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
        int protocol = iph->protocol;
 
        /* See comments at tcp_match in ip_tables.c */
-       if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET))
+       if (protocol <= 0)
                return false;
 
+       if (ntohs(iph->frag_off) & IP_OFFSET)
+               switch (protocol) {
+               case IPPROTO_TCP:
+               case IPPROTO_SCTP:
+               case IPPROTO_UDP:
+               case IPPROTO_UDPLITE:
+               case IPPROTO_ICMP:
+                       /* Port info not available for fragment offset > 0 */
+                       return false;
+               default:
+                       /* Other protocols doesn't have ports,
+                          so we can match fragments */
+                       *proto = protocol;
+                       return true;
+               }
+
        return get_port(skb, protocol, protooff, src, port, proto);
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
index 707bc520d629f311dd9978d2e0bf222719f71c82..6a80dbd30df7bea89d0279a5752faa668f2f68a7 100644 (file)
@@ -15,8 +15,7 @@
 #define rcu_dereference_bh(p)  rcu_dereference(p)
 #endif
 
-#define CONCAT(a, b)           a##b
-#define TOKEN(a, b)            CONCAT(a, b)
+#define rcu_dereference_bh_nfnl(p)     rcu_dereference_bh_check(p, 1)
 
 /* Hashing which uses arrays to resolve clashing. The hash table is resized
  * (doubled) when searching becomes too long.
@@ -78,10 +77,14 @@ struct htable {
 
 #define hbucket(h, i)          (&((h)->bucket[i]))
 
+#ifndef IPSET_NET_COUNT
+#define IPSET_NET_COUNT                1
+#endif
+
 /* Book-keeping of the prefixes added to the set */
 struct net_prefixes {
-       u8 cidr;                /* the different cidr values in the set */
-       u32 nets;               /* number of elements per cidr */
+       u32 nets[IPSET_NET_COUNT]; /* number of elements per cidr */
+       u8 cidr[IPSET_NET_COUNT];  /* the different cidr values in the set */
 };
 
 /* Compute the hash table size */
@@ -114,23 +117,6 @@ htable_bits(u32 hashsize)
        return bits;
 }
 
-/* Destroy the hashtable part of the set */
-static void
-ahash_destroy(struct htable *t)
-{
-       struct hbucket *n;
-       u32 i;
-
-       for (i = 0; i < jhash_size(t->htable_bits); i++) {
-               n = hbucket(t, i);
-               if (n->size)
-                       /* FIXME: use slab cache */
-                       kfree(n->value);
-       }
-
-       ip_set_free(t);
-}
-
 static int
 hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 {
@@ -156,30 +142,30 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 }
 
 #ifdef IP_SET_HASH_WITH_NETS
+#if IPSET_NET_COUNT > 1
+#define __CIDR(cidr, i)                (cidr[i])
+#else
+#define __CIDR(cidr, i)                (cidr)
+#endif
 #ifdef IP_SET_HASH_WITH_NETS_PACKED
 /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
-#define CIDR(cidr)             (cidr + 1)
+#define CIDR(cidr, i)          (__CIDR(cidr, i) + 1)
 #else
-#define CIDR(cidr)             (cidr)
+#define CIDR(cidr, i)          (__CIDR(cidr, i))
 #endif
 
 #define SET_HOST_MASK(family)  (family == AF_INET ? 32 : 128)
 
 #ifdef IP_SET_HASH_WITH_MULTI
-#define NETS_LENGTH(family)    (SET_HOST_MASK(family) + 1)
+#define NLEN(family)           (SET_HOST_MASK(family) + 1)
 #else
-#define NETS_LENGTH(family)    SET_HOST_MASK(family)
+#define NLEN(family)           SET_HOST_MASK(family)
 #endif
 
 #else
-#define NETS_LENGTH(family)    0
+#define NLEN(family)           0
 #endif /* IP_SET_HASH_WITH_NETS */
 
-#define ext_timeout(e, h)      \
-(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, h)      \
-(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER])
-
 #endif /* _IP_SET_HASH_GEN_H */
 
 /* Family dependent templates */
@@ -194,6 +180,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 #undef mtype_data_next
 #undef mtype_elem
 
+#undef mtype_ahash_destroy
+#undef mtype_ext_cleanup
 #undef mtype_add_cidr
 #undef mtype_del_cidr
 #undef mtype_ahash_memsize
@@ -220,41 +208,44 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 
 #undef HKEY
 
-#define mtype_data_equal       TOKEN(MTYPE, _data_equal)
+#define mtype_data_equal       IPSET_TOKEN(MTYPE, _data_equal)
 #ifdef IP_SET_HASH_WITH_NETS
-#define mtype_do_data_match    TOKEN(MTYPE, _do_data_match)
+#define mtype_do_data_match    IPSET_TOKEN(MTYPE, _do_data_match)
 #else
 #define mtype_do_data_match(d) 1
 #endif
-#define mtype_data_set_flags   TOKEN(MTYPE, _data_set_flags)
-#define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags)
-#define mtype_data_netmask     TOKEN(MTYPE, _data_netmask)
-#define mtype_data_list                TOKEN(MTYPE, _data_list)
-#define mtype_data_next                TOKEN(MTYPE, _data_next)
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_add_cidr         TOKEN(MTYPE, _add_cidr)
-#define mtype_del_cidr         TOKEN(MTYPE, _del_cidr)
-#define mtype_ahash_memsize    TOKEN(MTYPE, _ahash_memsize)
-#define mtype_flush            TOKEN(MTYPE, _flush)
-#define mtype_destroy          TOKEN(MTYPE, _destroy)
-#define mtype_gc_init          TOKEN(MTYPE, _gc_init)
-#define mtype_same_set         TOKEN(MTYPE, _same_set)
-#define mtype_kadt             TOKEN(MTYPE, _kadt)
-#define mtype_uadt             TOKEN(MTYPE, _uadt)
+#define mtype_data_set_flags   IPSET_TOKEN(MTYPE, _data_set_flags)
+#define mtype_data_reset_elem  IPSET_TOKEN(MTYPE, _data_reset_elem)
+#define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags)
+#define mtype_data_netmask     IPSET_TOKEN(MTYPE, _data_netmask)
+#define mtype_data_list                IPSET_TOKEN(MTYPE, _data_list)
+#define mtype_data_next                IPSET_TOKEN(MTYPE, _data_next)
+#define mtype_elem             IPSET_TOKEN(MTYPE, _elem)
+#define mtype_ahash_destroy    IPSET_TOKEN(MTYPE, _ahash_destroy)
+#define mtype_ext_cleanup      IPSET_TOKEN(MTYPE, _ext_cleanup)
+#define mtype_add_cidr         IPSET_TOKEN(MTYPE, _add_cidr)
+#define mtype_del_cidr         IPSET_TOKEN(MTYPE, _del_cidr)
+#define mtype_ahash_memsize    IPSET_TOKEN(MTYPE, _ahash_memsize)
+#define mtype_flush            IPSET_TOKEN(MTYPE, _flush)
+#define mtype_destroy          IPSET_TOKEN(MTYPE, _destroy)
+#define mtype_gc_init          IPSET_TOKEN(MTYPE, _gc_init)
+#define mtype_same_set         IPSET_TOKEN(MTYPE, _same_set)
+#define mtype_kadt             IPSET_TOKEN(MTYPE, _kadt)
+#define mtype_uadt             IPSET_TOKEN(MTYPE, _uadt)
 #define mtype                  MTYPE
 
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_add              TOKEN(MTYPE, _add)
-#define mtype_del              TOKEN(MTYPE, _del)
-#define mtype_test_cidrs       TOKEN(MTYPE, _test_cidrs)
-#define mtype_test             TOKEN(MTYPE, _test)
-#define mtype_expire           TOKEN(MTYPE, _expire)
-#define mtype_resize           TOKEN(MTYPE, _resize)
-#define mtype_head             TOKEN(MTYPE, _head)
-#define mtype_list             TOKEN(MTYPE, _list)
-#define mtype_gc               TOKEN(MTYPE, _gc)
-#define mtype_variant          TOKEN(MTYPE, _variant)
-#define mtype_data_match       TOKEN(MTYPE, _data_match)
+#define mtype_elem             IPSET_TOKEN(MTYPE, _elem)
+#define mtype_add              IPSET_TOKEN(MTYPE, _add)
+#define mtype_del              IPSET_TOKEN(MTYPE, _del)
+#define mtype_test_cidrs       IPSET_TOKEN(MTYPE, _test_cidrs)
+#define mtype_test             IPSET_TOKEN(MTYPE, _test)
+#define mtype_expire           IPSET_TOKEN(MTYPE, _expire)
+#define mtype_resize           IPSET_TOKEN(MTYPE, _resize)
+#define mtype_head             IPSET_TOKEN(MTYPE, _head)
+#define mtype_list             IPSET_TOKEN(MTYPE, _list)
+#define mtype_gc               IPSET_TOKEN(MTYPE, _gc)
+#define mtype_variant          IPSET_TOKEN(MTYPE, _variant)
+#define mtype_data_match       IPSET_TOKEN(MTYPE, _data_match)
 
 #ifndef HKEY_DATALEN
 #define HKEY_DATALEN           sizeof(struct mtype_elem)
@@ -269,13 +260,10 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 
 /* The generic hash structure */
 struct htype {
-       struct htable *table;   /* the hash table */
+       struct htable __rcu *table; /* the hash table */
        u32 maxelem;            /* max elements in the hash */
        u32 elements;           /* current element (vs timeout) */
        u32 initval;            /* random jhash init value */
-       u32 timeout;            /* timeout value, if enabled */
-       size_t dsize;           /* data struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        struct timer_list gc;   /* garbage collection when timeout enabled */
        struct mtype_elem next; /* temporary storage for uadd */
 #ifdef IP_SET_HASH_WITH_MULTI
@@ -297,49 +285,49 @@ struct htype {
 /* Network cidr size book keeping when the hash stores different
  * sized networks */
 static void
-mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length)
+mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 {
        int i, j;
 
        /* Add in increasing prefix order, so larger cidr first */
-       for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) {
+       for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) {
                if (j != -1)
                        continue;
-               else if (h->nets[i].cidr < cidr)
+               else if (h->nets[i].cidr[n] < cidr)
                        j = i;
-               else if (h->nets[i].cidr == cidr) {
-                       h->nets[i].nets++;
+               else if (h->nets[i].cidr[n] == cidr) {
+                       h->nets[i].nets[n]++;
                        return;
                }
        }
        if (j != -1) {
                for (; i > j; i--) {
-                       h->nets[i].cidr = h->nets[i - 1].cidr;
-                       h->nets[i].nets = h->nets[i - 1].nets;
+                       h->nets[i].cidr[n] = h->nets[i - 1].cidr[n];
+                       h->nets[i].nets[n] = h->nets[i - 1].nets[n];
                }
        }
-       h->nets[i].cidr = cidr;
-       h->nets[i].nets = 1;
+       h->nets[i].cidr[n] = cidr;
+       h->nets[i].nets[n] = 1;
 }
 
 static void
-mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
+mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 {
        u8 i, j, net_end = nets_length - 1;
 
        for (i = 0; i < nets_length; i++) {
-               if (h->nets[i].cidr != cidr)
+               if (h->nets[i].cidr[n] != cidr)
                        continue;
-                if (h->nets[i].nets > 1 || i == net_end ||
-                    h->nets[i + 1].nets == 0) {
-                        h->nets[i].nets--;
+                if (h->nets[i].nets[n] > 1 || i == net_end ||
+                    h->nets[i + 1].nets[n] == 0) {
+                        h->nets[i].nets[n]--;
                         return;
                 }
-                for (j = i; j < net_end && h->nets[j].nets; j++) {
-                       h->nets[j].cidr = h->nets[j + 1].cidr;
-                       h->nets[j].nets = h->nets[j + 1].nets;
+                for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
+                       h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
+                       h->nets[j].nets[n] = h->nets[j + 1].nets[n];
                 }
-                h->nets[j].nets = 0;
+                h->nets[j].nets[n] = 0;
                 return;
        }
 }
@@ -347,10 +335,10 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
 
 /* Calculate the actual memory size of the set data */
 static size_t
-mtype_ahash_memsize(const struct htype *h, u8 nets_length)
+mtype_ahash_memsize(const struct htype *h, const struct htable *t,
+                   u8 nets_length, size_t dsize)
 {
        u32 i;
-       struct htable *t = h->table;
        size_t memsize = sizeof(*h)
                         + sizeof(*t)
 #ifdef IP_SET_HASH_WITH_NETS
@@ -359,35 +347,70 @@ mtype_ahash_memsize(const struct htype *h, u8 nets_length)
                         + jhash_size(t->htable_bits) * sizeof(struct hbucket);
 
        for (i = 0; i < jhash_size(t->htable_bits); i++)
-               memsize += t->bucket[i].size * h->dsize;
+               memsize += t->bucket[i].size * dsize;
 
        return memsize;
 }
 
+/* Get the ith element from the array block n */
+#define ahash_data(n, i, dsize)        \
+       ((struct mtype_elem *)((n)->value + ((i) * (dsize))))
+
+static void
+mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
+{
+       int i;
+
+       for (i = 0; i < n->pos; i++)
+               ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
+}
+
 /* Flush a hash type of set: destroy all elements */
 static void
 mtype_flush(struct ip_set *set)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        struct hbucket *n;
        u32 i;
 
+       t = rcu_dereference_bh_nfnl(h->table);
        for (i = 0; i < jhash_size(t->htable_bits); i++) {
                n = hbucket(t, i);
                if (n->size) {
+                       if (set->extensions & IPSET_EXT_DESTROY)
+                               mtype_ext_cleanup(set, n);
                        n->size = n->pos = 0;
                        /* FIXME: use slab cache */
                        kfree(n->value);
                }
        }
 #ifdef IP_SET_HASH_WITH_NETS
-       memset(h->nets, 0, sizeof(struct net_prefixes)
-                          * NETS_LENGTH(set->family));
+       memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family));
 #endif
        h->elements = 0;
 }
 
+/* Destroy the hashtable part of the set */
+static void
+mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
+{
+       struct hbucket *n;
+       u32 i;
+
+       for (i = 0; i < jhash_size(t->htable_bits); i++) {
+               n = hbucket(t, i);
+               if (n->size) {
+                       if (set->extensions & IPSET_EXT_DESTROY && ext_destroy)
+                               mtype_ext_cleanup(set, n);
+                       /* FIXME: use slab cache */
+                       kfree(n->value);
+               }
+       }
+
+       ip_set_free(t);
+}
+
 /* Destroy a hash type of set */
 static void
 mtype_destroy(struct ip_set *set)
@@ -397,7 +420,7 @@ mtype_destroy(struct ip_set *set)
        if (set->extensions & IPSET_EXT_TIMEOUT)
                del_timer_sync(&h->gc);
 
-       ahash_destroy(h->table);
+       mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table), true);
 #ifdef IP_SET_HASH_WITH_RBTREE
        rbtree_destroy(&h->rbtree);
 #endif
@@ -414,10 +437,10 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&h->gc);
        h->gc.data = (unsigned long) set;
        h->gc.function = gc;
-       h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+       h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&h->gc);
        pr_debug("gc initialized, run in every %u\n",
-                IPSET_GC_PERIOD(h->timeout));
+                IPSET_GC_PERIOD(set->timeout));
 }
 
 static bool
@@ -428,37 +451,40 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
 
        /* Resizing changes htable_bits, so we ignore it */
        return x->maxelem == y->maxelem &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
 #ifdef IP_SET_HASH_WITH_NETMASK
               x->netmask == y->netmask &&
 #endif
               a->extensions == b->extensions;
 }
 
-/* Get the ith element from the array block n */
-#define ahash_data(n, i, dsize)        \
-       ((struct mtype_elem *)((n)->value + ((i) * (dsize))))
-
 /* Delete expired elements from the hashtable */
 static void
-mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
+mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
 {
-       struct htable *t = h->table;
+       struct htable *t;
        struct hbucket *n;
        struct mtype_elem *data;
        u32 i;
        int j;
+#ifdef IP_SET_HASH_WITH_NETS
+       u8 k;
+#endif
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
        for (i = 0; i < jhash_size(t->htable_bits); i++) {
                n = hbucket(t, i);
                for (j = 0; j < n->pos; j++) {
                        data = ahash_data(n, j, dsize);
-                       if (ip_set_timeout_expired(ext_timeout(data, h))) {
+                       if (ip_set_timeout_expired(ext_timeout(data, set))) {
                                pr_debug("expired %u/%u\n", i, j);
 #ifdef IP_SET_HASH_WITH_NETS
-                               mtype_del_cidr(h, CIDR(data->cidr),
-                                              nets_length);
+                               for (k = 0; k < IPSET_NET_COUNT; k++)
+                                       mtype_del_cidr(h, CIDR(data->cidr, k),
+                                                      nets_length, k);
 #endif
+                               ip_set_ext_destroy(set, data);
                                if (j != n->pos - 1)
                                        /* Not last one */
                                        memcpy(data,
@@ -481,6 +507,7 @@ mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
                        n->value = tmp;
                }
        }
+       rcu_read_unlock_bh();
 }
 
 static void
@@ -491,10 +518,10 @@ mtype_gc(unsigned long ul_set)
 
        pr_debug("called\n");
        write_lock_bh(&set->lock);
-       mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+       mtype_expire(set, h, NLEN(set->family), set->dsize);
        write_unlock_bh(&set->lock);
 
-       h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+       h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&h->gc);
 }
 
@@ -505,7 +532,7 @@ static int
 mtype_resize(struct ip_set *set, bool retried)
 {
        struct htype *h = set->data;
-       struct htable *t, *orig = h->table;
+       struct htable *t, *orig = rcu_dereference_bh_nfnl(h->table);
        u8 htable_bits = orig->htable_bits;
 #ifdef IP_SET_HASH_WITH_NETS
        u8 flags;
@@ -520,8 +547,7 @@ mtype_resize(struct ip_set *set, bool retried)
        if (SET_WITH_TIMEOUT(set) && !retried) {
                i = h->elements;
                write_lock_bh(&set->lock);
-               mtype_expire(set->data, NETS_LENGTH(set->family),
-                            h->dsize);
+               mtype_expire(set, set->data, NLEN(set->family), set->dsize);
                write_unlock_bh(&set->lock);
                if (h->elements < i)
                        return 0;
@@ -548,25 +574,25 @@ retry:
        for (i = 0; i < jhash_size(orig->htable_bits); i++) {
                n = hbucket(orig, i);
                for (j = 0; j < n->pos; j++) {
-                       data = ahash_data(n, j, h->dsize);
+                       data = ahash_data(n, j, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
                        flags = 0;
                        mtype_data_reset_flags(data, &flags);
 #endif
                        m = hbucket(t, HKEY(data, h->initval, htable_bits));
-                       ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize);
+                       ret = hbucket_elem_add(m, AHASH_MAX(h), set->dsize);
                        if (ret < 0) {
 #ifdef IP_SET_HASH_WITH_NETS
                                mtype_data_reset_flags(data, &flags);
 #endif
                                read_unlock_bh(&set->lock);
-                               ahash_destroy(t);
+                               mtype_ahash_destroy(set, t, false);
                                if (ret == -EAGAIN)
                                        goto retry;
                                return ret;
                        }
-                       d = ahash_data(m, m->pos++, h->dsize);
-                       memcpy(d, data, h->dsize);
+                       d = ahash_data(m, m->pos++, set->dsize);
+                       memcpy(d, data, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
                        mtype_data_reset_flags(d, &flags);
 #endif
@@ -581,7 +607,7 @@ retry:
 
        pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
                 orig->htable_bits, orig, t->htable_bits, t);
-       ahash_destroy(orig);
+       mtype_ahash_destroy(set, orig, false);
 
        return 0;
 }
@@ -604,7 +630,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 
        if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
                /* FIXME: when set is full, we slow down here */
-               mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+               mtype_expire(set, h, NLEN(set->family), set->dsize);
 
        if (h->elements >= h->maxelem) {
                if (net_ratelimit())
@@ -618,11 +644,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        key = HKEY(value, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (mtype_data_equal(data, d, &multi)) {
                        if (flag_exist ||
                            (SET_WITH_TIMEOUT(set) &&
-                            ip_set_timeout_expired(ext_timeout(data, h)))) {
+                            ip_set_timeout_expired(ext_timeout(data, set)))) {
                                /* Just the extensions could be overwritten */
                                j = i;
                                goto reuse_slot;
@@ -633,30 +659,37 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                }
                /* Reuse first timed out entry */
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(data, h)) &&
+                   ip_set_timeout_expired(ext_timeout(data, set)) &&
                    j != AHASH_MAX(h) + 1)
                        j = i;
        }
 reuse_slot:
        if (j != AHASH_MAX(h) + 1) {
                /* Fill out reused slot */
-               data = ahash_data(n, j, h->dsize);
+               data = ahash_data(n, j, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family));
-               mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (i = 0; i < IPSET_NET_COUNT; i++) {
+                       mtype_del_cidr(h, CIDR(data->cidr, i),
+                                      NLEN(set->family), i);
+                       mtype_add_cidr(h, CIDR(d->cidr, i),
+                                      NLEN(set->family), i);
+               }
 #endif
+               ip_set_ext_destroy(set, data);
        } else {
                /* Use/create a new slot */
                TUNE_AHASH_MAX(h, multi);
-               ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize);
+               ret = hbucket_elem_add(n, AHASH_MAX(h), set->dsize);
                if (ret != 0) {
                        if (ret == -EAGAIN)
                                mtype_data_next(&h->next, d);
                        goto out;
                }
-               data = ahash_data(n, n->pos++, h->dsize);
+               data = ahash_data(n, n->pos++, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (i = 0; i < IPSET_NET_COUNT; i++)
+                       mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family),
+                                      i);
 #endif
                h->elements++;
        }
@@ -665,9 +698,11 @@ reuse_slot:
        mtype_data_set_flags(data, flags);
 #endif
        if (SET_WITH_TIMEOUT(set))
-               ip_set_timeout_set(ext_timeout(data, h), ext->timeout);
+               ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(data, h), ext);
+               ip_set_init_counter(ext_counter(data, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(data, set), ext);
 
 out:
        rcu_read_unlock_bh();
@@ -682,47 +717,60 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
          struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        const struct mtype_elem *d = value;
        struct mtype_elem *data;
        struct hbucket *n;
-       int i;
+       int i, ret = -IPSET_ERR_EXIST;
+#ifdef IP_SET_HASH_WITH_NETS
+       u8 j;
+#endif
        u32 key, multi = 0;
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
        key = HKEY(value, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (!mtype_data_equal(data, d, &multi))
                        continue;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(data, h)))
-                       return -IPSET_ERR_EXIST;
+                   ip_set_timeout_expired(ext_timeout(data, set)))
+                       goto out;
                if (i != n->pos - 1)
                        /* Not last one */
-                       memcpy(data, ahash_data(n, n->pos - 1, h->dsize),
-                              h->dsize);
+                       memcpy(data, ahash_data(n, n->pos - 1, set->dsize),
+                              set->dsize);
 
                n->pos--;
                h->elements--;
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (j = 0; j < IPSET_NET_COUNT; j++)
+                       mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family),
+                                      j);
 #endif
+               ip_set_ext_destroy(set, data);
                if (n->pos + AHASH_INIT_SIZE < n->size) {
                        void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
-                                           * h->dsize,
+                                           * set->dsize,
                                            GFP_ATOMIC);
-                       if (!tmp)
-                               return 0;
+                       if (!tmp) {
+                               ret = 0;
+                               goto out;
+                       }
                        n->size -= AHASH_INIT_SIZE;
-                       memcpy(tmp, n->value, n->size * h->dsize);
+                       memcpy(tmp, n->value, n->size * set->dsize);
                        kfree(n->value);
                        n->value = tmp;
                }
-               return 0;
+               ret = 0;
+               goto out;
        }
 
-       return -IPSET_ERR_EXIST;
+out:
+       rcu_read_unlock_bh();
+       return ret;
 }
 
 static inline int
@@ -730,8 +778,7 @@ mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext,
                 struct ip_set_ext *mext, struct ip_set *set, u32 flags)
 {
        if (SET_WITH_COUNTER(set))
-               ip_set_update_counter(ext_counter(data,
-                                                 (struct htype *)(set->data)),
+               ip_set_update_counter(ext_counter(data, set),
                                      ext, mext, flags);
        return mtype_do_data_match(data);
 }
@@ -745,25 +792,38 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
                 struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t = rcu_dereference_bh(h->table);
        struct hbucket *n;
        struct mtype_elem *data;
+#if IPSET_NET_COUNT == 2
+       struct mtype_elem orig = *d;
+       int i, j = 0, k;
+#else
        int i, j = 0;
+#endif
        u32 key, multi = 0;
-       u8 nets_length = NETS_LENGTH(set->family);
+       u8 nets_length = NLEN(set->family);
 
        pr_debug("test by nets\n");
-       for (; j < nets_length && h->nets[j].nets && !multi; j++) {
-               mtype_data_netmask(d, h->nets[j].cidr);
+       for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) {
+#if IPSET_NET_COUNT == 2
+               mtype_data_reset_elem(d, &orig);
+               mtype_data_netmask(d, h->nets[j].cidr[0], false);
+               for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi;
+                    k++) {
+                       mtype_data_netmask(d, h->nets[k].cidr[1], true);
+#else
+               mtype_data_netmask(d, h->nets[j].cidr[0]);
+#endif
                key = HKEY(d, h->initval, t->htable_bits);
                n = hbucket(t, key);
                for (i = 0; i < n->pos; i++) {
-                       data = ahash_data(n, i, h->dsize);
+                       data = ahash_data(n, i, set->dsize);
                        if (!mtype_data_equal(data, d, &multi))
                                continue;
                        if (SET_WITH_TIMEOUT(set)) {
                                if (!ip_set_timeout_expired(
-                                                       ext_timeout(data, h)))
+                                               ext_timeout(data, set)))
                                        return mtype_data_match(data, ext,
                                                                mext, set,
                                                                flags);
@@ -774,6 +834,9 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
                                return mtype_data_match(data, ext,
                                                        mext, set, flags);
                }
+#if IPSET_NET_COUNT == 2
+               }
+#endif
        }
        return 0;
 }
@@ -785,30 +848,41 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
           struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        struct mtype_elem *d = value;
        struct hbucket *n;
        struct mtype_elem *data;
-       int i;
+       int i, ret = 0;
        u32 key, multi = 0;
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
 #ifdef IP_SET_HASH_WITH_NETS
        /* If we test an IP address and not a network address,
         * try all possible network sizes */
-       if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
-               return mtype_test_cidrs(set, d, ext, mext, flags);
+       for (i = 0; i < IPSET_NET_COUNT; i++)
+               if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family))
+                       break;
+       if (i == IPSET_NET_COUNT) {
+               ret = mtype_test_cidrs(set, d, ext, mext, flags);
+               goto out;
+       }
 #endif
 
        key = HKEY(d, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (mtype_data_equal(data, d, &multi) &&
                    !(SET_WITH_TIMEOUT(set) &&
-                     ip_set_timeout_expired(ext_timeout(data, h))))
-                       return mtype_data_match(data, ext, mext, set, flags);
+                     ip_set_timeout_expired(ext_timeout(data, set)))) {
+                       ret = mtype_data_match(data, ext, mext, set, flags);
+                       goto out;
+               }
        }
-       return 0;
+out:
+       rcu_read_unlock_bh();
+       return ret;
 }
 
 /* Reply a HEADER request: fill out the header part of the set */
@@ -816,18 +890,18 @@ static int
 mtype_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct htype *h = set->data;
+       const struct htable *t;
        struct nlattr *nested;
        size_t memsize;
 
-       read_lock_bh(&set->lock);
-       memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family));
-       read_unlock_bh(&set->lock);
+       t = rcu_dereference_bh_nfnl(h->table);
+       memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize);
 
        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)
                goto nla_put_failure;
        if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
-                         htonl(jhash_size(h->table->htable_bits))) ||
+                         htonl(jhash_size(t->htable_bits))) ||
            nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
                goto nla_put_failure;
 #ifdef IP_SET_HASH_WITH_NETMASK
@@ -836,12 +910,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
                goto nla_put_failure;
 #endif
        if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-           nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
-           ((set->extensions & IPSET_EXT_TIMEOUT) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) ||
-           ((set->extensions & IPSET_EXT_COUNTER) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))))
+           nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -856,7 +927,7 @@ mtype_list(const struct ip_set *set,
           struct sk_buff *skb, struct netlink_callback *cb)
 {
        const struct htype *h = set->data;
-       const struct htable *t = h->table;
+       const struct htable *t = rcu_dereference_bh_nfnl(h->table);
        struct nlattr *atd, *nested;
        const struct hbucket *n;
        const struct mtype_elem *e;
@@ -874,9 +945,9 @@ mtype_list(const struct ip_set *set,
                n = hbucket(t, cb->args[2]);
                pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
                for (i = 0; i < n->pos; i++) {
-                       e = ahash_data(n, i, h->dsize);
+                       e = ahash_data(n, i, set->dsize);
                        if (SET_WITH_TIMEOUT(set) &&
-                           ip_set_timeout_expired(ext_timeout(e, h)))
+                           ip_set_timeout_expired(ext_timeout(e, set)))
                                continue;
                        pr_debug("list hash %lu hbucket %p i %u, data %p\n",
                                 cb->args[2], n, i, e);
@@ -890,13 +961,7 @@ mtype_list(const struct ip_set *set,
                        }
                        if (mtype_data_list(skb, e))
                                goto nla_put_failure;
-                       if (SET_WITH_TIMEOUT(set) &&
-                           nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_get(
-                                               ext_timeout(e, h)))))
-                               goto nla_put_failure;
-                       if (SET_WITH_COUNTER(set) &&
-                           ip_set_put_counter(skb, ext_counter(e, h)))
+                       if (ip_set_put_extensions(skb, set, e, true))
                                goto nla_put_failure;
                        ipset_nest_end(skb, nested);
                }
@@ -909,24 +974,24 @@ mtype_list(const struct ip_set *set,
 
 nla_put_failure:
        nlmsg_trim(skb, incomplete);
-       ipset_nest_end(skb, atd);
        if (unlikely(first == cb->args[2])) {
                pr_warning("Can't list set %s: one bucket does not fit into "
                           "a message. Please report it!\n", set->name);
                cb->args[2] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, atd);
        return 0;
 }
 
 static int
-TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
-             const struct xt_action_param *par,
-             enum ipset_adt adt, struct ip_set_adt_opt *opt);
+IPSET_TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
+           const struct xt_action_param *par,
+           enum ipset_adt adt, struct ip_set_adt_opt *opt);
 
 static int
-TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
-             enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
+IPSET_TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
+           enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
 
 static const struct ip_set_type_variant mtype_variant = {
        .kadt   = mtype_kadt,
@@ -946,16 +1011,17 @@ static const struct ip_set_type_variant mtype_variant = {
 
 #ifdef IP_SET_EMIT_CREATE
 static int
-TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
+IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
+                           struct nlattr *tb[], u32 flags)
 {
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-       u32 cadt_flags = 0;
        u8 hbits;
 #ifdef IP_SET_HASH_WITH_NETMASK
        u8 netmask;
 #endif
        size_t hsize;
        struct HTYPE *h;
+       struct htable *t;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -1005,7 +1071,7 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->netmask = netmask;
 #endif
        get_random_bytes(&h->initval, sizeof(h->initval));
-       h->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
        hsize = htable_size(hbits);
@@ -1013,91 +1079,37 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
                kfree(h);
                return -ENOMEM;
        }
-       h->table = ip_set_alloc(hsize);
-       if (!h->table) {
+       t = ip_set_alloc(hsize);
+       if (!t) {
                kfree(h);
                return -ENOMEM;
        }
-       h->table->htable_bits = hbits;
+       t->htable_bits = hbits;
+       rcu_assign_pointer(h->table, t);
 
        set->data = h;
-       if (set->family ==  NFPROTO_IPV4)
-               set->variant = &TOKEN(HTYPE, 4_variant);
-       else
-               set->variant = &TOKEN(HTYPE, 6_variant);
-
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       h->timeout =
-                               ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       if (set->family == NFPROTO_IPV4) {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 4ct_elem));
-                               h->offset[IPSET_OFFSET_TIMEOUT] =
-                                       offsetof(struct TOKEN(HTYPE, 4ct_elem),
-                                                timeout);
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 4ct_elem),
-                                                counter);
-                               TOKEN(HTYPE, 4_gc_init)(set,
-                                       TOKEN(HTYPE, 4_gc));
-                       } else {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 6ct_elem));
-                               h->offset[IPSET_OFFSET_TIMEOUT] =
-                                       offsetof(struct TOKEN(HTYPE, 6ct_elem),
-                                                timeout);
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 6ct_elem),
-                                                counter);
-                               TOKEN(HTYPE, 6_gc_init)(set,
-                                       TOKEN(HTYPE, 6_gc));
-                       }
-               } else {
-                       if (set->family == NFPROTO_IPV4) {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 4c_elem));
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 4c_elem),
-                                                counter);
-                       } else {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 6c_elem));
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 6c_elem),
-                                                counter);
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
-               if (set->family == NFPROTO_IPV4) {
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem));
-                       h->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct TOKEN(HTYPE, 4t_elem),
-                                        timeout);
-                       TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc));
-               } else {
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem));
-                       h->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct TOKEN(HTYPE, 6t_elem),
-                                        timeout);
-                       TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc));
-               }
+       if (set->family == NFPROTO_IPV4) {
+               set->variant = &IPSET_TOKEN(HTYPE, 4_variant);
+               set->dsize = ip_set_elem_len(set, tb,
+                               sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)));
        } else {
+               set->variant = &IPSET_TOKEN(HTYPE, 6_variant);
+               set->dsize = ip_set_elem_len(set, tb,
+                               sizeof(struct IPSET_TOKEN(HTYPE, 6_elem)));
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                if (set->family == NFPROTO_IPV4)
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem));
+                       IPSET_TOKEN(HTYPE, 4_gc_init)(set,
+                               IPSET_TOKEN(HTYPE, 4_gc));
                else
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem));
+                       IPSET_TOKEN(HTYPE, 6_gc_init)(set,
+                               IPSET_TOKEN(HTYPE, 6_gc));
        }
 
        pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-                set->name, jhash_size(h->table->htable_bits),
-                h->table->htable_bits, h->maxelem, set->data, h->table);
+                set->name, jhash_size(t->htable_bits),
+                t->htable_bits, h->maxelem, set->data, t);
 
        return 0;
 }
index c74e6e14cd933f4f20bbd5496312fdd0056b7e52..e65fc2423d56dd2b21cee513786eec41ceabefc9 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counters support */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counters support */
+#define IPSET_TYPE_REV_MAX     2       /* Comments support */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ip
 #define IP_SET_HASH_WITH_NETMASK
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ip4_elem {
@@ -43,22 +44,6 @@ struct hash_ip4_elem {
        __be32 ip;
 };
 
-struct hash_ip4t_elem {
-       __be32 ip;
-       unsigned long timeout;
-};
-
-struct hash_ip4c_elem {
-       __be32 ip;
-       struct ip_set_counter counter;
-};
-
-struct hash_ip4ct_elem {
-       __be32 ip;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -99,7 +84,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip4_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        __be32 ip;
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
@@ -118,8 +103,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip4_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, hosts;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, hosts;
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -178,29 +163,13 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 /* Member elements */
 struct hash_ip6_elem {
        union nf_inet_addr ip;
 };
 
-struct hash_ip6t_elem {
-       union nf_inet_addr ip;
-       unsigned long timeout;
-};
-
-struct hash_ip6c_elem {
-       union nf_inet_addr ip;
-       struct ip_set_counter counter;
-};
-
-struct hash_ip6ct_elem {
-       union nf_inet_addr ip;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -253,7 +222,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip6_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
        hash_ip6_netmask(&e.ip, h->netmask);
@@ -270,7 +239,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip6_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -304,8 +273,8 @@ static struct ip_set_type hash_ip_type __read_mostly = {
        .features       = IPSET_TYPE_IP,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ip_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -324,6 +293,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 7a2d2bd98d046f7b9eec8bc10d099c3794a1e0f6..525a595dd1fe4bf0efe6db7ca9cc06d995c66430 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-#define REVISION_MAX   2 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Counters support added */
+#define IPSET_TYPE_REV_MAX     3 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ipport
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ipport4_elem {
@@ -46,31 +47,6 @@ struct hash_ipport4_elem {
        u8 padding;
 };
 
-struct hash_ipport4t_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipport4c_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipport4ct_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -116,10 +92,9 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
                  const struct xt_action_param *par,
                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -136,8 +111,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip, ip_to = 0, p = 0, port, port_to;
        bool with_ports = false;
        int ret;
 
@@ -222,7 +197,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipport6_elem {
        union nf_inet_addr ip;
@@ -231,31 +206,6 @@ struct hash_ipport6_elem {
        u8 padding;
 };
 
-struct hash_ipport6t_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipport6c_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipport6ct_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -306,10 +256,9 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
                  const struct xt_action_param *par,
                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -326,7 +275,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        int ret;
@@ -396,8 +345,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipport_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -419,6 +368,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 34e8a1acce42219686c897f4f1db30387b733b43..f5636631466eb3ee98509e8b832e46da4ad9a417 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-#define REVISION_MAX   2 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Counters support added */
+#define IPSET_TYPE_REV_MAX     3 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,ip");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ipportip
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements  */
 struct hash_ipportip4_elem {
@@ -47,34 +48,6 @@ struct hash_ipportip4_elem {
        u8 padding;
 };
 
-struct hash_ipportip4t_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipportip4c_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportip4ct_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 static inline bool
 hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
                          const struct hash_ipportip4_elem *ip2,
@@ -120,10 +93,9 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
                    const struct xt_action_param *par,
                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -141,8 +113,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip, ip_to = 0, p = 0, port, port_to;
        bool with_ports = false;
        int ret;
 
@@ -231,7 +203,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipportip6_elem {
        union nf_inet_addr ip;
@@ -241,34 +213,6 @@ struct hash_ipportip6_elem {
        u8 padding;
 };
 
-struct hash_ipportip6t_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipportip6c_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportip6ct_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -319,10 +263,9 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
                    const struct xt_action_param *par,
                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -340,7 +283,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        int ret;
@@ -414,8 +357,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
        .dimension      = IPSET_DIM_THREE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipportip_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -437,6 +380,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index f15f3e28b9c338c6e72d3aa949d9d241c0e7cabe..5d87fe8a41ffa4888a70b6d91897360e30c253bc 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-/*                     2    Range as input support for IPv4 added */
-/*                     3    nomatch flag support added */
-#define REVISION_MAX   4 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Range as input support for IPv4 added */
+/*                             3    nomatch flag support added */
+/*                             4    Counters support added */
+#define IPSET_TYPE_REV_MAX     5 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,net");
 
 /* Type specific function prefix */
@@ -46,7 +47,7 @@ MODULE_ALIAS("ip_set_hash:ip,port,net");
 #define IP_SET_HASH_WITH_PROTO
 #define IP_SET_HASH_WITH_NETS
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ipportnet4_elem {
@@ -58,37 +59,6 @@ struct hash_ipportnet4_elem {
        u8 proto;
 };
 
-struct hash_ipportnet4t_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       unsigned long timeout;
-};
-
-struct hash_ipportnet4c_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportnet4ct_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -170,9 +140,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -195,9 +165,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
-       u32 ip2_from, ip2_to, ip2_last, ip2;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, p = 0, port, port_to;
+       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -272,7 +242,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                if (ip > ip_to)
                        swap(ip, ip_to);
        } else if (tb[IPSET_ATTR_CIDR]) {
-               u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
                if (!cidr || cidr > 32)
                        return -IPSET_ERR_INVALID_CIDR;
@@ -306,9 +276,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                                                       : port;
                for (; p <= port_to; p++) {
                        e.port = htons(p);
-                       ip2 = retried
-                             && ip == ntohl(h->next.ip)
-                             && p == ntohs(h->next.port)
+                       ip2 = retried &&
+                             ip == ntohl(h->next.ip) &&
+                             p == ntohs(h->next.port)
                                ? ntohl(h->next.ip2) : ip2_from;
                        while (!after(ip2, ip2_to)) {
                                e.ip2 = htonl(ip2);
@@ -328,7 +298,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipportnet6_elem {
        union nf_inet_addr ip;
@@ -339,37 +309,6 @@ struct hash_ipportnet6_elem {
        u8 proto;
 };
 
-struct hash_ipportnet6t_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       unsigned long timeout;
-};
-
-struct hash_ipportnet6c_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportnet6ct_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -454,9 +393,9 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -479,7 +418,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        u8 cidr;
@@ -574,8 +513,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
                          IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_THREE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipportnet_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -600,6 +539,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 223e9f546d0fa3e414b7a53a839b56d6835093bb..8295cf4f9fdcfdb3d3da4b72792add9fc83156ad 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    Range as input support for IPv4 added */
-/*                     2    nomatch flag support added */
-#define REVISION_MAX   3 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    Range as input support for IPv4 added */
+/*                             2    nomatch flag support added */
+/*                             3    Counters support added */
+#define IPSET_TYPE_REV_MAX     4 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net");
 
 /* Type specific function prefix */
 #define HTYPE          hash_net
 #define IP_SET_HASH_WITH_NETS
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements  */
 struct hash_net4_elem {
@@ -46,31 +47,6 @@ struct hash_net4_elem {
        u8 cidr;
 };
 
-struct hash_net4t_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       unsigned long timeout;
-};
-
-struct hash_net4c_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-};
-
-struct hash_net4ct_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -143,9 +119,9 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (e.cidr == 0)
                return -EINVAL;
@@ -165,8 +141,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = { .cidr = HOST_MASK };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -228,7 +204,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_net6_elem {
        union nf_inet_addr ip;
@@ -237,31 +213,6 @@ struct hash_net6_elem {
        u8 cidr;
 };
 
-struct hash_net6t_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       unsigned long timeout;
-};
-
-struct hash_net6c_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-};
-
-struct hash_net6ct_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -338,9 +289,9 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (e.cidr == 0)
                return -EINVAL;
@@ -357,10 +308,9 @@ static int
 hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
               enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-       const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net6_elem e = { .cidr = HOST_MASK };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -406,8 +356,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_net_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -425,6 +375,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 7d798d5d5cd30a66de3d6fade0b4110eeba583f7..3f64a66bf5d9b78bfa551124cbc7bd3d7d8a8857 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    nomatch flag support added */
-/*                     2    /0 support added */
-#define REVISION_MAX   3 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    nomatch flag support added */
+/*                             2    /0 support added */
+/*                             3    Counters support added */
+#define IPSET_TYPE_REV_MAX     4 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net,iface");
 
 /* Interface name rbtree */
@@ -134,7 +135,7 @@ iface_add(struct rb_root *root, const char **iface)
 
 #define STREQ(a, b)    (strcmp(a, b) == 0)
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 struct hash_netiface4_elem_hashed {
        __be32 ip;
@@ -144,7 +145,7 @@ struct hash_netiface4_elem_hashed {
        u8 elem;
 };
 
-/* Member elements without timeout */
+/* Member elements */
 struct hash_netiface4_elem {
        __be32 ip;
        u8 physdev;
@@ -154,37 +155,6 @@ struct hash_netiface4_elem {
        const char *iface;
 };
 
-struct hash_netiface4t_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       unsigned long timeout;
-};
-
-struct hash_netiface4c_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-};
-
-struct hash_netiface4ct_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -265,10 +235,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        int ret;
 
        if (e.cidr == 0)
@@ -319,8 +289,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
        char iface[IFNAMSIZ];
        int ret;
 
@@ -399,7 +369,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_netiface6_elem_hashed {
        union nf_inet_addr ip;
@@ -418,37 +388,6 @@ struct hash_netiface6_elem {
        const char *iface;
 };
 
-struct hash_netiface6t_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       unsigned long timeout;
-};
-
-struct hash_netiface6c_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-};
-
-struct hash_netiface6ct_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -534,10 +473,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        int ret;
 
        if (e.cidr == 0)
@@ -584,7 +523,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        char iface[IFNAMSIZ];
        int ret;
 
@@ -645,8 +584,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
                          IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_netiface_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -668,6 +607,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
new file mode 100644 (file)
index 0000000..4260327
--- /dev/null
@@ -0,0 +1,483 @@
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+ *
+ * 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.
+ */
+
+/* Kernel module implementing an IP set type: the hash:net type */
+
+#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ipset/pfxlen.h>
+#include <linux/netfilter/ipset/ip_set.h>
+#include <linux/netfilter/ipset/ip_set_hash.h>
+
+#define IPSET_TYPE_REV_MIN     0
+#define IPSET_TYPE_REV_MAX     0
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
+IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
+MODULE_ALIAS("ip_set_hash:net,net");
+
+/* Type specific function prefix */
+#define HTYPE          hash_netnet
+#define IP_SET_HASH_WITH_NETS
+#define IPSET_NET_COUNT 2
+
+/* IPv4 variants */
+
+/* Member elements  */
+struct hash_netnet4_elem {
+       union {
+               __be32 ip[2];
+               __be64 ipcmp;
+       };
+       u8 nomatch;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+};
+
+/* Common functions */
+
+static inline bool
+hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1,
+                    const struct hash_netnet4_elem *ip2,
+                    u32 *multi)
+{
+       return ip1->ipcmp == ip2->ipcmp &&
+              ip2->ccmp == ip2->ccmp;
+}
+
+static inline int
+hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags)
+{
+       elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
+}
+
+static inline void
+hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem,
+                         struct hash_netnet4_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner)
+{
+       if (inner) {
+               elem->ip[1] &= ip_set_netmask(cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               elem->ip[0] &= ip_set_netmask(cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netnet4_data_list(struct sk_buff *skb,
+                   const struct hash_netnet4_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
+           nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netnet4_data_next(struct hash_netnet4_elem *next,
+                   const struct hash_netnet4_elem *d)
+{
+       next->ipcmp = d->ipcmp;
+}
+
+#define MTYPE          hash_netnet4
+#define PF             4
+#define HOST_MASK      32
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
+              const struct xt_action_param *par,
+              enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet4_elem e = {
+               .cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK,
+               .cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK,
+       };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
+
+       ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
+       e.ip[0] &= ip_set_netmask(e.cidr[0]);
+       e.ip[1] &= ip_set_netmask(e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
+              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet4_elem e = { .cidr[0] = HOST_MASK,
+                                      .cidr[1] = HOST_MASK };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
+       u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2;
+       u8 cidr, cidr2;
+       int ret;
+
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+             ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[0] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_CIDR2]) {
+               cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr2 || cidr2 > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[1] = cidr2;
+       }
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] &&
+                                  tb[IPSET_ATTR_IP2_TO])) {
+               e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
+               e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       ip_to = ip;
+       if (tb[IPSET_ATTR_IP_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+               if (ret)
+                       return ret;
+               if (ip_to < ip)
+                       swap(ip, ip_to);
+               if (ip + UINT_MAX == ip_to)
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       ip2_to = ip2_from;
+       if (tb[IPSET_ATTR_IP2_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
+               if (ret)
+                       return ret;
+               if (ip2_to < ip2_from)
+                       swap(ip2_from, ip2_to);
+               if (ip2_from + UINT_MAX == ip2_to)
+                       return -IPSET_ERR_HASH_RANGE;
+
+       }
+
+       if (retried)
+               ip = ntohl(h->next.ip[0]);
+
+       while (!after(ip, ip_to)) {
+               e.ip[0] = htonl(ip);
+               last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               e.cidr[0] = cidr;
+               ip2 = (retried &&
+                      ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1])
+                                                  : ip2_from;
+               while (!after(ip2, ip2_to)) {
+                       e.ip[1] = htonl(ip2);
+                       last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2);
+                       e.cidr[1] = cidr2;
+                       ret = adtfn(set, &e, &ext, &ext, flags);
+                       if (ret && !ip_set_eexist(ret, flags))
+                               return ret;
+                       else
+                               ret = 0;
+                       ip2 = last2 + 1;
+               }
+               ip = last + 1;
+       }
+       return ret;
+}
+
+/* IPv6 variants */
+
+struct hash_netnet6_elem {
+       union nf_inet_addr ip[2];
+       u8 nomatch;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+};
+
+/* Common functions */
+
+static inline bool
+hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1,
+                    const struct hash_netnet6_elem *ip2,
+                    u32 *multi)
+{
+       return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
+              ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
+              ip1->ccmp == ip2->ccmp;
+}
+
+static inline int
+hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags)
+{
+       elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
+}
+
+static inline void
+hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem,
+                         struct hash_netnet6_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner)
+{
+       if (inner) {
+               ip6_netmask(&elem->ip[1], cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               ip6_netmask(&elem->ip[0], cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netnet6_data_list(struct sk_buff *skb,
+                   const struct hash_netnet6_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
+           nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netnet6_data_next(struct hash_netnet4_elem *next,
+                   const struct hash_netnet6_elem *d)
+{
+}
+
+#undef MTYPE
+#undef PF
+#undef HOST_MASK
+
+#define MTYPE          hash_netnet6
+#define PF             6
+#define HOST_MASK      128
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
+              const struct xt_action_param *par,
+              enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet6_elem e = {
+               .cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK,
+               .cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK
+       };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK;
+
+       ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6);
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
+              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet6_elem e = { .cidr[0] = HOST_MASK,
+                                      .cidr[1] = HOST_MASK };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       int ret;
+
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+       if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO]))
+               return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
+             ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR])
+               e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+       if (tb[IPSET_ATTR_CIDR2])
+               e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+       if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
+           e.cidr[1] > HOST_MASK)
+               return -IPSET_ERR_INVALID_CIDR;
+
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       ret = adtfn(set, &e, &ext, &ext, flags);
+
+       return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+              ip_set_eexist(ret, flags) ? 0 : ret;
+}
+
+static struct ip_set_type hash_netnet_type __read_mostly = {
+       .name           = "hash:net,net",
+       .protocol       = IPSET_PROTOCOL,
+       .features       = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH,
+       .dimension      = IPSET_DIM_TWO,
+       .family         = NFPROTO_UNSPEC,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
+       .create         = hash_netnet_create,
+       .create_policy  = {
+               [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
+               [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
+               [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
+               [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+       },
+       .adt_policy     = {
+               [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2]        = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2_TO]     = { .type = NLA_NESTED },
+               [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
+               [IPSET_ATTR_CIDR2]      = { .type = NLA_U8 },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
+               [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init
+hash_netnet_init(void)
+{
+       return ip_set_type_register(&hash_netnet_type);
+}
+
+static void __exit
+hash_netnet_fini(void)
+{
+       ip_set_type_unregister(&hash_netnet_type);
+}
+
+module_init(hash_netnet_init);
+module_exit(hash_netnet_fini);
index 09d6690bee6fd33891c4a00bbbb909f2bbf7731b..7097fb0141bf6e1363ca0b0342451e66c34773b4 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-/*                     2    Range as input support for IPv4 added */
-/*                     3    nomatch flag support added */
-#define REVISION_MAX   4 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Range as input support for IPv4 added */
+/*                             3    nomatch flag support added */
+/*                             4    Counters support added */
+#define IPSET_TYPE_REV_MAX     5 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net,port");
 
 /* Type specific function prefix */
@@ -45,7 +46,7 @@ MODULE_ALIAS("ip_set_hash:net,port");
  */
 #define IP_SET_HASH_WITH_NETS_PACKED
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_netport4_elem {
@@ -56,34 +57,6 @@ struct hash_netport4_elem {
        u8 nomatch:1;
 };
 
-struct hash_netport4t_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       unsigned long timeout;
-};
-
-struct hash_netport4c_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-};
-
-struct hash_netport4ct_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -162,9 +135,9 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -186,8 +159,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 port, port_to, p = 0, ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 port, port_to, p = 0, ip = 0, ip_to = 0, last;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -287,7 +260,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_netport6_elem {
        union nf_inet_addr ip;
@@ -297,34 +270,6 @@ struct hash_netport6_elem {
        u8 nomatch:1;
 };
 
-struct hash_netport6t_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       unsigned long timeout;
-};
-
-struct hash_netport6c_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-};
-
-struct hash_netport6ct_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -407,9 +352,9 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -431,7 +376,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem e = { .cidr = HOST_MASK  - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        u8 cidr;
@@ -518,8 +463,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_netport_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -542,6 +487,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
new file mode 100644 (file)
index 0000000..363fab9
--- /dev/null
@@ -0,0 +1,588 @@
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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.
+ */
+
+/* Kernel module implementing an IP set type: the hash:ip,port,net type */
+
+#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ipset/pfxlen.h>
+#include <linux/netfilter/ipset/ip_set.h>
+#include <linux/netfilter/ipset/ip_set_getport.h>
+#include <linux/netfilter/ipset/ip_set_hash.h>
+
+#define IPSET_TYPE_REV_MIN     0
+#define IPSET_TYPE_REV_MAX     0 /* Comments support added */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
+IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
+MODULE_ALIAS("ip_set_hash:net,port,net");
+
+/* Type specific function prefix */
+#define HTYPE          hash_netportnet
+#define IP_SET_HASH_WITH_PROTO
+#define IP_SET_HASH_WITH_NETS
+#define IPSET_NET_COUNT 2
+
+/* IPv4 variant */
+
+/* Member elements */
+struct hash_netportnet4_elem {
+       union {
+               __be32 ip[2];
+               __be64 ipcmp;
+       };
+       __be16 port;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+       u8 nomatch:1;
+       u8 proto;
+};
+
+/* Common functions */
+
+static inline bool
+hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1,
+                          const struct hash_netportnet4_elem *ip2,
+                          u32 *multi)
+{
+       return ip1->ipcmp == ip2->ipcmp &&
+              ip1->ccmp == ip2->ccmp &&
+              ip1->port == ip2->port &&
+              ip1->proto == ip2->proto;
+}
+
+static inline int
+hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags)
+{
+       elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
+}
+
+static inline void
+hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem,
+                               struct hash_netportnet4_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem,
+                             u8 cidr, bool inner)
+{
+       if (inner) {
+               elem->ip[1] &= ip_set_netmask(cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               elem->ip[0] &= ip_set_netmask(cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netportnet4_data_list(struct sk_buff *skb,
+                         const struct hash_netportnet4_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
+           nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
+           nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netportnet4_data_next(struct hash_netportnet4_elem *next,
+                         const struct hash_netportnet4_elem *d)
+{
+       next->ipcmp = d->ipcmp;
+       next->port = d->port;
+}
+
+#define MTYPE          hash_netportnet4
+#define PF             4
+#define HOST_MASK      32
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
+                    const struct xt_action_param *par,
+                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet4_elem e = {
+               .cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK),
+       };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
+
+       if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
+                                &e.port, &e.proto))
+               return -EINVAL;
+
+       ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]);
+       e.ip[0] &= ip_set_netmask(e.cidr[0]);
+       e.ip[1] &= ip_set_netmask(e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
+                    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet4_elem e = { .cidr[0] = HOST_MASK,
+                                          .cidr[1] = HOST_MASK };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to;
+       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+       bool with_ports = false;
+       u8 cidr, cidr2;
+       int ret;
+
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+             ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[0] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_CIDR2]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[1] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_PORT])
+               e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+       else
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_PROTO]) {
+               e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(e.proto);
+
+               if (e.proto == 0)
+                       return -IPSET_ERR_INVALID_PROTO;
+       } else
+               return -IPSET_ERR_MISSING_PROTO;
+
+       if (!(with_ports || e.proto == IPPROTO_ICMP))
+               e.port = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+       if (adt == IPSET_TEST ||
+           !(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) {
+               e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
+               e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       ip_to = ip;
+       if (tb[IPSET_ATTR_IP_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+               if (ret)
+                       return ret;
+               if (ip > ip_to)
+                       swap(ip, ip_to);
+               if (unlikely(ip + UINT_MAX == ip_to))
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       port_to = port = ntohs(e.port);
+       if (tb[IPSET_ATTR_PORT_TO]) {
+               port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+               if (port > port_to)
+                       swap(port, port_to);
+       }
+
+       ip2_to = ip2_from;
+       if (tb[IPSET_ATTR_IP2_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
+               if (ret)
+                       return ret;
+               if (ip2_from > ip2_to)
+                       swap(ip2_from, ip2_to);
+               if (unlikely(ip2_from + UINT_MAX == ip2_to))
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       if (retried)
+               ip = ntohl(h->next.ip[0]);
+
+       while (!after(ip, ip_to)) {
+               e.ip[0] = htonl(ip);
+               ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               e.cidr[0] = cidr;
+               p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port)
+                                                         : port;
+               for (; p <= port_to; p++) {
+                       e.port = htons(p);
+                       ip2 = (retried && ip == ntohl(h->next.ip[0]) &&
+                              p == ntohs(h->next.port)) ? ntohl(h->next.ip[1])
+                                                        : ip2_from;
+                       while (!after(ip2, ip2_to)) {
+                               e.ip[1] = htonl(ip2);
+                               ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
+                                                               &cidr2);
+                               e.cidr[1] = cidr2;
+                               ret = adtfn(set, &e, &ext, &ext, flags);
+                               if (ret && !ip_set_eexist(ret, flags))
+                                       return ret;
+                               else
+                                       ret = 0;
+                               ip2 = ip2_last + 1;
+                       }
+               }
+               ip = ip_last + 1;
+       }
+       return ret;
+}
+
+/* IPv6 variant */
+
+struct hash_netportnet6_elem {
+       union nf_inet_addr ip[2];
+       __be16 port;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+       u8 nomatch:1;
+       u8 proto;
+};
+
+/* Common functions */
+
+static inline bool
+hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1,
+                          const struct hash_netportnet6_elem *ip2,
+                          u32 *multi)
+{
+       return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
+              ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
+              ip1->ccmp == ip2->ccmp &&
+              ip1->port == ip2->port &&
+              ip1->proto == ip2->proto;
+}
+
+static inline int
+hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags)
+{
+       elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
+}
+
+static inline void
+hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem,
+                               struct hash_netportnet6_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem,
+                             u8 cidr, bool inner)
+{
+       if (inner) {
+               ip6_netmask(&elem->ip[1], cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               ip6_netmask(&elem->ip[0], cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netportnet6_data_list(struct sk_buff *skb,
+                         const struct hash_netportnet6_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
+           nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
+           nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netportnet6_data_next(struct hash_netportnet4_elem *next,
+                         const struct hash_netportnet6_elem *d)
+{
+       next->port = d->port;
+}
+
+#undef MTYPE
+#undef PF
+#undef HOST_MASK
+
+#define MTYPE          hash_netportnet6
+#define PF             6
+#define HOST_MASK      128
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
+                    const struct xt_action_param *par,
+                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet6_elem e = {
+               .cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK),
+       };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;
+
+       if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
+                                &e.port, &e.proto))
+               return -EINVAL;
+
+       ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6);
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
+                    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet6_elem e = { .cidr[0] = HOST_MASK,
+                                          .cidr[1] = HOST_MASK };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 port, port_to;
+       bool with_ports = false;
+       int ret;
+
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+       if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO]))
+               return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
+             ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR])
+               e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+       if (tb[IPSET_ATTR_CIDR2])
+               e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+       if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
+                    e.cidr[1] > HOST_MASK))
+               return -IPSET_ERR_INVALID_CIDR;
+
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       if (tb[IPSET_ATTR_PORT])
+               e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+       else
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_PROTO]) {
+               e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(e.proto);
+
+               if (e.proto == 0)
+                       return -IPSET_ERR_INVALID_PROTO;
+       } else
+               return -IPSET_ERR_MISSING_PROTO;
+
+       if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+               e.port = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       port = ntohs(e.port);
+       port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+       if (port > port_to)
+               swap(port, port_to);
+
+       if (retried)
+               port = ntohs(h->next.port);
+       for (; port <= port_to; port++) {
+               e.port = htons(port);
+               ret = adtfn(set, &e, &ext, &ext, flags);
+
+               if (ret && !ip_set_eexist(ret, flags))
+                       return ret;
+               else
+                       ret = 0;
+       }
+       return ret;
+}
+
+static struct ip_set_type hash_netportnet_type __read_mostly = {
+       .name           = "hash:net,port,net",
+       .protocol       = IPSET_PROTOCOL,
+       .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
+                         IPSET_TYPE_NOMATCH,
+       .dimension      = IPSET_DIM_THREE,
+       .family         = NFPROTO_UNSPEC,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
+       .create         = hash_netportnet_create,
+       .create_policy  = {
+               [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
+               [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
+               [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
+               [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+       },
+       .adt_policy     = {
+               [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2]        = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2_TO]     = { .type = NLA_NESTED },
+               [IPSET_ATTR_PORT]       = { .type = NLA_U16 },
+               [IPSET_ATTR_PORT_TO]    = { .type = NLA_U16 },
+               [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
+               [IPSET_ATTR_CIDR2]      = { .type = NLA_U8 },
+               [IPSET_ATTR_PROTO]      = { .type = NLA_U8 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
+               [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
+               [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init
+hash_netportnet_init(void)
+{
+       return ip_set_type_register(&hash_netportnet_type);
+}
+
+static void __exit
+hash_netportnet_fini(void)
+{
+       ip_set_type_unregister(&hash_netportnet_type);
+}
+
+module_init(hash_netportnet_init);
+module_exit(hash_netportnet_fini);
index 979b8c90e42201c656faf914e38b4b30bac2d298..ec6f6d15dded36429ee235196b48e828f7842fbc 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_list.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    Counters support added */
+#define IPSET_TYPE_REV_MAX     2 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_list:set");
 
 /* Member elements  */
@@ -28,28 +29,6 @@ struct set_elem {
        ip_set_id_t id;
 };
 
-struct sett_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-};
-
-struct setc_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-};
-
-struct setct_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 struct set_adt_elem {
        ip_set_id_t id;
        ip_set_id_t refid;
@@ -58,24 +37,14 @@ struct set_adt_elem {
 
 /* Type structure */
 struct list_set {
-       size_t dsize;           /* element size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        u32 size;               /* size of set list array */
-       u32 timeout;            /* timeout value */
        struct timer_list gc;   /* garbage collection */
+       struct net *net;        /* namespace */
        struct set_elem members[0]; /* the set members */
 };
 
-static inline struct set_elem *
-list_set_elem(const struct list_set *map, u32 id)
-{
-       return (struct set_elem *)((void *)map->members + id * map->dsize);
-}
-
-#define ext_timeout(e, m)      \
-(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, m)      \
-(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER])
+#define list_set_elem(set, map, id)    \
+       (struct set_elem *)((void *)(map)->members + (id) * (set)->dsize)
 
 static int
 list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
@@ -92,16 +61,16 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
        if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
                opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_test(e->id, skb, par, opt);
                if (ret > 0) {
                        if (SET_WITH_COUNTER(set))
-                               ip_set_update_counter(ext_counter(e, map),
+                               ip_set_update_counter(ext_counter(e, set),
                                                      ext, &opt->ext,
                                                      cmdflags);
                        return ret;
@@ -121,11 +90,11 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_add(e->id, skb, par, opt);
                if (ret == 0)
@@ -145,11 +114,11 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_del(e->id, skb, par, opt);
                if (ret == 0)
@@ -163,8 +132,7 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
              const struct xt_action_param *par,
              enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       struct list_set *map = set->data;
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        switch (adt) {
        case IPSET_TEST:
@@ -188,10 +156,10 @@ id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
        if (i >= map->size)
                return 0;
 
-       e = list_set_elem(map, i);
+       e = list_set_elem(set, map, i);
        return !!(e->id == id &&
                 !(SET_WITH_TIMEOUT(set) &&
-                  ip_set_timeout_expired(ext_timeout(e, map))));
+                  ip_set_timeout_expired(ext_timeout(e, set))));
 }
 
 static int
@@ -199,28 +167,36 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
             const struct ip_set_ext *ext)
 {
        struct list_set *map = set->data;
-       struct set_elem *e = list_set_elem(map, i);
+       struct set_elem *e = list_set_elem(set, map, i);
 
        if (e->id != IPSET_INVALID_ID) {
-               if (i == map->size - 1)
+               if (i == map->size - 1) {
                        /* Last element replaced: e.g. add new,before,last */
-                       ip_set_put_byindex(e->id);
-               else {
-                       struct set_elem *x = list_set_elem(map, map->size - 1);
+                       ip_set_put_byindex(map->net, e->id);
+                       ip_set_ext_destroy(set, e);
+               } else {
+                       struct set_elem *x = list_set_elem(set, map,
+                                                          map->size - 1);
 
                        /* Last element pushed off */
-                       if (x->id != IPSET_INVALID_ID)
-                               ip_set_put_byindex(x->id);
-                       memmove(list_set_elem(map, i + 1), e,
-                               map->dsize * (map->size - (i + 1)));
+                       if (x->id != IPSET_INVALID_ID) {
+                               ip_set_put_byindex(map->net, x->id);
+                               ip_set_ext_destroy(set, x);
+                       }
+                       memmove(list_set_elem(set, map, i + 1), e,
+                               set->dsize * (map->size - (i + 1)));
+                       /* Extensions must be initialized to zero */
+                       memset(e, 0, set->dsize);
                }
        }
 
        e->id = d->id;
        if (SET_WITH_TIMEOUT(set))
-               ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+               ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(e, map), ext);
+               ip_set_init_counter(ext_counter(e, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(e, set), ext);
        return 0;
 }
 
@@ -228,16 +204,17 @@ static int
 list_set_del(struct ip_set *set, u32 i)
 {
        struct list_set *map = set->data;
-       struct set_elem *e = list_set_elem(map, i);
+       struct set_elem *e = list_set_elem(set, map, i);
 
-       ip_set_put_byindex(e->id);
+       ip_set_put_byindex(map->net, e->id);
+       ip_set_ext_destroy(set, e);
 
        if (i < map->size - 1)
-               memmove(e, list_set_elem(map, i + 1),
-                       map->dsize * (map->size - (i + 1)));
+               memmove(e, list_set_elem(set, map, i + 1),
+                       set->dsize * (map->size - (i + 1)));
 
        /* Last element */
-       e = list_set_elem(map, map->size - 1);
+       e = list_set_elem(set, map, map->size - 1);
        e->id = IPSET_INVALID_ID;
        return 0;
 }
@@ -247,13 +224,16 @@ set_cleanup_entries(struct ip_set *set)
 {
        struct list_set *map = set->data;
        struct set_elem *e;
-       u32 i;
+       u32 i = 0;
 
-       for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+       while (i < map->size) {
+               e = list_set_elem(set, map, i);
                if (e->id != IPSET_INVALID_ID &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        list_set_del(set, i);
+                       /* Check element moved to position i in next loop */
+               else
+                       i++;
        }
 }
 
@@ -268,11 +248,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
+                        ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                else if (e->id != d->id)
                        continue;
@@ -299,14 +279,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        bool flag_exist = flags & IPSET_FLAG_EXIST;
        u32 i, ret = 0;
 
+       if (SET_WITH_TIMEOUT(set))
+               set_cleanup_entries(set);
+
        /* Check already added element */
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        goto insert;
-               else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
-                       continue;
                else if (e->id != d->id)
                        continue;
 
@@ -319,18 +299,22 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                        /* Can't re-add */
                        return -IPSET_ERR_EXIST;
                /* Update extensions */
+               ip_set_ext_destroy(set, e);
+
                if (SET_WITH_TIMEOUT(set))
-                       ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+                       ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
                if (SET_WITH_COUNTER(set))
-                       ip_set_init_counter(ext_counter(e, map), ext);
+                       ip_set_init_counter(ext_counter(e, set), ext);
+               if (SET_WITH_COMMENT(set))
+                       ip_set_init_comment(ext_comment(e, set), ext);
                /* Set is already added to the list */
-               ip_set_put_byindex(d->id);
+               ip_set_put_byindex(map->net, d->id);
                return 0;
        }
 insert:
        ret = -IPSET_ERR_LIST_FULL;
        for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
                                : list_set_add(set, i, d, ext);
@@ -355,12 +339,12 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        u32 i;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return d->before != 0 ? -IPSET_ERR_REF_EXIST
                                              : -IPSET_ERR_EXIST;
                else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
+                        ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                else if (e->id != d->id)
                        continue;
@@ -386,7 +370,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        struct list_set *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        struct ip_set *s;
        int ret = 0;
 
@@ -403,7 +387,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        ret = ip_set_get_extensions(set, tb, &ext);
        if (ret)
                return ret;
-       e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+       e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
        if (e.id == IPSET_INVALID_ID)
                return -IPSET_ERR_NAME;
        /* "Loop detection" */
@@ -423,7 +407,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        }
 
        if (tb[IPSET_ATTR_NAMEREF]) {
-               e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+               e.refid = ip_set_get_byname(map->net,
+                                           nla_data(tb[IPSET_ATTR_NAMEREF]),
                                            &s);
                if (e.refid == IPSET_INVALID_ID) {
                        ret = -IPSET_ERR_NAMEREF;
@@ -439,9 +424,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 
 finish:
        if (e.refid != IPSET_INVALID_ID)
-               ip_set_put_byindex(e.refid);
+               ip_set_put_byindex(map->net, e.refid);
        if (adt != IPSET_ADD || ret)
-               ip_set_put_byindex(e.id);
+               ip_set_put_byindex(map->net, e.id);
 
        return ip_set_eexist(ret, flags) ? 0 : ret;
 }
@@ -454,9 +439,10 @@ list_set_flush(struct ip_set *set)
        u32 i;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id != IPSET_INVALID_ID) {
-                       ip_set_put_byindex(e->id);
+                       ip_set_put_byindex(map->net, e->id);
+                       ip_set_ext_destroy(set, e);
                        e->id = IPSET_INVALID_ID;
                }
        }
@@ -485,14 +471,11 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
        if (!nested)
                goto nla_put_failure;
        if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
-           (SET_WITH_TIMEOUT(set) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
-           (SET_WITH_COUNTER(set) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))) ||
            nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
-                         htonl(sizeof(*map) + map->size * map->dsize)))
+                         htonl(sizeof(*map) + map->size * set->dsize)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -515,11 +498,11 @@ list_set_list(const struct ip_set *set,
                return -EMSGSIZE;
        for (; cb->args[2] < map->size; cb->args[2]++) {
                i = cb->args[2];
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        goto finish;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
                if (!nested) {
@@ -530,15 +513,9 @@ list_set_list(const struct ip_set *set,
                                goto nla_put_failure;
                }
                if (nla_put_string(skb, IPSET_ATTR_NAME,
-                                  ip_set_name_byindex(e->id)))
-                       goto nla_put_failure;
-               if (SET_WITH_TIMEOUT(set) &&
-                   nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                 htonl(ip_set_timeout_get(
-                                               ext_timeout(e, map)))))
+                                  ip_set_name_byindex(map->net, e->id)))
                        goto nla_put_failure;
-               if (SET_WITH_COUNTER(set) &&
-                   ip_set_put_counter(skb, ext_counter(e, map)))
+               if (ip_set_put_extensions(skb, set, e, true))
                        goto nla_put_failure;
                ipset_nest_end(skb, nested);
        }
@@ -550,11 +527,11 @@ finish:
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
-       ipset_nest_end(skb, atd);
        if (unlikely(i == first)) {
                cb->args[2] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, atd);
        return 0;
 }
 
@@ -565,7 +542,7 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
        const struct list_set *y = b->data;
 
        return x->size == y->size &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -594,7 +571,7 @@ list_set_gc(unsigned long ul_set)
        set_cleanup_entries(set);
        write_unlock_bh(&set->lock);
 
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
@@ -606,43 +583,40 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&map->gc);
        map->gc.data = (unsigned long) set;
        map->gc.function = gc;
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
 /* Create list:set type of sets */
 
-static struct list_set *
-init_list_set(struct ip_set *set, u32 size, size_t dsize,
-             unsigned long timeout)
+static bool
+init_list_set(struct net *net, struct ip_set *set, u32 size)
 {
        struct list_set *map;
        struct set_elem *e;
        u32 i;
 
-       map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
+       map = kzalloc(sizeof(*map) + size * set->dsize, GFP_KERNEL);
        if (!map)
-               return NULL;
+               return false;
 
        map->size = size;
-       map->dsize = dsize;
-       map->timeout = timeout;
+       map->net = net;
        set->data = map;
 
        for (i = 0; i < size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                e->id = IPSET_INVALID_ID;
        }
 
-       return map;
+       return true;
 }
 
 static int
-list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+               u32 flags)
 {
-       struct list_set *map;
-       u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0;
-       unsigned long timeout = 0;
+       u32 size = IP_SET_LIST_DEFAULT_SIZE;
 
        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
@@ -654,45 +628,13 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        if (size < IP_SET_LIST_MIN_SIZE)
                size = IP_SET_LIST_MIN_SIZE;
 
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (tb[IPSET_ATTR_TIMEOUT])
-               timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        set->variant = &set_variant;
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map = init_list_set(set, size,
-                                       sizeof(struct setct_elem), timeout);
-                       if (!map)
-                               return -ENOMEM;
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct setct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct setct_elem, counter);
-                       list_set_gc_init(set, list_set_gc);
-               } else {
-                       map = init_list_set(set, size,
-                                           sizeof(struct setc_elem), 0);
-                       if (!map)
-                               return -ENOMEM;
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct setc_elem, counter);
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map = init_list_set(set, size,
-                                   sizeof(struct sett_elem), timeout);
-               if (!map)
-                       return -ENOMEM;
-               set->extensions |= IPSET_EXT_TIMEOUT;
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct sett_elem, timeout);
+       set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem));
+       if (!init_list_set(net, set, size))
+               return -ENOMEM;
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                list_set_gc_init(set, list_set_gc);
-       } else {
-               map = init_list_set(set, size, sizeof(struct set_elem), 0);
-               if (!map)
-                       return -ENOMEM;
        }
        return 0;
 }
@@ -703,8 +645,8 @@ static struct ip_set_type list_set_type __read_mostly = {
        .features       = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = list_set_create,
        .create_policy  = {
                [IPSET_ATTR_SIZE]       = { .type = NLA_U32 },
@@ -721,6 +663,7 @@ static struct ip_set_type list_set_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index e0c4373b47478d4d72899d95166566db34a8bcfa..466410eaa482c2a3940d3768351e92be17a644ef 100644 (file)
@@ -52,66 +52,8 @@ module_param(sip_direct_media, int, 0600);
 MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
                                   "endpoints only (default 1)");
 
-unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff,
-                               unsigned int dataoff, const char **dptr,
-                               unsigned int *datalen) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
-
-void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff,
-                                  s16 off) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook);
-
-unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
-                                      unsigned int protoff,
-                                      unsigned int dataoff,
-                                      const char **dptr,
-                                      unsigned int *datalen,
-                                      struct nf_conntrack_expect *exp,
-                                      unsigned int matchoff,
-                                      unsigned int matchlen) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
-
-unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff,
-                                    unsigned int dataoff,
-                                    const char **dptr,
-                                    unsigned int *datalen,
-                                    unsigned int sdpoff,
-                                    enum sdp_header_types type,
-                                    enum sdp_header_types term,
-                                    const union nf_inet_addr *addr)
-                                    __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
-
-unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff,
-                                    unsigned int dataoff,
-                                    const char **dptr,
-                                    unsigned int *datalen,
-                                    unsigned int matchoff,
-                                    unsigned int matchlen,
-                                    u_int16_t port) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);
-
-unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
-                                       unsigned int protoff,
-                                       unsigned int dataoff,
-                                       const char **dptr,
-                                       unsigned int *datalen,
-                                       unsigned int sdpoff,
-                                       const union nf_inet_addr *addr)
-                                       __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook);
-
-unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff,
-                                     unsigned int dataoff,
-                                     const char **dptr,
-                                     unsigned int *datalen,
-                                     struct nf_conntrack_expect *rtp_exp,
-                                     struct nf_conntrack_expect *rtcp_exp,
-                                     unsigned int mediaoff,
-                                     unsigned int medialen,
-                                     union nf_inet_addr *rtp_addr)
-                                     __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook);
+const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
+EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
 
 static int string_len(const struct nf_conn *ct, const char *dptr,
                      const char *limit, int *shift)
@@ -914,8 +856,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
        int direct_rtp = 0, skip_expect = 0, ret = NF_DROP;
        u_int16_t base_port;
        __be16 rtp_port, rtcp_port;
-       typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port;
-       typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
+       const struct nf_nat_sip_hooks *hooks;
 
        saddr = NULL;
        if (sip_direct_media) {
@@ -966,22 +907,23 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
 #endif
                        skip_expect = 1;
        } while (!skip_expect);
-       rcu_read_unlock();
 
        base_port = ntohs(tuple.dst.u.udp.port) & ~1;
        rtp_port = htons(base_port);
        rtcp_port = htons(base_port + 1);
 
        if (direct_rtp) {
-               nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook);
-               if (nf_nat_sdp_port &&
-                   !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks &&
+                   !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen,
                                     mediaoff, medialen, ntohs(rtp_port)))
                        goto err1;
        }
 
-       if (skip_expect)
+       if (skip_expect) {
+               rcu_read_unlock();
                return NF_ACCEPT;
+       }
 
        rtp_exp = nf_ct_expect_alloc(ct);
        if (rtp_exp == NULL)
@@ -995,10 +937,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
        nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr,
                          IPPROTO_UDP, NULL, &rtcp_port);
 
-       nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook);
-       if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp)
-               ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen,
-                                      rtp_exp, rtcp_exp,
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp)
+               ret = hooks->sdp_media(skb, protoff, dataoff, dptr,
+                                      datalen, rtp_exp, rtcp_exp,
                                       mediaoff, medialen, daddr);
        else {
                if (nf_ct_expect_related(rtp_exp) == 0) {
@@ -1012,6 +954,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
 err2:
        nf_ct_expect_put(rtp_exp);
 err1:
+       rcu_read_unlock();
        return ret;
 }
 
@@ -1051,13 +994,12 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
        unsigned int caddr_len, maddr_len;
        unsigned int i;
        union nf_inet_addr caddr, maddr, rtp_addr;
+       const struct nf_nat_sip_hooks *hooks;
        unsigned int port;
        const struct sdp_media_type *t;
        int ret = NF_ACCEPT;
-       typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
-       typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
 
-       nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
 
        /* Find beginning of session description */
        if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
@@ -1125,10 +1067,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
                }
 
                /* Update media connection address if present */
-               if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
-                       ret = nf_nat_sdp_addr(skb, protoff, dataoff,
+               if (maddr_len && hooks && ct->status & IPS_NAT_MASK) {
+                       ret = hooks->sdp_addr(skb, protoff, dataoff,
                                              dptr, datalen, mediaoff,
-                                             SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
+                                             SDP_HDR_CONNECTION,
+                                             SDP_HDR_MEDIA,
                                              &rtp_addr);
                        if (ret != NF_ACCEPT) {
                                nf_ct_helper_log(skb, ct, "cannot mangle SDP");
@@ -1139,10 +1082,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
        }
 
        /* Update session connection and owner addresses */
-       nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook);
-       if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK)
-               ret = nf_nat_sdp_session(skb, protoff, dataoff,
-                                        dptr, datalen, sdpoff, &rtp_addr);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK)
+               ret = hooks->sdp_session(skb, protoff, dataoff,
+                                        dptr, datalen, sdpoff,
+                                        &rtp_addr);
 
        return ret;
 }
@@ -1242,11 +1186,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        unsigned int matchoff, matchlen;
        struct nf_conntrack_expect *exp;
        union nf_inet_addr *saddr, daddr;
+       const struct nf_nat_sip_hooks *hooks;
        __be16 port;
        u8 proto;
        unsigned int expires = 0;
        int ret;
-       typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
 
        /* Expected connections can not register again. */
        if (ct->status & IPS_EXPECTED)
@@ -1309,10 +1253,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        exp->helper = nfct_help(ct)->helper;
        exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
 
-       nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
-       if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
-               ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen,
-                                       exp, matchoff, matchlen);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK)
+               ret = hooks->expect(skb, protoff, dataoff, dptr, datalen,
+                                   exp, matchoff, matchlen);
        else {
                if (nf_ct_expect_related(exp) != 0) {
                        nf_ct_helper_log(skb, ct, "cannot add expectation");
@@ -1515,7 +1459,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
                           unsigned int protoff, unsigned int dataoff,
                           const char **dptr, unsigned int *datalen)
 {
-       typeof(nf_nat_sip_hook) nf_nat_sip;
+       const struct nf_nat_sip_hooks *hooks;
        int ret;
 
        if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
@@ -1524,9 +1468,9 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
                ret = process_sip_response(skb, protoff, dataoff, dptr, datalen);
 
        if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
-               nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
-               if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff,
-                                             dptr, datalen)) {
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks && !hooks->msg(skb, protoff, dataoff,
+                                        dptr, datalen)) {
                        nf_ct_helper_log(skb, ct, "cannot NAT SIP message");
                        ret = NF_DROP;
                }
@@ -1546,7 +1490,6 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
        s16 diff, tdiff = 0;
        int ret = NF_ACCEPT;
        bool term;
-       typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust;
 
        if (ctinfo != IP_CT_ESTABLISHED &&
            ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -1610,9 +1553,11 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
        }
 
        if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
-               nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook);
-               if (nf_nat_sip_seq_adjust)
-                       nf_nat_sip_seq_adjust(skb, protoff, tdiff);
+               const struct nf_nat_sip_hooks *hooks;
+
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks)
+                       hooks->seq_adjust(skb, protoff, tdiff);
        }
 
        return ret;
index f9790405b7fff5ead6004bf4266fe4e96abbc6cb..b4d691db955ed451954381e01baeed57ca588d11 100644 (file)
@@ -625,33 +625,26 @@ static struct nf_ct_helper_expectfn sip_nat = {
 
 static void __exit nf_nat_sip_fini(void)
 {
-       RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
+       RCU_INIT_POINTER(nf_nat_sip_hooks, NULL);
+
        nf_ct_helper_expectfn_unregister(&sip_nat);
        synchronize_rcu();
 }
 
+static const struct nf_nat_sip_hooks sip_hooks = {
+       .msg            = nf_nat_sip,
+       .seq_adjust     = nf_nat_sip_seq_adjust,
+       .expect         = nf_nat_sip_expect,
+       .sdp_addr       = nf_nat_sdp_addr,
+       .sdp_port       = nf_nat_sdp_port,
+       .sdp_session    = nf_nat_sdp_session,
+       .sdp_media      = nf_nat_sdp_media,
+};
+
 static int __init nf_nat_sip_init(void)
 {
-       BUG_ON(nf_nat_sip_hook != NULL);
-       BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
-       BUG_ON(nf_nat_sip_expect_hook != NULL);
-       BUG_ON(nf_nat_sdp_addr_hook != NULL);
-       BUG_ON(nf_nat_sdp_port_hook != NULL);
-       BUG_ON(nf_nat_sdp_session_hook != NULL);
-       BUG_ON(nf_nat_sdp_media_hook != NULL);
-       RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip);
-       RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust);
-       RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect);
-       RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr);
-       RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port);
-       RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session);
-       RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media);
+       BUG_ON(nf_nat_sip_hooks != NULL);
+       RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks);
        nf_ct_helper_expectfn_register(&sip_nat);
        return 0;
 }
index 50580494148d00433c092ee0ad2a7097a1653221..476accd171452fcfbfbe01018dcadd55fee41f67 100644 (file)
@@ -49,10 +49,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
 };
 
 static int
-ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
-                         struct nf_conntrack_l4proto *l4proto,
-                         struct net *net,
-                         const struct nlattr *attr)
+ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
+                         struct net *net, const struct nlattr *attr)
 {
        int ret = 0;
 
@@ -64,8 +62,7 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
                if (ret < 0)
                        return ret;
 
-               ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net,
-                                                         &timeout->data);
+               ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts);
        }
        return ret;
 }
@@ -123,7 +120,8 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
                                goto err_proto_put;
                        }
 
-                       ret = ctnl_timeout_parse_policy(matching, l4proto, net,
+                       ret = ctnl_timeout_parse_policy(&matching->data,
+                                                       l4proto, net,
                                                        cda[CTA_TIMEOUT_DATA]);
                        return ret;
                }
@@ -138,7 +136,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
                goto err_proto_put;
        }
 
-       ret = ctnl_timeout_parse_policy(timeout, l4proto, net,
+       ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net,
                                        cda[CTA_TIMEOUT_DATA]);
        if (ret < 0)
                goto err;
@@ -342,6 +340,147 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
        return ret;
 }
 
+static int
+cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
+                     const struct nlmsghdr *nlh,
+                     const struct nlattr * const cda[])
+{
+       __u16 l3num;
+       __u8 l4num;
+       struct nf_conntrack_l4proto *l4proto;
+       struct net *net = sock_net(skb->sk);
+       unsigned int *timeouts;
+       int ret;
+
+       if (!cda[CTA_TIMEOUT_L3PROTO] ||
+           !cda[CTA_TIMEOUT_L4PROTO] ||
+           !cda[CTA_TIMEOUT_DATA])
+               return -EINVAL;
+
+       l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
+       l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
+       l4proto = nf_ct_l4proto_find_get(l3num, l4num);
+
+       /* This protocol is not supported, skip. */
+       if (l4proto->l4proto != l4num) {
+               ret = -EOPNOTSUPP;
+               goto err;
+       }
+
+       timeouts = l4proto->get_timeouts(net);
+
+       ret = ctnl_timeout_parse_policy(timeouts, l4proto, net,
+                                       cda[CTA_TIMEOUT_DATA]);
+       if (ret < 0)
+               goto err;
+
+       nf_ct_l4proto_put(l4proto);
+       return 0;
+err:
+       nf_ct_l4proto_put(l4proto);
+       return ret;
+}
+
+static int
+cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid,
+                           u32 seq, u32 type, int event,
+                           struct nf_conntrack_l4proto *l4proto)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       unsigned int flags = portid ? NLM_F_MULTI : 0;
+
+       event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
+       if (nlh == NULL)
+               goto nlmsg_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family = AF_UNSPEC;
+       nfmsg->version = NFNETLINK_V0;
+       nfmsg->res_id = 0;
+
+       if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) ||
+           nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto))
+               goto nla_put_failure;
+
+       if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
+               struct nlattr *nest_parms;
+               unsigned int *timeouts = l4proto->get_timeouts(net);
+               int ret;
+
+               nest_parms = nla_nest_start(skb,
+                                           CTA_TIMEOUT_DATA | NLA_F_NESTED);
+               if (!nest_parms)
+                       goto nla_put_failure;
+
+               ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts);
+               if (ret < 0)
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, nest_parms);
+       }
+
+       nlmsg_end(skb, nlh);
+       return skb->len;
+
+nlmsg_failure:
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -1;
+}
+
+static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
+                                const struct nlmsghdr *nlh,
+                                const struct nlattr * const cda[])
+{
+       __u16 l3num;
+       __u8 l4num;
+       struct nf_conntrack_l4proto *l4proto;
+       struct net *net = sock_net(skb->sk);
+       struct sk_buff *skb2;
+       int ret, err;
+
+       if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO])
+               return -EINVAL;
+
+       l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
+       l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
+       l4proto = nf_ct_l4proto_find_get(l3num, l4num);
+
+       /* This protocol is not supported, skip. */
+       if (l4proto->l4proto != l4num) {
+               err = -EOPNOTSUPP;
+               goto err;
+       }
+
+       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (skb2 == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid,
+                                         nlh->nlmsg_seq,
+                                         NFNL_MSG_TYPE(nlh->nlmsg_type),
+                                         IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
+                                         l4proto);
+       if (ret <= 0) {
+               kfree_skb(skb2);
+               err = -ENOMEM;
+               goto err;
+       }
+       ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
+       if (ret > 0)
+               ret = 0;
+
+       /* this avoids a loop in nfnetlink. */
+       return ret == -EAGAIN ? -ENOBUFS : ret;
+err:
+       nf_ct_l4proto_put(l4proto);
+       return err;
+}
+
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
 {
@@ -384,6 +523,12 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
        [IPCTNL_MSG_TIMEOUT_DELETE]     = { .call = cttimeout_del_timeout,
                                            .attr_count = CTA_TIMEOUT_MAX,
                                            .policy = cttimeout_nla_policy },
+       [IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set,
+                                           .attr_count = CTA_TIMEOUT_MAX,
+                                           .policy = cttimeout_nla_policy },
+       [IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get,
+                                           .attr_count = CTA_TIMEOUT_MAX,
+                                           .policy = cttimeout_nla_policy },
 };
 
 static const struct nfnetlink_subsystem cttimeout_subsys = {
index d92cc317bf8b25a0c371b770688c860169c9dea8..3c4b69e5fe17348b422f390bf79ab4269043fe5d 100644 (file)
@@ -319,7 +319,8 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags)
 }
 
 static struct sk_buff *
-nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
+nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
+                unsigned int pkt_size)
 {
        struct sk_buff *skb;
        unsigned int n;
@@ -328,13 +329,13 @@ nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
         * message.  WARNING: has to be <= 128k due to slab restrictions */
 
        n = max(inst_size, pkt_size);
-       skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC);
+       skb = nfnetlink_alloc_skb(net, n, peer_portid, GFP_ATOMIC);
        if (!skb) {
                if (n > pkt_size) {
                        /* try to allocate only as much as we need for current
                         * packet */
 
-                       skb = nfnetlink_alloc_skb(&init_net, pkt_size,
+                       skb = nfnetlink_alloc_skb(net, pkt_size,
                                                  peer_portid, GFP_ATOMIC);
                        if (!skb)
                                pr_err("nfnetlink_log: can't even alloc %u bytes\n",
@@ -702,8 +703,8 @@ nfulnl_log_packet(struct net *net,
        }
 
        if (!inst->skb) {
-               inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz,
-                                            size);
+               inst->skb = nfulnl_alloc_skb(net, inst->peer_portid,
+                                            inst->nlbufsiz, size);
                if (!inst->skb)
                        goto alloc_failure;
        }
index ae2e5c11d01ac9887c5158d0084f193c624373ef..21258cf7009118c3820c7b9575b4a4bd1b331f1f 100644 (file)
@@ -298,7 +298,7 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
 }
 
 static struct sk_buff *
-nfqnl_build_packet_message(struct nfqnl_instance *queue,
+nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                           struct nf_queue_entry *entry,
                           __be32 **packet_id_ptr)
 {
@@ -372,7 +372,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        if (queue->flags & NFQA_CFG_F_CONNTRACK)
                ct = nfqnl_ct_get(entskb, &size, &ctinfo);
 
-       skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid,
+       skb = nfnetlink_alloc_skb(net, size, queue->peer_portid,
                                  GFP_ATOMIC);
        if (!skb)
                return NULL;
@@ -525,7 +525,7 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
        __be32 *packet_id_ptr;
        int failopen = 0;
 
-       nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
+       nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr);
        if (nskb == NULL) {
                err = -ENOMEM;
                goto err_out;
index cd24290f3b2fdbff20088a0ffd2692fb6240a9ce..e762de5ee89bfa480b1789ce164cb024ac35e91e 100644 (file)
@@ -43,10 +43,42 @@ optlen(const u_int8_t *opt, unsigned int offset)
                return opt[offset+1];
 }
 
+static u_int32_t tcpmss_reverse_mtu(struct net *net,
+                                   const struct sk_buff *skb,
+                                   unsigned int family)
+{
+       struct flowi fl;
+       const struct nf_afinfo *ai;
+       struct rtable *rt = NULL;
+       u_int32_t mtu     = ~0U;
+
+       if (family == PF_INET) {
+               struct flowi4 *fl4 = &fl.u.ip4;
+               memset(fl4, 0, sizeof(*fl4));
+               fl4->daddr = ip_hdr(skb)->saddr;
+       } else {
+               struct flowi6 *fl6 = &fl.u.ip6;
+
+               memset(fl6, 0, sizeof(*fl6));
+               fl6->daddr = ipv6_hdr(skb)->saddr;
+       }
+       rcu_read_lock();
+       ai = nf_get_afinfo(family);
+       if (ai != NULL)
+               ai->route(net, (struct dst_entry **)&rt, &fl, false);
+       rcu_read_unlock();
+
+       if (rt != NULL) {
+               mtu = dst_mtu(&rt->dst);
+               dst_release(&rt->dst);
+       }
+       return mtu;
+}
+
 static int
 tcpmss_mangle_packet(struct sk_buff *skb,
                     const struct xt_action_param *par,
-                    unsigned int in_mtu,
+                    unsigned int family,
                     unsigned int tcphoff,
                     unsigned int minlen)
 {
@@ -76,6 +108,9 @@ tcpmss_mangle_packet(struct sk_buff *skb,
                return -1;
 
        if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
+               struct net *net = dev_net(par->in ? par->in : par->out);
+               unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);
+
                if (dst_mtu(skb_dst(skb)) <= minlen) {
                        net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
                                            dst_mtu(skb_dst(skb)));
@@ -165,37 +200,6 @@ tcpmss_mangle_packet(struct sk_buff *skb,
        return TCPOLEN_MSS;
 }
 
-static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
-                                   unsigned int family)
-{
-       struct flowi fl;
-       const struct nf_afinfo *ai;
-       struct rtable *rt = NULL;
-       u_int32_t mtu     = ~0U;
-
-       if (family == PF_INET) {
-               struct flowi4 *fl4 = &fl.u.ip4;
-               memset(fl4, 0, sizeof(*fl4));
-               fl4->daddr = ip_hdr(skb)->saddr;
-       } else {
-               struct flowi6 *fl6 = &fl.u.ip6;
-
-               memset(fl6, 0, sizeof(*fl6));
-               fl6->daddr = ipv6_hdr(skb)->saddr;
-       }
-       rcu_read_lock();
-       ai = nf_get_afinfo(family);
-       if (ai != NULL)
-               ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
-       rcu_read_unlock();
-
-       if (rt != NULL) {
-               mtu = dst_mtu(&rt->dst);
-               dst_release(&rt->dst);
-       }
-       return mtu;
-}
-
 static unsigned int
 tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
 {
@@ -204,7 +208,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
        int ret;
 
        ret = tcpmss_mangle_packet(skb, par,
-                                  tcpmss_reverse_mtu(skb, PF_INET),
+                                  PF_INET,
                                   iph->ihl * 4,
                                   sizeof(*iph) + sizeof(struct tcphdr));
        if (ret < 0)
@@ -233,7 +237,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        if (tcphoff < 0)
                return NF_DROP;
        ret = tcpmss_mangle_packet(skb, par,
-                                  tcpmss_reverse_mtu(skb, PF_INET6),
+                                  PF_INET6,
                                   tcphoff,
                                   sizeof(*ipv6h) + sizeof(struct tcphdr));
        if (ret < 0)
index 31790e789e224f8fb758ee87e62f8dd8908e81dd..e7c4e0e01ff5de40fb32cdeddd70cb204b84ea32 100644 (file)
@@ -81,7 +81,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
        struct xt_set_info_match_v0 *info = par->matchinfo;
        ip_set_id_t index;
 
-       index = ip_set_nfnl_get_byindex(info->match_set.index);
+       index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
 
        if (index == IPSET_INVALID_ID) {
                pr_warning("Cannot find set indentified by id %u to match\n",
@@ -91,7 +91,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
        if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
                pr_warning("Protocol error: set match dimension "
                           "is over the limit!\n");
-               ip_set_nfnl_put(info->match_set.index);
+               ip_set_nfnl_put(par->net, info->match_set.index);
                return -ERANGE;
        }
 
@@ -106,9 +106,104 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par)
 {
        struct xt_set_info_match_v0 *info = par->matchinfo;
 
-       ip_set_nfnl_put(info->match_set.index);
+       ip_set_nfnl_put(par->net, info->match_set.index);
 }
 
+/* Revision 1 match */
+
+static bool
+set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_set_info_match_v1 *info = par->matchinfo;
+       ADT_OPT(opt, par->family, info->match_set.dim,
+               info->match_set.flags, 0, UINT_MAX);
+
+       if (opt.flags & IPSET_RETURN_NOMATCH)
+               opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
+
+       return match_set(info->match_set.index, skb, par, &opt,
+                        info->match_set.flags & IPSET_INV_MATCH);
+}
+
+static int
+set_match_v1_checkentry(const struct xt_mtchk_param *par)
+{
+       struct xt_set_info_match_v1 *info = par->matchinfo;
+       ip_set_id_t index;
+
+       index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
+
+       if (index == IPSET_INVALID_ID) {
+               pr_warning("Cannot find set indentified by id %u to match\n",
+                          info->match_set.index);
+               return -ENOENT;
+       }
+       if (info->match_set.dim > IPSET_DIM_MAX) {
+               pr_warning("Protocol error: set match dimension "
+                          "is over the limit!\n");
+               ip_set_nfnl_put(par->net, info->match_set.index);
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+static void
+set_match_v1_destroy(const struct xt_mtdtor_param *par)
+{
+       struct xt_set_info_match_v1 *info = par->matchinfo;
+
+       ip_set_nfnl_put(par->net, info->match_set.index);
+}
+
+/* Revision 3 match */
+
+static bool
+match_counter(u64 counter, const struct ip_set_counter_match *info)
+{
+       switch (info->op) {
+       case IPSET_COUNTER_NONE:
+               return true;
+       case IPSET_COUNTER_EQ:
+               return counter == info->value;
+       case IPSET_COUNTER_NE:
+               return counter != info->value;
+       case IPSET_COUNTER_LT:
+               return counter < info->value;
+       case IPSET_COUNTER_GT:
+               return counter > info->value;
+       }
+       return false;
+}
+
+static bool
+set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_set_info_match_v3 *info = par->matchinfo;
+       ADT_OPT(opt, par->family, info->match_set.dim,
+               info->match_set.flags, info->flags, UINT_MAX);
+       int ret;
+
+       if (info->packets.op != IPSET_COUNTER_NONE ||
+           info->bytes.op != IPSET_COUNTER_NONE)
+               opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
+
+       ret = match_set(info->match_set.index, skb, par, &opt,
+                       info->match_set.flags & IPSET_INV_MATCH);
+
+       if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
+               return ret;
+
+       if (!match_counter(opt.ext.packets, &info->packets))
+               return 0;
+       return match_counter(opt.ext.bytes, &info->bytes);
+}
+
+#define set_match_v3_checkentry        set_match_v1_checkentry
+#define set_match_v3_destroy   set_match_v1_destroy
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
 static unsigned int
 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
 {
@@ -133,7 +228,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
        ip_set_id_t index;
 
        if (info->add_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->add_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find add_set index %u as target\n",
                                   info->add_set.index);
@@ -142,12 +237,12 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
        }
 
        if (info->del_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->del_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find del_set index %u as target\n",
                                   info->del_set.index);
                        if (info->add_set.index != IPSET_INVALID_ID)
-                               ip_set_nfnl_put(info->add_set.index);
+                               ip_set_nfnl_put(par->net, info->add_set.index);
                        return -ENOENT;
                }
        }
@@ -156,9 +251,9 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
                pr_warning("Protocol error: SET target dimension "
                           "is over the limit!\n");
                if (info->add_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->add_set.index);
+                       ip_set_nfnl_put(par->net, info->add_set.index);
                if (info->del_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->del_set.index);
+                       ip_set_nfnl_put(par->net, info->del_set.index);
                return -ERANGE;
        }
 
@@ -175,57 +270,12 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par)
        const struct xt_set_info_target_v0 *info = par->targinfo;
 
        if (info->add_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->add_set.index);
+               ip_set_nfnl_put(par->net, info->add_set.index);
        if (info->del_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->del_set.index);
+               ip_set_nfnl_put(par->net, info->del_set.index);
 }
 
-/* Revision 1 match and target */
-
-static bool
-set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
-{
-       const struct xt_set_info_match_v1 *info = par->matchinfo;
-       ADT_OPT(opt, par->family, info->match_set.dim,
-               info->match_set.flags, 0, UINT_MAX);
-
-       if (opt.flags & IPSET_RETURN_NOMATCH)
-               opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
-
-       return match_set(info->match_set.index, skb, par, &opt,
-                        info->match_set.flags & IPSET_INV_MATCH);
-}
-
-static int
-set_match_v1_checkentry(const struct xt_mtchk_param *par)
-{
-       struct xt_set_info_match_v1 *info = par->matchinfo;
-       ip_set_id_t index;
-
-       index = ip_set_nfnl_get_byindex(info->match_set.index);
-
-       if (index == IPSET_INVALID_ID) {
-               pr_warning("Cannot find set indentified by id %u to match\n",
-                          info->match_set.index);
-               return -ENOENT;
-       }
-       if (info->match_set.dim > IPSET_DIM_MAX) {
-               pr_warning("Protocol error: set match dimension "
-                          "is over the limit!\n");
-               ip_set_nfnl_put(info->match_set.index);
-               return -ERANGE;
-       }
-
-       return 0;
-}
-
-static void
-set_match_v1_destroy(const struct xt_mtdtor_param *par)
-{
-       struct xt_set_info_match_v1 *info = par->matchinfo;
-
-       ip_set_nfnl_put(info->match_set.index);
-}
+/* Revision 1 target */
 
 static unsigned int
 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
@@ -251,7 +301,7 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
        ip_set_id_t index;
 
        if (info->add_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->add_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find add_set index %u as target\n",
                                   info->add_set.index);
@@ -260,12 +310,12 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
        }
 
        if (info->del_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->del_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find del_set index %u as target\n",
                                   info->del_set.index);
                        if (info->add_set.index != IPSET_INVALID_ID)
-                               ip_set_nfnl_put(info->add_set.index);
+                               ip_set_nfnl_put(par->net, info->add_set.index);
                        return -ENOENT;
                }
        }
@@ -274,9 +324,9 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
                pr_warning("Protocol error: SET target dimension "
                           "is over the limit!\n");
                if (info->add_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->add_set.index);
+                       ip_set_nfnl_put(par->net, info->add_set.index);
                if (info->del_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->del_set.index);
+                       ip_set_nfnl_put(par->net, info->del_set.index);
                return -ERANGE;
        }
 
@@ -289,9 +339,9 @@ set_target_v1_destroy(const struct xt_tgdtor_param *par)
        const struct xt_set_info_target_v1 *info = par->targinfo;
 
        if (info->add_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->add_set.index);
+               ip_set_nfnl_put(par->net, info->add_set.index);
        if (info->del_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->del_set.index);
+               ip_set_nfnl_put(par->net, info->del_set.index);
 }
 
 /* Revision 2 target */
@@ -320,52 +370,6 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 #define set_target_v2_checkentry       set_target_v1_checkentry
 #define set_target_v2_destroy          set_target_v1_destroy
 
-/* Revision 3 match */
-
-static bool
-match_counter(u64 counter, const struct ip_set_counter_match *info)
-{
-       switch (info->op) {
-       case IPSET_COUNTER_NONE:
-               return true;
-       case IPSET_COUNTER_EQ:
-               return counter == info->value;
-       case IPSET_COUNTER_NE:
-               return counter != info->value;
-       case IPSET_COUNTER_LT:
-               return counter < info->value;
-       case IPSET_COUNTER_GT:
-               return counter > info->value;
-       }
-       return false;
-}
-
-static bool
-set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
-{
-       const struct xt_set_info_match_v3 *info = par->matchinfo;
-       ADT_OPT(opt, par->family, info->match_set.dim,
-               info->match_set.flags, info->flags, UINT_MAX);
-       int ret;
-
-       if (info->packets.op != IPSET_COUNTER_NONE ||
-           info->bytes.op != IPSET_COUNTER_NONE)
-               opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
-
-       ret = match_set(info->match_set.index, skb, par, &opt,
-                       info->match_set.flags & IPSET_INV_MATCH);
-
-       if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
-               return ret;
-
-       if (!match_counter(opt.ext.packets, &info->packets))
-               return 0;
-       return match_counter(opt.ext.bytes, &info->bytes);
-}
-
-#define set_match_v3_checkentry        set_match_v1_checkentry
-#define set_match_v3_destroy   set_match_v1_destroy
-
 static struct xt_match set_matches[] __read_mostly = {
        {
                .name           = "set",
index 938b7cbf56278b34c6d5574a64c8c32fa4a1d3a2..1ac41d3de5c36d4c32cf27806172b6ea884980de 100644 (file)
@@ -24,11 +24,12 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
 {
        struct xt_set_info *set = data;
        ip_set_id_t index;
+       struct net *net = qdisc_dev(tp->q)->nd_net;
 
        if (data_len != sizeof(*set))
                return -EINVAL;
 
-       index = ip_set_nfnl_get_byindex(set->index);
+       index = ip_set_nfnl_get_byindex(net, set->index);
        if (index == IPSET_INVALID_ID)
                return -ENOENT;
 
@@ -37,7 +38,7 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
        if (em->data)
                return 0;
 
-       ip_set_nfnl_put(index);
+       ip_set_nfnl_put(net, index);
        return -ENOMEM;
 }
 
@@ -45,7 +46,7 @@ static void em_ipset_destroy(struct tcf_proto *p, struct tcf_ematch *em)
 {
        const struct xt_set_info *set = (const void *) em->data;
        if (set) {
-               ip_set_nfnl_put(set->index);
+               ip_set_nfnl_put(qdisc_dev(p->q)->nd_net, set->index);
                kfree((void *) em->data);
        }
 }