summaryrefslogtreecommitdiffstats
path: root/kernel/include/linux
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-15 13:30:55 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-15 13:30:55 +0200
commit3fd6b24ace319b139ec3c4e3031a5f05d21e304e (patch)
treee6ac952e95fa44968196149e0172b1ef13e8236f /kernel/include/linux
parent00bcb2b40450eca4c7ad785bf85b12692e8d29af (diff)
ipset 5 in an almost ready state - milestonev5.0-pre1
Reworked protocol and internal interfaces, missing set types added, backward compatibility verified, lots of tests added (and thanks to the tests, bugs fixed), even the manpage is rewritten ;-). Countless changes everywhere... The missing bits before announcing ipset 5: - net namespace support - new iptables/ip6tables extension library - iptables/ip6tables match and target tests (backward/forward compatibility) - tests on catching syntax errors
Diffstat (limited to 'kernel/include/linux')
-rw-r--r--kernel/include/linux/netfilter/ip_set.h246
-rw-r--r--kernel/include/linux/netfilter/ip_set_bitmap.h6
-rw-r--r--kernel/include/linux/netfilter/ip_set_chash.h1096
-rw-r--r--kernel/include/linux/netfilter/ip_set_getport.h25
-rw-r--r--kernel/include/linux/netfilter/ip_set_hash.h5
-rw-r--r--kernel/include/linux/netfilter/ip_set_jhash.h152
-rw-r--r--kernel/include/linux/netfilter/ip_set_kernel.h20
-rw-r--r--kernel/include/linux/netfilter/ip_set_list.h21
-rw-r--r--kernel/include/linux/netfilter/ip_set_slist.h86
-rw-r--r--kernel/include/linux/netfilter/ip_set_timeout.h35
-rw-r--r--kernel/include/linux/netfilter/ipt_set.h21
-rw-r--r--kernel/include/linux/netfilter/xt_set.h55
12 files changed, 1503 insertions, 265 deletions
diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h
index d0b47a0..e700503 100644
--- a/kernel/include/linux/netfilter/ip_set.h
+++ b/kernel/include/linux/netfilter/ip_set.h
@@ -11,14 +11,10 @@
* published by the Free Software Foundation.
*/
-#if 1
-#define IP_SET_DEBUG
-#endif
-
/* The protocol version */
#define IPSET_PROTOCOL 5
-/* The max length of strings: set and type identifiers */
+/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
/* Message types and commands */
@@ -43,6 +39,7 @@ enum ipset_cmd {
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */
IPSET_CMD_HELP, /* Get help */
IPSET_CMD_VERSION, /* Get program version */
+ IPSET_CMD_QUIT, /* Quit from interactive mode */
IPSET_CMD_MAX,
@@ -58,6 +55,7 @@ enum {
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
IPSET_ATTR_REVISION, /* Settype revision */
IPSET_ATTR_FAMILY, /* Settype family */
+ IPSET_ATTR_FLAGS, /* Flags at command level */
IPSET_ATTR_DATA, /* Nested attributes */
IPSET_ATTR_ADT, /* Multiple data containers */
IPSET_ATTR_LINENO, /* Restore lineno */
@@ -77,8 +75,8 @@ enum {
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
IPSET_ATTR_PORT_TO,
IPSET_ATTR_TIMEOUT,
- IPSET_ATTR_FLAGS,
- /* IPSET_ATTR_LINENO */
+ IPSET_ATTR_CADT_FLAGS,
+ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -123,15 +121,19 @@ enum ipset_errno {
IPSET_ERR_INVALID_NETMASK,
IPSET_ERR_INVALID_FAMILY,
IPSET_ERR_TIMEOUT,
+ IPSET_ERR_REFERENCED,
+ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 160,
};
-
-enum ipset_data_flags {
+
+enum ipset_cmd_flags {
IPSET_FLAG_BIT_EXIST = 0,
IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST),
-
- IPSET_FLAG_BIT_BEFORE = 2,
+};
+
+enum ipset_cadt_flags {
+ IPSET_FLAG_BIT_BEFORE = 0,
IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE),
};
@@ -140,35 +142,13 @@ enum ipset_adt {
IPSET_ADD,
IPSET_DEL,
IPSET_TEST,
- IPSET_CREATE,
+ IPSET_ADT_MAX,
+ IPSET_CREATE = IPSET_ADT_MAX,
IPSET_CADT_MAX,
};
-#ifndef __KERNEL__
-#ifdef IP_SET_DEBUG
-#include <stdio.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#define D(format, args...) do { \
- fprintf(stderr, "%s: %s: ", __FILE__, __FUNCTION__); \
- fprintf(stderr, format "\n" , ## args); \
-} while (0)
-static inline void
-dump_nla(struct nlattr *nla[], int maxlen)
-{
- int i;
-
- for (i = 0; i < maxlen; i++)
- D("nla[%u] does%s exist", i, !nla[i] ? " NOT" : "");
-}
-
-#else
-#define D(format, args...)
-#define dump_nla(nla, maxlen)
-#endif
-#endif /* !__KERNEL__ */
-
#ifdef __KERNEL__
+#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netlink.h>
#include <net/netlink.h>
@@ -176,19 +156,27 @@ dump_nla(struct nlattr *nla[], int maxlen)
/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
* and IPSET_INVALID_ID if you want to increase the max number of sets.
*/
-typedef uint16_t ip_set_id_t;
+typedef u16 ip_set_id_t;
#define IPSET_INVALID_ID 65535
+enum ip_set_dim {
+ IPSET_DIM_ZERO = 0,
+ IPSET_DIM_ONE,
+ IPSET_DIM_TWO,
+ IPSET_DIM_THREE,
+ /* Max dimension in elements.
+ * If changed, new revision of iptables match/target is required.
+ */
+ IPSET_DIM_MAX = 6,
+};
+
/* Option flags for kernel operations */
enum ip_set_kopt {
- /* Bit 0 is reserved */
- IPSET_SRC_FLAG = 1,
- IPSET_SRC = (1 << IPSET_SRC_FLAG),
- IPSET_DST_FLAG = 2,
- IPSET_DST = (1 << IPSET_DST_FLAG),
- IPSET_INV_FLAG = 3,
- IPSET_INV = (1 << IPSET_INV_FLAG),
+ IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
+ IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
+ IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
+ IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
};
/* Set features */
@@ -203,72 +191,60 @@ enum ip_set_feature {
IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG),
IPSET_TYPE_NAME_FLAG = 4,
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
+ /* Actually just a flag for dumping */
+ IPSET_DUMP_LAST_FLAG = 7,
+ IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
};
+/* Calculate the bytes required to store the inclusive range of a-b */
static inline int
-bitmap_bytes(uint32_t a, uint32_t b)
+bitmap_bytes(u32 a, u32 b)
{
return 4 * ((((b - a + 8) / 8) + 3) / 4);
}
-#define ip_set_printk(format, args...) \
- do { \
- printk("%s: %s: ", __FILE__, __FUNCTION__); \
- printk(format "\n" , ## args); \
- } while (0)
-
-#if defined(IP_SET_DEBUG)
-#define D(format, args...) \
- do { \
- printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\
- printk(format "\n" , ## args); \
- } while (0)
-
-static inline void
-dump_nla(const struct nlattr * const nla[], int maxlen)
-{
- int i;
-
- for (i = 0; i < maxlen; i++)
- printk("nlattr[%u] does%s exist\n", i, nla[i] ? "" : " NOT");
-}
-#else
-#define D(format, args...)
-#define dump_nla(nla, maxlen)
-#endif
-
struct ip_set;
+typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout);
+
/* Set type, variant-specific part */
struct ip_set_type_variant {
/* Kernelspace: test/add/del entries */
int (*kadt)(struct ip_set *set, const struct sk_buff * skb,
- enum ipset_adt adt, uint8_t pf, const uint8_t *flags);
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
/* Userspace: test/add/del entries */
int (*uadt)(struct ip_set *set, struct nlattr *head, int len,
- enum ipset_adt adt, uint32_t *lineno, uint32_t flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags);
+
+ /* Low level add/del/test entries */
+ ipset_adtfn adt[IPSET_ADT_MAX];
/* When adding entries and set is full, try to resize the set */
- int (*resize)(struct ip_set *set, uint8_t retried);
+ int (*resize)(struct ip_set *set, gfp_t gfp_flags, bool retried);
/* Destroy the set */
void (*destroy)(struct ip_set *set);
/* Flush the elements */
void (*flush)(struct ip_set *set);
-
+ /* Expire entries before listing */
+ void (*expire)(struct ip_set *set);
/* List set header data */
int (*head)(struct ip_set *set, struct sk_buff *skb);
/* List elements */
int (*list)(struct ip_set *set, struct sk_buff *skb,
struct netlink_callback *cb);
+
+ /* Return true if "b" set is the same as "a"
+ * according to the set parameters */
+ bool (*same_set)(const struct ip_set *a, const struct ip_set *b);
};
/* Flags for the set type variants */
enum ip_set_type_flags {
- IP_SET_FLAG_VMALLOC_BIT = 0,
- IP_SET_FLAG_VMALLOC = (1 << IP_SET_FLAG_VMALLOC_BIT),
- IP_SET_FLAG_TIMEOUT_BIT = 1,
- IP_SET_FLAG_TIMEOUT = (1 << IP_SET_FLAG_TIMEOUT_BIT),
+ /* Set members created by kmalloc */
+ IP_SET_FLAG_KMALLOC_BIT = 0,
+ IP_SET_FLAG_KMALLOC = (1 << IP_SET_FLAG_KMALLOC_BIT),
};
/* The core set type structure */
@@ -278,17 +254,19 @@ struct ip_set_type {
/* Typename */
char name[IPSET_MAXNAMELEN];
/* Protocol version */
- uint8_t protocol;
+ u8 protocol;
/* Set features to control swapping */
- uint8_t features;
+ u8 features;
+ /* Set type dimension */
+ u8 dimension;
/* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */
- uint8_t family;
+ u8 family;
/* Type revision */
- uint8_t revision;
+ u8 revision;
/* Create set */
int (*create)(struct ip_set *set,
- struct nlattr *head, int len, uint32_t flags);
+ struct nlattr *head, int len, u32 flags);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
@@ -310,86 +288,90 @@ struct ip_set {
/* The type variant doing the real job */
const struct ip_set_type_variant *variant;
/* The actual INET family */
- uint8_t family;
+ u8 family;
/* Set type flags, filled/modified by create/resize */
- uint8_t flags;
+ u8 flags;
/* The type specific data */
void *data;
};
/* register and unregister set references */
-extern ip_set_id_t ip_set_get_byname(const char name[IPSET_MAXNAMELEN]);
+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);
/* API for iptables set match, and SET target */
extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
- uint8_t family, const uint8_t *flags);
+ u8 family, u8 dim, u8 flags);
/* Allocate members */
static inline void *
-ip_set_alloc(size_t size, gfp_t gfp_mask, uint8_t *flags)
+ip_set_alloc(size_t size, gfp_t gfp_mask, u8 *flags)
{
- void *members = kzalloc(size, gfp_mask);
+ void *members = kzalloc(size, gfp_mask | __GFP_NOWARN);
if (members) {
- *flags &= ~IP_SET_FLAG_VMALLOC;
- D("allocated with kmalloc %p", members);
+ *flags |= IP_SET_FLAG_KMALLOC;
+ pr_debug("%p: allocated with kmalloc", members);
return members;
}
members = __vmalloc(size, gfp_mask | __GFP_ZERO, PAGE_KERNEL);
if (!members)
return NULL;
- *flags |= IP_SET_FLAG_VMALLOC;
- D("allocated with vmalloc %p", members);
+ *flags &= ~IP_SET_FLAG_KMALLOC;
+ pr_debug("%p: allocated with vmalloc", members);
return members;
}
static inline void
-ip_set_free(void *members, uint8_t flags)
+ip_set_free(void *members, u8 flags)
{
- D("free with %s %p", flags & IP_SET_FLAG_VMALLOC ? "vmalloc" : "kmalloc",
- members);
- if (flags & IP_SET_FLAG_VMALLOC)
- vfree(members);
- else
+ pr_debug("%p: free with %s", members,
+ flags & IP_SET_FLAG_KMALLOC ? "kmalloc" : "vmalloc");
+ if (flags & IP_SET_FLAG_KMALLOC)
kfree(members);
+ else
+ vfree(members);
}
/* Useful converters */
-static inline uint32_t
+static inline u32
ip_set_get_h32(const struct nlattr *attr)
{
- uint32_t value = nla_get_u32(attr);
+ u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohl(value) : value;
}
-static inline uint16_t
+static inline u16
ip_set_get_h16(const struct nlattr *attr)
{
- uint16_t value = nla_get_u16(attr);
+ u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? ntohs(value) : value;
}
-static inline uint32_t
+static inline u32
ip_set_get_n32(const struct nlattr *attr)
{
- uint32_t value = nla_get_u32(attr);
+ u32 value = nla_get_u32(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htonl(value);
}
-static inline uint16_t
+static inline u16
ip_set_get_n16(const struct nlattr *attr)
{
- uint16_t value = nla_get_u16(attr);
+ u16 value = nla_get_u16(attr);
return attr->nla_type & NLA_F_NET_BYTEORDER ? value : htons(value);
}
@@ -404,31 +386,49 @@ ip_set_get_n16(const struct nlattr *attr)
NLA_PUT_BE16(skb, type | NLA_F_NET_BYTEORDER, value)
/* Get address from skbuff */
-static inline uint32_t
-ip4addr(const struct sk_buff *skb, const uint8_t *flags)
+static inline u32
+ip4addr(const struct sk_buff *skb, bool src)
{
- return flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr;
+ return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
-ip4addrptr(const struct sk_buff *skb, const uint8_t *flags, uint32_t *addr)
+ip4addrptr(const struct sk_buff *skb, bool src, u32 *addr)
{
- *addr = flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr;
+ *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr;
}
static inline void
-ip6addrptr(const struct sk_buff *skb, const uint8_t *flags,
- struct in6_addr *addr)
+ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr)
{
- memcpy(addr, flags[0] & IPSET_SRC ? &ipv6_hdr(skb)->saddr
- : &ipv6_hdr(skb)->daddr,
+ memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr,
sizeof(*addr));
}
-#define pack_ip_port(map, ip, port) \
- (port + ((ip - ((map)->first_ip)) << 16))
+/* Interface to iptables/ip6tables */
+
+#define SO_IP_SET 83
+
+union ip_set_name_index {
+ char name[IPSET_MAXNAMELEN];
+ ip_set_id_t index;
+};
+
+#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
+struct ip_set_req_get_set {
+ unsigned op;
+ unsigned version;
+ union ip_set_name_index set;
+};
+
+#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
+/* Uses ip_set_req_get_set */
+
+#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
+struct ip_set_req_version {
+ unsigned op;
+ unsigned version;
+};
#endif /* __KERNEL__ */
diff --git a/kernel/include/linux/netfilter/ip_set_bitmap.h b/kernel/include/linux/netfilter/ip_set_bitmap.h
index 49d0f5c..0d067d0 100644
--- a/kernel/include/linux/netfilter/ip_set_bitmap.h
+++ b/kernel/include/linux/netfilter/ip_set_bitmap.h
@@ -12,10 +12,10 @@ enum {
/* Common functions */
-static inline uint32_t
-range_to_mask(uint32_t from, uint32_t to, uint8_t *bits)
+static inline u32
+range_to_mask(u32 from, u32 to, u8 *bits)
{
- uint32_t mask = 0xFFFFFFFE;
+ u32 mask = 0xFFFFFFFE;
*bits = 32;
while (--(*bits) > 0 && mask && (to & mask) != from)
diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h
new file mode 100644
index 0000000..0d77a5d
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_chash.h
@@ -0,0 +1,1096 @@
+#ifndef _IP_SET_CHASH_H
+#define _IP_SET_CHASH_H
+
+#include <linux/netfilter/ip_set_jhash.h>
+#include <linux/netfilter/ip_set_slist.h>
+#include <linux/netfilter/ip_set_timeout.h>
+
+#define CONCAT(a, b, c) a##b##c
+#define TOKEN(a, b, c) CONCAT(a, b, c)
+
+/* Cache friendly hash with resizing when linear searching becomes too long.
+ * Internally jhash is used with the assumption that the size of the stored
+ * data is a multiple of sizeof(u32). If storage supports timeout, the
+ * timeout field must be the last one in the data structure.
+ */
+
+/* Number of elements to store in an array block */
+#define CHASH_DEFAULT_ARRAY_SIZE 4
+/* Number of arrays: max ARRAY_SIZE * CHAIN_LIMIT "long" chains */
+#define CHASH_DEFAULT_CHAIN_LIMIT 3
+
+struct chash_nets {
+ u32 nets; /* number of elements per cidr */
+ u8 cidr; /* the cidr values added to the set */
+};
+
+struct chash {
+ struct slist *htable; /* Hashtable of single linked lists */
+ 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 */
+ struct timer_list gc; /* garbage collection when timeout enabled */
+ u8 htable_bits; /* size of hash table == 2^htable_bits */
+ u8 array_size; /* number of elements in an array */
+ u8 chain_limit; /* max number of arrays */
+#ifdef IP_SET_HASH_WITH_NETMASK
+ u8 netmask; /* netmask value for subnets to store */
+#endif
+#ifdef IP_SET_HASH_WITH_NETS
+ struct chash_nets nets[0]; /* book keeping of networks */
+#endif
+};
+
+static inline u8
+htable_bits(u32 hashsize)
+{
+ /* Assume that hashsize == 2^htable_bits */
+ u8 bits = fls(hashsize - 1);
+ if (jhash_size(bits) != hashsize)
+ /* Round up to the first 2^n value */
+ bits = fls(hashsize);
+
+ return bits;
+}
+
+static inline void
+add_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+{
+ u8 i;
+
+ pr_debug("add_cidr %u", cidr);
+ for (i = 0; i < host_mask - 1 && nets[i].cidr; i++) {
+ /* Add in increasing prefix order, so larger cidr first */
+ if (nets[i].cidr < cidr)
+ swap(nets[i].cidr, cidr);
+ }
+ if (i < host_mask - 1)
+ nets[i].cidr = cidr;
+}
+
+static inline void
+del_cidr(struct chash_nets *nets, u8 host_mask, u8 cidr)
+{
+ u8 i;
+
+ pr_debug("del_cidr %u", cidr);
+ for (i = 0; i < host_mask - 2 && nets[i].cidr; i++) {
+ if (nets[i].cidr == cidr)
+ nets[i].cidr = cidr = nets[i+1].cidr;
+ }
+ nets[host_mask - 2].cidr = 0;
+}
+
+static void
+chash_destroy(struct slist *t, u8 htable_bits, u8 flags)
+{
+ struct slist *n, *tmp;
+ u32 i;
+
+ for (i = 0; i < jhash_size(htable_bits); i++)
+ slist_for_each_safe(n, tmp, &t[i])
+ /* FIXME: slab cache */
+ kfree(n);
+
+ ip_set_free(t, flags);
+}
+
+static size_t
+chash_memsize(const struct chash *h, size_t dsize, u8 host_mask)
+{
+ struct slist *n;
+ u32 i;
+ size_t memsize = sizeof(*h)
+#ifdef IP_SET_HASH_WITH_NETS
+ + sizeof(struct chash_nets) * (host_mask - 1)
+#endif
+ + jhash_size(h->htable_bits) * sizeof(struct slist);
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++)
+ slist_for_each(n, &h->htable[i])
+ memsize += sizeof(struct slist)
+ + h->array_size * dsize;
+
+ return memsize;
+}
+
+static void
+ip_set_hash_flush(struct ip_set *set)
+{
+ struct chash *h = set->data;
+ struct slist *n, *tmp;
+ u32 i;
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+ slist_for_each_safe(n, tmp, &h->htable[i])
+ /* FIXME: slab cache */
+ kfree(n);
+ h->htable[i].next = NULL;
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ memset(h->nets, 0, sizeof(struct chash_nets)
+ * (set->family == AF_INET ? 31 : 127));
+#endif
+ h->elements = 0;
+}
+
+static void
+ip_set_hash_destroy(struct ip_set *set)
+{
+ struct chash *h = set->data;
+
+ if (with_timeout(h->timeout))
+ del_timer_sync(&h->gc);
+
+ chash_destroy(h->htable, h->htable_bits, set->flags);
+ kfree(h);
+
+ set->data = NULL;
+}
+
+#define JHASH2(data, initval, htable_bits) \
+jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
+ & jhash_mask(htable_bits)
+
+#endif /* _IP_SET_CHASH_H */
+
+/* Type/family dependent function prototypes */
+
+#define type_pf_data_equal TOKEN(TYPE, PF, _data_equal)
+#define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull)
+#define type_pf_data_copy TOKEN(TYPE, PF, _data_copy)
+#define type_pf_data_swap TOKEN(TYPE, PF, _data_swap)
+#define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out)
+#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
+#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
+#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
+
+#define type_pf_elem TOKEN(TYPE, PF, _elem)
+#define type_pf_telem TOKEN(TYPE, PF, _telem)
+#define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout)
+#define type_pf_data_expired TOKEN(TYPE, PF, _data_expired)
+#define type_pf_data_swap_timeout TOKEN(TYPE, PF, _data_swap_timeout)
+#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set)
+
+#define type_pf_chash_readd TOKEN(TYPE, PF, _chash_readd)
+#define type_pf_chash_del_elem TOKEN(TYPE, PF, _chash_del_elem)
+#define type_pf_chash_add TOKEN(TYPE, PF, _chash_add)
+#define type_pf_chash_del TOKEN(TYPE, PF, _chash_del)
+#define type_pf_chash_test_cidrs TOKEN(TYPE, PF, _chash_test_cidrs)
+#define type_pf_chash_test TOKEN(TYPE, PF, _chash_test)
+
+#define type_pf_chash_treadd TOKEN(TYPE, PF, _chash_treadd)
+#define type_pf_chash_del_telem TOKEN(TYPE, PF, _chash_del_telem)
+#define type_pf_chash_expire TOKEN(TYPE, PF, _chash_expire)
+#define type_pf_chash_tadd TOKEN(TYPE, PF, _chash_tadd)
+#define type_pf_chash_tdel TOKEN(TYPE, PF, _chash_tdel)
+#define type_pf_chash_ttest_cidrs TOKEN(TYPE, PF, _chash_ttest_cidrs)
+#define type_pf_chash_ttest TOKEN(TYPE, PF, _chash_ttest)
+
+#define type_pf_resize TOKEN(TYPE, PF, _resize)
+#define type_pf_tresize TOKEN(TYPE, PF, _tresize)
+#define type_pf_flush ip_set_hash_flush
+#define type_pf_destroy ip_set_hash_destroy
+#define type_pf_head TOKEN(TYPE, PF, _head)
+#define type_pf_list TOKEN(TYPE, PF, _list)
+#define type_pf_tlist TOKEN(TYPE, PF, _tlist)
+#define type_pf_same_set TOKEN(TYPE, PF, _same_set)
+#define type_pf_kadt TOKEN(TYPE, PF, _kadt)
+#define type_pf_uadt TOKEN(TYPE, PF, _uadt)
+#define type_pf_gc TOKEN(TYPE, PF, _gc)
+#define type_pf_gc_init TOKEN(TYPE, PF, _gc_init)
+#define type_pf_variant TOKEN(TYPE, PF, _variant)
+#define type_pf_tvariant TOKEN(TYPE, PF, _tvariant)
+
+/* Flavour without timeout */
+
+#define chash_data(n, i) \
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem))
+
+static int
+type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits,
+ const struct type_pf_elem *value, gfp_t gfp_flags)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash = JHASH2(value, h->initval, htable_bits);
+
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_data(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, value);
+ return 0;
+}
+
+static void
+type_pf_chash_del_elem(struct chash *h, struct slist *prev,
+ struct slist *n, int i)
+{
+ struct type_pf_elem *data = chash_data(n, i);
+ struct slist *tmp;
+ int j;
+
+ if (n->next != NULL) {
+ for (prev = n, tmp = n->next;
+ tmp->next != NULL;
+ prev = tmp, tmp = tmp->next)
+ /* Find last array */;
+ j = 0;
+ } else {
+ /* Already at last array */
+ tmp = n;
+ j = i;
+ }
+ /* Find last non-empty element */
+ for (; j < h->array_size - 1; j++)
+ if (type_pf_data_isnull(chash_data(tmp, j + 1)))
+ break;
+
+ if (!(tmp == n && i == j)) {
+ type_pf_data_swap(data, chash_data(tmp, j));
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+#endif
+ if (j == 0) {
+ prev->next = NULL;
+ kfree(tmp);
+ } else
+ type_pf_data_zero_out(chash_data(tmp, j));
+
+ h->elements--;
+}
+
+static int
+type_pf_resize(struct ip_set *set, gfp_t gfp_flags, bool retried)
+{
+ struct chash *h = set->data;
+ u8 htable_bits = h->htable_bits;
+ struct slist *t, *n;
+ const struct type_pf_elem *data;
+ u32 i, j;
+ u8 oflags, flags;
+ int ret;
+
+retry:
+ ret = 0;
+ htable_bits++;
+ if (!htable_bits)
+ /* In case we have plenty of memory :-) */
+ return -IPSET_ERR_HASH_FULL;
+ t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist),
+ gfp_flags, &flags);
+ if (!t)
+ return -ENOMEM;
+
+ write_lock_bh(&set->lock);
+ flags = oflags = set->flags;
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+next_slot:
+ slist_for_each(n, &h->htable[i]) {
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_data(n, j);
+ if (type_pf_data_isnull(data)) {
+ i++;
+ goto next_slot;
+ }
+ ret = type_pf_chash_readd(h, t, htable_bits,
+ data, gfp_flags);
+ if (ret < 0) {
+ write_unlock_bh(&set->lock);
+ chash_destroy(t, htable_bits, flags);
+ if (ret == -EAGAIN)
+ goto retry;
+ return ret;
+ }
+ }
+ }
+ }
+
+ n = h->htable;
+ i = h->htable_bits;
+
+ h->htable = t;
+ h->htable_bits = htable_bits;
+ set->flags = flags;
+ write_unlock_bh(&set->lock);
+
+ chash_destroy(n, i, oflags);
+
+ return 0;
+}
+
+static int
+type_pf_chash_add(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev, *t = h->htable;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash;
+
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
+#else
+ if (h->elements >= h->maxelem)
+#endif
+ return -IPSET_ERR_HASH_FULL;
+
+ hash = JHASH2(value, h->initval, h->htable_bits);
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ if (type_pf_data_equal(data, d))
+ return -IPSET_ERR_EXIST;
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_elem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_data(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, d);
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->nets[d->cidr-1].nets++ == 0)
+ add_cidr(h->nets, HOST_MASK, d->cidr);
+#endif
+ h->elements++;
+ return 0;
+}
+
+static int
+type_pf_chash_del(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev;
+ int i;
+ struct type_pf_elem *data;
+ u32 hash = JHASH2(value, h->initval, h->htable_bits);
+
+ slist_for_each_prev(prev, n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ return -IPSET_ERR_EXIST;
+ if (type_pf_data_equal(data, d)) {
+ type_pf_chash_del_elem(h, prev, n, i);
+ return 0;
+ }
+ }
+
+ return -IPSET_ERR_EXIST;
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+static inline int
+type_pf_chash_test_cidrs(struct ip_set *set,
+ struct type_pf_elem *d,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ int i, j = 0;
+ u32 hash;
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+retry:
+ pr_debug("test by nets");
+ for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ type_pf_data_netmask(d, h->nets[j].cidr);
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data)) {
+ j++;
+ goto retry;
+ }
+ if (type_pf_data_equal(data, d))
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+static inline int
+type_pf_chash_test(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *d = value;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ int i;
+ u32 hash;
+#ifdef IP_SET_HASH_WITH_NETS
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+ if (d->cidr == host_mask)
+ return type_pf_chash_test_cidrs(set, d, gfp_flags, timeout);
+#endif
+
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ return 0;
+ if (type_pf_data_equal(data, d))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+type_pf_head(struct ip_set *set, struct sk_buff *skb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *nested;
+ size_t memsize;
+
+ read_lock_bh(&set->lock);
+ memsize = chash_memsize(h, with_timeout(h->timeout)
+ ? sizeof(struct type_pf_telem)
+ : sizeof(struct type_pf_elem),
+ set->family == AF_INET ? 32 : 128);
+ read_unlock_bh(&set->lock);
+
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+ NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE,
+ htonl(jhash_size(h->htable_bits)));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem));
+#ifdef IP_SET_HASH_WITH_NETMASK
+ if (h->netmask != HOST_MASK)
+ NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
+#endif
+ NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
+ htonl(atomic_read(&set->ref) - 1));
+ NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
+ if (with_timeout(h->timeout))
+ NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout));
+ ipset_nest_end(skb, nested);
+
+ return 0;
+nla_put_failure:
+ return -EFAULT;
+}
+
+static int
+type_pf_list(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *atd, *nested;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ u32 first = cb->args[2];
+ int i;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ pr_debug("list hash set %s", set->name);
+ for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) {
+ slist_for_each(n, &h->htable[cb->args[2]]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_data(n, i);
+ if (type_pf_data_isnull(data))
+ break;
+ pr_debug("list hash %lu slist %p i %u",
+ cb->args[2], n, i);
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (cb->args[2] == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
+ }
+ if (type_pf_data_list(skb, data))
+ goto nla_put_failure;
+ ipset_nest_end(skb, nested);
+ }
+ }
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+static int
+type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
+ enum ipset_adt adt, u8 pf, u8 dim, u8 flags);
+static int
+type_pf_uadt(struct ip_set *set, struct nlattr *head, int len,
+ enum ipset_adt adt, u32 *lineno, u32 flags);
+
+static const struct ip_set_type_variant type_pf_variant __read_mostly = {
+ .kadt = type_pf_kadt,
+ .uadt = type_pf_uadt,
+ .adt = {
+ [IPSET_ADD] = type_pf_chash_add,
+ [IPSET_DEL] = type_pf_chash_del,
+ [IPSET_TEST] = type_pf_chash_test,
+ },
+ .destroy = type_pf_destroy,
+ .flush = type_pf_flush,
+ .head = type_pf_head,
+ .list = type_pf_list,
+ .resize = type_pf_resize,
+ .same_set = type_pf_same_set,
+};
+
+/* Flavour with timeout support */
+
+#define chash_tdata(n, i) \
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_telem))
+
+static inline u32
+type_pf_data_timeout(const struct type_pf_elem *data)
+{
+ const struct type_pf_telem *tdata =
+ (const struct type_pf_telem *) data;
+
+ return tdata->timeout;
+}
+
+static inline bool
+type_pf_data_expired(const struct type_pf_elem *data)
+{
+ const struct type_pf_telem *tdata =
+ (const struct type_pf_telem *) data;
+
+ return ip_set_timeout_expired(tdata->timeout);
+}
+
+static inline void
+type_pf_data_swap_timeout(struct type_pf_elem *src,
+ struct type_pf_elem *dst)
+{
+ struct type_pf_telem *x = (struct type_pf_telem *) src;
+ struct type_pf_telem *y = (struct type_pf_telem *) dst;
+
+ swap(x->timeout, y->timeout);
+}
+
+static inline void
+type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
+{
+ struct type_pf_telem *tdata = (struct type_pf_telem *) data;
+
+ tdata->timeout = ip_set_timeout_set(timeout);
+}
+
+static int
+type_pf_chash_treadd(struct chash *h, struct slist *t, u8 htable_bits,
+ const struct type_pf_elem *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash = JHASH2(value, h->initval, htable_bits);
+
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)) {
+ tmp = n;
+ goto found;
+ }
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_tdata(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ type_pf_data_copy(data, value);
+ type_pf_data_timeout_set(data, timeout);
+ return 0;
+}
+
+static void
+type_pf_chash_del_telem(struct chash *h, struct slist *prev,
+ struct slist *n, int i)
+{
+ struct type_pf_elem *d, *data = chash_tdata(n, i);
+ struct slist *tmp;
+ int j;
+
+ pr_debug("del %u", i);
+ if (n->next != NULL) {
+ for (prev = n, tmp = n->next;
+ tmp->next != NULL;
+ prev = tmp, tmp = tmp->next)
+ /* Find last array */;
+ j = 0;
+ } else {
+ /* Already at last array */
+ tmp = n;
+ j = i;
+ }
+ /* Find last non-empty element */
+ for (; j < h->array_size - 1; j++)
+ if (type_pf_data_isnull(chash_tdata(tmp, j + 1)))
+ break;
+
+ d = chash_tdata(tmp, j);
+ if (!(tmp == n && i == j)) {
+ type_pf_data_swap(data, d);
+ type_pf_data_swap_timeout(data, d);
+ }
+#ifdef IP_SET_HASH_WITH_NETS
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+#endif
+ if (j == 0) {
+ prev->next = NULL;
+ kfree(tmp);
+ } else
+ type_pf_data_zero_out(d);
+
+ h->elements--;
+}
+
+static void
+type_pf_chash_expire(struct chash *h)
+{
+ struct slist *n, *prev;
+ struct type_pf_elem *data;
+ u32 i;
+ int j;
+
+ for (i = 0; i < jhash_size(h->htable_bits); i++)
+ slist_for_each_prev(prev, n, &h->htable[i])
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_tdata(n, j);
+ if (type_pf_data_isnull(data))
+ break;
+ if (type_pf_data_expired(data)) {
+ pr_debug("expire %u/%u", i, j);
+ type_pf_chash_del_telem(h, prev, n, j);
+ }
+ }
+}
+
+static int
+type_pf_tresize(struct ip_set *set, gfp_t gfp_flags, bool retried)
+{
+ struct chash *h = set->data;
+ u8 htable_bits = h->htable_bits;
+ struct slist *t, *n;
+ const struct type_pf_elem *data;
+ u32 i, j;
+ u8 oflags, flags;
+ int ret;
+
+ /* Try to cleanup once */
+ if (!retried) {
+ i = h->elements;
+ write_lock_bh(&set->lock);
+ type_pf_chash_expire(set->data);
+ write_unlock_bh(&set->lock);
+ if (h->elements < i)
+ return 0;
+ }
+
+retry:
+ ret = 0;
+ htable_bits++;
+ if (!htable_bits)
+ /* In case we have plenty of memory :-) */
+ return -IPSET_ERR_HASH_FULL;
+ t = ip_set_alloc(jhash_size(htable_bits) * sizeof(struct slist),
+ gfp_flags, &flags);
+ if (!t)
+ return -ENOMEM;
+
+ write_lock_bh(&set->lock);
+ flags = oflags = set->flags;
+ for (i = 0; i < jhash_size(h->htable_bits); i++) {
+next_slot:
+ slist_for_each(n, &h->htable[i]) {
+ for (j = 0; j < h->array_size; j++) {
+ data = chash_tdata(n, j);
+ if (type_pf_data_isnull(data)) {
+ i++;
+ goto next_slot;
+ }
+ ret = type_pf_chash_treadd(h, t, htable_bits,
+ data, gfp_flags,
+ type_pf_data_timeout(data));
+ if (ret < 0) {
+ write_unlock_bh(&set->lock);
+ chash_destroy(t, htable_bits, flags);
+ if (ret == -EAGAIN)
+ goto retry;
+ return ret;
+ }
+ }
+ }
+ }
+
+ n = h->htable;
+ i = h->htable_bits;
+
+ h->htable = t;
+ h->htable_bits = htable_bits;
+ set->flags = flags;
+ write_unlock_bh(&set->lock);
+
+ chash_destroy(n, i, oflags);
+
+ return 0;
+}
+
+static int
+type_pf_chash_tadd(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev, *t = h->htable;
+ struct type_pf_elem *data;
+ void *tmp;
+ int i = 0, j = 0;
+ u32 hash;
+
+ if (h->elements >= h->maxelem)
+ /* FIXME: when set is full, we slow down here */
+ type_pf_chash_expire(h);
+#ifdef IP_SET_HASH_WITH_NETS
+ if (h->elements >= h->maxelem || h->nets[d->cidr-1].nets == UINT_MAX)
+#else
+ if (h->elements >= h->maxelem)
+#endif
+ return -IPSET_ERR_HASH_FULL;
+
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each_prev(prev, n, &t[hash]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)
+ || type_pf_data_expired(data)) {
+ tmp = n;
+ goto found;
+ }
+ if (type_pf_data_equal(data, d))
+ return -IPSET_ERR_EXIST;
+ }
+ j++;
+ }
+ if (j < h->chain_limit) {
+ tmp = kzalloc(h->array_size * sizeof(struct type_pf_telem)
+ + sizeof(struct slist), gfp_flags);
+ if (!tmp)
+ return -ENOMEM;
+ prev->next = (struct slist *) tmp;
+ data = chash_tdata(tmp, 0);
+ } else {
+ /* Rehashing */
+ return -EAGAIN;
+ }
+found:
+ if (type_pf_data_isnull(data)) {
+ h->elements++;
+#ifdef IP_SET_HASH_WITH_NETS
+ } else {
+ if (--h->nets[data->cidr-1].nets == 0)
+ del_cidr(h->nets, HOST_MASK, data->cidr);
+ }
+ if (h->nets[d->cidr-1].nets++ == 0) {
+ add_cidr(h->nets, HOST_MASK, d->cidr);
+#endif
+ }
+ type_pf_data_copy(data, d);
+ type_pf_data_timeout_set(data, timeout);
+ return 0;
+}
+
+static int
+type_pf_chash_tdel(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ const struct type_pf_elem *d = value;
+ struct slist *n, *prev;
+ int i, ret = 0;
+ struct type_pf_elem *data;
+ u32 hash = JHASH2(value, h->initval, h->htable_bits);
+
+ slist_for_each_prev(prev, n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data))
+ return -IPSET_ERR_EXIST;
+ if (type_pf_data_equal(data, d)) {
+ if (type_pf_data_expired(data))
+ ret = -IPSET_ERR_EXIST;
+ type_pf_chash_del_telem(h, prev, n, i);
+ return ret;
+ }
+ }
+
+ return -IPSET_ERR_EXIST;
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+static inline int
+type_pf_chash_ttest_cidrs(struct ip_set *set,
+ struct type_pf_elem *d,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *data;
+ struct slist *n;
+ int i, j = 0;
+ u32 hash;
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+retry:
+ for (; j < host_mask - 1 && h->nets[j].cidr; j++) {
+ type_pf_data_netmask(d, h->nets[j].cidr);
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data)) {
+ j++;
+ goto retry;
+ }
+ if (type_pf_data_equal(data, d))
+ return !type_pf_data_expired(data);
+ }
+ }
+ return 0;
+}
+#endif
+
+static inline int
+type_pf_chash_ttest(struct ip_set *set, void *value,
+ gfp_t gfp_flags, u32 timeout)
+{
+ struct chash *h = set->data;
+ struct type_pf_elem *data, *d = value;
+ struct slist *n;
+ int i;
+ u32 hash;
+#ifdef IP_SET_HASH_WITH_NETS
+ u8 host_mask = set->family == AF_INET ? 32 : 128;
+
+ if (d->cidr == host_mask)
+ return type_pf_chash_ttest_cidrs(set, d, gfp_flags,
+ timeout);
+#endif
+ hash = JHASH2(d, h->initval, h->htable_bits);
+ slist_for_each(n, &h->htable[hash])
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ if (type_pf_data_isnull(data))
+ return 0;
+ if (type_pf_data_equal(data, d))
+ return !type_pf_data_expired(data);
+ }
+ return 0;
+}
+
+static int
+type_pf_tlist(struct ip_set *set,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ const struct chash *h = set->data;
+ struct nlattr *atd, *nested;
+ struct slist *n;
+ const struct type_pf_elem *data;
+ u32 first = cb->args[2];
+ int i;
+
+ atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+ if (!atd)
+ return -EFAULT;
+ for (; cb->args[2] < jhash_size(h->htable_bits); cb->args[2]++) {
+ slist_for_each(n, &h->htable[cb->args[2]]) {
+ for (i = 0; i < h->array_size; i++) {
+ data = chash_tdata(n, i);
+ pr_debug("list %p %u", n, i);
+ if (type_pf_data_isnull(data))
+ break;
+ if (type_pf_data_expired(data))
+ continue;
+ pr_debug("do list %p %u", n, i);
+ nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+ if (!nested) {
+ if (cb->args[2] == first) {
+ nla_nest_cancel(skb, atd);
+ return -EFAULT;
+ } else
+ goto nla_put_failure;
+ }
+ if (type_pf_data_tlist(skb, data))
+ goto nla_put_failure;
+ ipset_nest_end(skb, nested);
+ }
+ }
+ }
+ ipset_nest_end(skb, atd);
+ /* Set listing finished */
+ cb->args[2] = 0;
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nested);
+ ipset_nest_end(skb, atd);
+ return 0;
+}
+
+static const struct ip_set_type_variant type_pf_tvariant __read_mostly = {
+ .kadt = type_pf_kadt,
+ .uadt = type_pf_uadt,
+ .adt = {
+ [IPSET_ADD] = type_pf_chash_tadd,
+ [IPSET_DEL] = type_pf_chash_tdel,
+ [IPSET_TEST] = type_pf_chash_ttest,
+ },
+ .destroy = type_pf_destroy,
+ .flush = type_pf_flush,
+ .head = type_pf_head,
+ .list = type_pf_tlist,
+ .resize = type_pf_tresize,
+ .same_set = type_pf_same_set,
+};
+
+static void
+type_pf_gc(unsigned long ul_set)
+{
+ struct ip_set *set = (struct ip_set *) ul_set;
+ struct chash *h = set->data;
+
+ pr_debug("called");
+ write_lock_bh(&set->lock);
+ type_pf_chash_expire(h);
+ write_unlock_bh(&set->lock);
+
+ h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+ add_timer(&h->gc);
+}
+
+static inline void
+type_pf_gc_init(struct ip_set *set)
+{
+ struct chash *h = set->data;
+
+ init_timer(&h->gc);
+ h->gc.data = (unsigned long) set;
+ h->gc.function = type_pf_gc;
+ h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+ add_timer(&h->gc);
+ pr_debug("gc initialized, run in every %u", IPSET_GC_PERIOD(h->timeout));
+}
+
+#undef type_pf_data_equal
+#undef type_pf_data_isnull
+#undef type_pf_data_copy
+#undef type_pf_data_swap
+#undef type_pf_data_zero_out
+#undef type_pf_data_list
+#undef type_pf_data_tlist
+
+#undef type_pf_elem
+#undef type_pf_telem
+#undef type_pf_data_timeout
+#undef type_pf_data_expired
+#undef type_pf_data_swap_timeout
+#undef type_pf_data_netmask
+#undef type_pf_data_timeout_set
+
+#undef type_pf_chash_readd
+#undef type_pf_chash_del_elem
+#undef type_pf_chash_add
+#undef type_pf_chash_del
+#undef type_pf_chash_test_cidrs
+#undef type_pf_chash_test
+
+#undef type_pf_chash_treadd
+#undef type_pf_chash_del_telem
+#undef type_pf_chash_expire
+#undef type_pf_chash_tadd
+#undef type_pf_chash_tdel
+#undef type_pf_chash_ttest_cidrs
+#undef type_pf_chash_ttest
+
+#undef type_pf_resize
+#undef type_pf_tresize
+#undef type_pf_flush
+#undef type_pf_destroy
+#undef type_pf_head
+#undef type_pf_list
+#undef type_pf_tlist
+#undef type_pf_same_set
+#undef type_pf_kadt
+#undef type_pf_uadt
+#undef type_pf_gc
+#undef type_pf_gc_init
+#undef type_pf_variant
+#undef type_pf_tvariant
diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h
index 855f12a..ffa89f1 100644
--- a/kernel/include/linux/netfilter/ip_set_getport.h
+++ b/kernel/include/linux/netfilter/ip_set_getport.h
@@ -8,8 +8,8 @@
#define IPSET_INVALID_PORT 65536
/* We must handle non-linear skbs */
-static uint32_t
-get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
+static bool
+get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port)
{
unsigned short protocol;
unsigned int protoff;
@@ -30,19 +30,19 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off);
if (protohdr < 0)
- return IPSET_INVALID_PORT;
+ return false;
protocol = protohdr;
fragoff = frag_off;
break;
}
default:
- return IPSET_INVALID_PORT;
+ return false;
}
/* See comments at tcp_match in ip_tables.c */
if (fragoff)
- return IPSET_INVALID_PORT;
+ return false;
switch (protocol) {
case IPPROTO_TCP: {
@@ -52,9 +52,10 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
if (th == NULL)
/* No choice either */
- return IPSET_INVALID_PORT;
+ return false;
- return flags[0] & IPSET_SRC ? th->source : th->dest;
+ *port = src ? th->source : th->dest;
+ break;
}
case IPPROTO_UDP: {
struct udphdr _udph;
@@ -63,14 +64,16 @@ get_port(uint8_t pf, const struct sk_buff *skb, const uint8_t *flags)
uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
if (uh == NULL)
/* No choice either */
- return IPSET_INVALID_PORT;
+ return false;
- return flags[0] & IPSET_SRC ? uh->source : uh->dest;
+ *port = src ? uh->source : uh->dest;
+ break;
}
default:
- return IPSET_INVALID_PORT;
+ return false;
}
+ return true;
}
-#endif /* __KERNEL__ */
+#endif /* __KERNEL__ */
#endif /*_IP_SET_GETPORT_H*/
diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h
index dd183b7..c1a6964 100644
--- a/kernel/include/linux/netfilter/ip_set_hash.h
+++ b/kernel/include/linux/netfilter/ip_set_hash.h
@@ -9,12 +9,11 @@ enum {
#ifdef __KERNEL__
-#define initval_t uint32_t
-
#define IPSET_DEFAULT_HASHSIZE 1024
+#define IPSET_MIMINAL_HASHSIZE 64
#define IPSET_DEFAULT_MAXELEM 65536
#define IPSET_DEFAULT_PROBES 4
-#define IPSET_DEFAULT_RESIZE 50
+#define IPSET_DEFAULT_RESIZE 100
#endif /* __KERNEL__ */
diff --git a/kernel/include/linux/netfilter/ip_set_jhash.h b/kernel/include/linux/netfilter/ip_set_jhash.h
index 90bfcc3..d5e0d6d 100644
--- a/kernel/include/linux/netfilter/ip_set_jhash.h
+++ b/kernel/include/linux/netfilter/ip_set_jhash.h
@@ -1,7 +1,6 @@
#ifndef _LINUX_JHASH_H
#define _LINUX_JHASH_H
-
-/* jhash.h: Jenkins hash support.
+/* jhash.c: Jenkins hash support.
*
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
*
@@ -17,141 +16,106 @@
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
- * Copyright (C) 2009 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* I've modified Bob's hash to be useful in the Linux kernel, and
- * any bugs present are my fault. Jozsef
+ * any bugs present are my fault. The generic jhash is left out intentionally.
+ * Jozsef
*/
-
-#define __rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-/* __jhash_mix - mix 3 32-bit values reversibly. */
-#define __jhash_mix(a,b,c) \
-{ \
- a -= c; a ^= __rot(c, 4); c += b; \
- b -= a; b ^= __rot(a, 6); a += c; \
- c -= b; c ^= __rot(b, 8); b += a; \
- a -= c; a ^= __rot(c,16); c += b; \
- b -= a; b ^= __rot(a,19); a += c; \
- c -= b; c ^= __rot(b, 4); b += a; \
+#ifdef __KERNEL__
+#include <asm/byteorder.h>
+
+/* Best hash sizes are of power of two */
+#define jhash_size(n) ((u32)1<<(n))
+/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
+#define jhash_mask(n) (jhash_size(n)-1)
+
+/* __jhash_rot - rotate 32 bit */
+#define __jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a,b,c) \
+{ \
+ a -= c; a ^= __jhash_rot(c, 4); c += b; \
+ b -= a; b ^= __jhash_rot(a, 6); a += c; \
+ c -= b; c ^= __jhash_rot(b, 8); b += a; \
+ a -= c; a ^= __jhash_rot(c,16); c += b; \
+ b -= a; b ^= __jhash_rot(a,19); a += c; \
+ c -= b; c ^= __jhash_rot(b, 4); b += a; \
}
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
-#define __jhash_final(a,b,c) \
-{ \
- c ^= b; c -= __rot(b,14); \
- a ^= c; a -= __rot(c,11); \
- b ^= a; b -= __rot(a,25); \
- c ^= b; c -= __rot(b,16); \
- a ^= c; a -= __rot(c,4); \
- b ^= a; b -= __rot(a,14); \
- c ^= b; c -= __rot(b,24); \
+#define __jhash_final(a,b,c) \
+{ \
+ c ^= b; c -= __jhash_rot(b,14); \
+ a ^= c; a -= __jhash_rot(c,11); \
+ b ^= a; b -= __jhash_rot(a,25); \
+ c ^= b; c -= __jhash_rot(b,16); \
+ a ^= c; a -= __jhash_rot(c,4); \
+ b ^= a; b -= __jhash_rot(a,14); \
+ c ^= b; c -= __jhash_rot(b,24); \
}
-/* An arbitrary value */
-#define JHASH_RANDOM_PARAM 0xdeadbeef
-
-/* The most generic version, hashes an arbitrary sequence
- * of bytes. No alignment or length assumptions are made about
- * the input key. The result depends on endianness.
- */
-static inline u32 jhash(const void *key, u32 length, u32 initval)
-{
- u32 a,b,c;
- const u8 *k = key;
-
- /* Set up the internal state */
- a = b = c = JHASH_RANDOM_PARAM + length + initval;
-
- /* all but the last block: affect some 32 bits of (a,b,c) */
- while (length > 12) {
- a += (k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24));
- b += (k[4] + ((u32)k[5]<<8) + ((u32)k[6]<<16) + ((u32)k[7]<<24));
- c += (k[8] + ((u32)k[9]<<8) + ((u32)k[10]<<16) + ((u32)k[11]<<24));
- __jhash_mix(a, b, c);
- length -= 12;
- k += 12;
- }
-
- /* last block: affect all 32 bits of (c) */
- /* all the case statements fall through */
- switch (length) {
- case 12: c += (u32)k[11]<<24;
- case 11: c += (u32)k[10]<<16;
- case 10: c += (u32)k[9]<<8;
- case 9 : c += k[8];
- case 8 : b += (u32)k[7]<<24;
- case 7 : b += (u32)k[6]<<16;
- case 6 : b += (u32)k[5]<<8;
- case 5 : b += k[4];
- case 4 : a += (u32)k[3]<<24;
- case 3 : a += (u32)k[2]<<16;
- case 2 : a += (u32)k[1]<<8;
- case 1 : a += k[0];
- __jhash_final(a, b, c);
- case 0 :
- break;
- }
-
- return c;
-}
+#define JHASH_INITVAL 0xdeadbeef
-/* A special optimized version that handles 1 or more of u32s.
- * The length parameter here is the number of u32s in the key.
+/* jhash2 - hash an array of u32's
+ * @k: the key which must be an array of u32's
+ * @length: the number of u32's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
*/
static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
{
u32 a, b, c;
/* Set up the internal state */
- a = b = c = JHASH_RANDOM_PARAM + (length<<2) + initval;
+ a = b = c = JHASH_INITVAL + (length<<2) + initval;
- /* handle most of the key */
+ /* Handle most of the key */
while (length > 3) {
a += k[0];
b += k[1];
c += k[2];
- __jhash_mix(a, b, c);
+ __jhash_mix(a,b,c);
length -= 3;
k += 3;
}
-
- /* handle the last 3 u32's */
- /* all the case statements fall through */
- switch (length) {
+
+ /* Handle the last 3 u32's: all the case statements fall through */
+ switch(length) {
case 3: c += k[2];
case 2: b += k[1];
case 1: a += k[0];
- __jhash_final(a, b, c);
- case 0: /* case 0: nothing left to add */
+ __jhash_final(a,b,c);
+ case 0: /* Nothing left to add */
break;
}
return c;
}
-/* A special ultra-optimized versions that knows they are hashing exactly
- * 3, 2 or 1 word(s).
- */
+/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
{
- a += JHASH_RANDOM_PARAM + initval;
- b += JHASH_RANDOM_PARAM + initval;
- c += JHASH_RANDOM_PARAM + initval;
-
- __jhash_final(a, b, c);
+ a += JHASH_INITVAL;
+ b += JHASH_INITVAL;
+ c += initval;
+ __jhash_final(a,b,c);
+
return c;
}
-
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
{
- return jhash_3words(0, a, b, initval);
+ return jhash_3words(a, b, 0, initval);
}
static inline u32 jhash_1word(u32 a, u32 initval)
{
- return jhash_3words(0, 0, a, initval);
+ return jhash_3words(a, 0, 0, initval);
}
+#endif /* __KERNEL__ */
+
#endif /* _LINUX_JHASH_H */
diff --git a/kernel/include/linux/netfilter/ip_set_kernel.h b/kernel/include/linux/netfilter/ip_set_kernel.h
new file mode 100644
index 0000000..d6e033b
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_kernel.h
@@ -0,0 +1,20 @@
+#ifndef _IP_SET_KERNEL_H
+#define _IP_SET_KERNEL_H
+
+/* Copyright (C) 2003-2010 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.
+ */
+
+#ifdef __KERNEL__
+
+/* Complete debug messages */
+#define pr_fmt(fmt) "%s %s[%i]: " fmt "\n", __FILE__, __func__, __LINE__
+
+#include <linux/kernel.h>
+
+#endif /* __KERNEL__ */
+
+#endif /*_IP_SET_H */
diff --git a/kernel/include/linux/netfilter/ip_set_list.h b/kernel/include/linux/netfilter/ip_set_list.h
new file mode 100644
index 0000000..c40643e
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_list.h
@@ -0,0 +1,21 @@
+#ifndef __IP_SET_LIST_H
+#define __IP_SET_LIST_H
+
+/* List type specific error codes */
+enum {
+ IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
+ IPSET_ERR_LOOP,
+ IPSET_ERR_BEFORE,
+ IPSET_ERR_NAMEREF,
+ IPSET_ERR_LIST_FULL,
+ IPSET_ERR_REF_EXIST,
+};
+
+#ifdef __KERNEL__
+
+#define IP_SET_LIST_DEFAULT_SIZE 8
+#define IP_SET_LIST_MIN_SIZE 4
+
+#endif /* __KERNEL__ */
+
+#endif /* __IP_SET_LIST_H */
diff --git a/kernel/include/linux/netfilter/ip_set_slist.h b/kernel/include/linux/netfilter/ip_set_slist.h
new file mode 100644
index 0000000..abc5afe
--- /dev/null
+++ b/kernel/include/linux/netfilter/ip_set_slist.h
@@ -0,0 +1,86 @@
+#ifndef _IP_SET_SLIST_H
+#define _IP_SET_SLIST_H
+
+#include <linux/stddef.h>
+#include <linux/prefetch.h>
+#include <asm/system.h>
+
+/*
+ * Single linked lists with a single pointer.
+ * Mostly useful for hash tables where the two pointer list head
+ * and list node is too wasteful.
+ */
+
+struct slist {
+ struct slist *next;
+};
+
+#define SLIST(name) struct slist name = { .next = NULL }
+#define INIT_SLIST(ptr) ((ptr)->next = NULL)
+
+#define slist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define slist_for_each(pos, head) \
+ for (pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define slist_for_each_prev(prev, pos, head) \
+ for (prev = head, pos = (head)->next; pos && ({ prefetch(pos->next); 1; }); \
+ prev = pos, pos = pos->next)
+
+#define slist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * slist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * slist_for_each_entry_safe - iterate over list of given type safe against
+ * removal of list entry
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct slist to use as a loop cursor.
+ * @n: another &struct slist to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the slist within the struct.
+ */
+#define slist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->next; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = slist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif /* _IP_SET_SLIST_H */
diff --git a/kernel/include/linux/netfilter/ip_set_timeout.h b/kernel/include/linux/netfilter/ip_set_timeout.h
index da18875..bf1cbf6 100644
--- a/kernel/include/linux/netfilter/ip_set_timeout.h
+++ b/kernel/include/linux/netfilter/ip_set_timeout.h
@@ -10,21 +10,33 @@
#ifdef __KERNEL__
-/* How often should the gc be run at a minimum */
+/* How often should the gc be run by default */
#define IPSET_GC_TIME (3 * 60)
/* Timeout period depending on the timeout value of the given set */
#define IPSET_GC_PERIOD(timeout) \
- max_t(uint32_t, (timeout)/10, IPSET_GC_TIME)
+ ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
-/* How much msec to sleep before retrying to destroy gc timer */
-#define IPSET_DESTROY_TIMER_SLEEP 10
+/* Set is defined without timeout support */
+#define IPSET_NO_TIMEOUT UINT_MAX
-/* Timing out etries: unset and permanent */
+#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT)
+
+static inline unsigned int
+ip_set_timeout_uget(struct nlattr *tb)
+{
+ unsigned int timeout = ip_set_get_h32(tb);
+
+ return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
+}
+
+#ifdef IP_SET_BITMAP_TIMEOUT
+
+/* Bitmap entry is unset */
#define IPSET_ELEM_UNSET 0
+/* Bitmap entry is set with no timeout value */
#define IPSET_ELEM_PERMANENT UINT_MAX/2
-#ifdef IP_SET_BITMAP_TIMEOUT
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
@@ -42,7 +54,7 @@ ip_set_timeout_expired(unsigned long timeout)
}
static inline unsigned long
-ip_set_timeout_set(uint32_t timeout)
+ip_set_timeout_set(u32 timeout)
{
unsigned long t;
@@ -56,7 +68,7 @@ ip_set_timeout_set(uint32_t timeout)
return t;
}
-static inline uint32_t
+static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ;
@@ -64,6 +76,9 @@ ip_set_timeout_get(unsigned long timeout)
#else
+/* Hash entry is set with no timeout value */
+#define IPSET_ELEM_UNSET 0
+
static inline bool
ip_set_timeout_test(unsigned long timeout)
{
@@ -77,7 +92,7 @@ ip_set_timeout_expired(unsigned long timeout)
}
static inline unsigned long
-ip_set_timeout_set(uint32_t timeout)
+ip_set_timeout_set(u32 timeout)
{
unsigned long t;
@@ -91,7 +106,7 @@ ip_set_timeout_set(uint32_t timeout)
return t;
}
-static inline uint32_t
+static inline u32
ip_set_timeout_get(unsigned long timeout)
{
return timeout == IPSET_ELEM_UNSET ? 0 : (timeout - jiffies)/HZ;
diff --git a/kernel/include/linux/netfilter/ipt_set.h b/kernel/include/linux/netfilter/ipt_set.h
deleted file mode 100644
index 2a18b93..0000000
--- a/kernel/include/linux/netfilter/ipt_set.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _IPT_SET_H
-#define _IPT_SET_H
-
-#include <linux/netfilter_ipv4/ip_set.h>
-
-struct ipt_set_info {
- ip_set_id_t index;
- u_int32_t flags[IP_SET_MAX_BINDINGS + 1];
-};
-
-/* match info */
-struct ipt_set_info_match {
- struct ipt_set_info match_set;
-};
-
-struct ipt_set_info_target {
- struct ipt_set_info add_set;
- struct ipt_set_info del_set;
-};
-
-#endif /*_IPT_SET_H*/
diff --git a/kernel/include/linux/netfilter/xt_set.h b/kernel/include/linux/netfilter/xt_set.h
new file mode 100644
index 0000000..949fa59
--- /dev/null
+++ b/kernel/include/linux/netfilter/xt_set.h
@@ -0,0 +1,55 @@
+#ifndef _XT_SET_H
+#define _XT_SET_H
+
+#include <linux/netfilter/ip_set.h>
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
+/*
+ * Option flags for kernel operations (xt_set_info_v0)
+ */
+#define IPSET_SRC 0x01 /* Source match/add */
+#define IPSET_DST 0x02 /* Destination match/add */
+#define IPSET_MATCH_INV 0x04 /* Inverse matching */
+
+struct xt_set_info_v0 {
+ ip_set_id_t index;
+ union {
+ u_int32_t flags[IPSET_DIM_MAX + 1];
+ struct {
+ u_int32_t __flags[IPSET_DIM_MAX];
+ u_int8_t dim;
+ u_int8_t flags;
+ } compat;
+ } u;
+};
+
+/* match and target infos */
+struct xt_set_info_match_v0 {
+ struct xt_set_info_v0 match_set;
+};
+
+struct xt_set_info_target_v0 {
+ struct xt_set_info_v0 add_set;
+ struct xt_set_info_v0 del_set;
+};
+
+/* Revision 1: current interface to netfilter/iptables */
+
+struct xt_set_info {
+ ip_set_id_t index;
+ u_int8_t dim;
+ u_int8_t flags;
+};
+
+/* match and target infos */
+struct xt_set_info_match {
+ struct xt_set_info match_set;
+};
+
+struct xt_set_info_target {
+ struct xt_set_info add_set;
+ struct xt_set_info del_set;
+};
+
+#endif /*_XT_SET_H*/