X-Git-Url: http://git.onelab.eu/?p=linux-2.6.git;a=blobdiff_plain;f=linux-2.6-250-ipsets.patch;h=ce87f8062e15e54273795362fbb426afcb82cf96;hp=0700eb01cff9d4c0a73d7cd2eb8843e9071c4102;hb=HEAD;hpb=25f1eae35e636f9c0e7fbfdddd7a8dc5fa33b0da diff --git a/linux-2.6-250-ipsets.patch b/linux-2.6-250-ipsets.patch index 0700eb01c..ce87f8062 100644 --- a/linux-2.6-250-ipsets.patch +++ b/linux-2.6-250-ipsets.patch @@ -1,9 +1,52 @@ +From 810b98f82c5999118c2f74d5a22c7f0836771c7b Mon Sep 17 00:00:00 2001 +From: S.Çağlar Onur +Date: Tue, 7 Dec 2010 11:05:31 -0500 +Subject: [PATCH] linux-2.6-250-ipsets.patch + +--- + include/linux/netfilter_ipv4/ip_set.h | 561 +++++++ + include/linux/netfilter_ipv4/ip_set_bitmaps.h | 120 ++ + include/linux/netfilter_ipv4/ip_set_compat.h | 92 ++ + include/linux/netfilter_ipv4/ip_set_getport.h | 48 + + include/linux/netfilter_ipv4/ip_set_hashes.h | 314 ++++ + include/linux/netfilter_ipv4/ip_set_iphash.h | 30 + + include/linux/netfilter_ipv4/ip_set_ipmap.h | 57 + + include/linux/netfilter_ipv4/ip_set_ipporthash.h | 33 + + include/linux/netfilter_ipv4/ip_set_ipportiphash.h | 39 + + .../linux/netfilter_ipv4/ip_set_ipportnethash.h | 42 + + include/linux/netfilter_ipv4/ip_set_iptree.h | 39 + + include/linux/netfilter_ipv4/ip_set_iptreemap.h | 40 + + include/linux/netfilter_ipv4/ip_set_jhash.h | 157 ++ + include/linux/netfilter_ipv4/ip_set_macipmap.h | 39 + + include/linux/netfilter_ipv4/ip_set_malloc.h | 153 ++ + include/linux/netfilter_ipv4/ip_set_nethash.h | 31 + + include/linux/netfilter_ipv4/ip_set_portmap.h | 25 + + include/linux/netfilter_ipv4/ip_set_setlist.h | 26 + + include/linux/netfilter_ipv4/ipt_set.h | 21 + + net/ipv4/netfilter/Kconfig | 117 ++ + net/ipv4/netfilter/Makefile | 13 + + net/ipv4/netfilter/ip_set.c | 1531 ++++++++++++++++++++ + net/ipv4/netfilter/ip_set_iphash.c | 164 +++ + net/ipv4/netfilter/ip_set_ipmap.c | 158 ++ + net/ipv4/netfilter/ip_set_ipporthash.c | 197 +++ + net/ipv4/netfilter/ip_set_ipportiphash.c | 215 +++ + net/ipv4/netfilter/ip_set_ipportnethash.c | 298 ++++ + net/ipv4/netfilter/ip_set_iptree.c | 464 ++++++ + net/ipv4/netfilter/ip_set_iptreemap.c | 699 +++++++++ + net/ipv4/netfilter/ip_set_macipmap.c | 179 +++ + net/ipv4/netfilter/ip_set_nethash.c | 218 +++ + net/ipv4/netfilter/ip_set_portmap.c | 130 ++ + net/ipv4/netfilter/ip_set_setlist.c | 324 +++++ + net/ipv4/netfilter/ipt_SET.c | 256 ++++ + net/ipv4/netfilter/ipt_set.c | 253 ++++ + 35 files changed, 7083 insertions(+), 0 deletions(-) + diff --git a/include/linux/netfilter_ipv4/ip_set.h b/include/linux/netfilter_ipv4/ip_set.h new file mode 100644 -index 0000000..92a746e +index 0000000..da17319 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set.h -@@ -0,0 +1,498 @@ +@@ -0,0 +1,561 @@ +#ifndef _IP_SET_H +#define _IP_SET_H + @@ -46,7 +89,8 @@ index 0000000..92a746e +/* + * Used so that the kernel module and ipset-binary can match their versions + */ -+#define IP_SET_PROTOCOL_VERSION 2 ++#define IP_SET_PROTOCOL_UNALIGNED 3 ++#define IP_SET_PROTOCOL_VERSION 4 + +#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ + @@ -93,6 +137,9 @@ index 0000000..92a746e +#define IPSET_TYPE_PORT 0x02 /* Port type of set */ +#define IPSET_DATA_SINGLE 0x04 /* Single data storage */ +#define IPSET_DATA_DOUBLE 0x08 /* Double data storage */ ++#define IPSET_DATA_TRIPLE 0x10 /* Triple data storage */ ++#define IPSET_TYPE_IP1 0x20 /* IP address type of set */ ++#define IPSET_TYPE_SETNAME 0x40 /* setname type of set */ + +/* Reserved keywords */ +#define IPSET_TOKEN_DEFAULT ":default:" @@ -231,7 +278,7 @@ index 0000000..92a746e +struct ip_set_req_setnames { + unsigned op; + ip_set_id_t index; /* set to list/save */ -+ size_t size; /* size to get setdata/bindings */ ++ u_int32_t size; /* size to get setdata */ + /* followed by sets number of struct ip_set_name_list */ +}; + @@ -253,9 +300,9 @@ index 0000000..92a746e + ip_set_id_t index; + ip_set_id_t binding; + u_int32_t ref; -+ size_t header_size; /* Set header data of header_size */ -+ size_t members_size; /* Set members data of members_size */ -+ size_t bindings_size; /* Set bindings data of bindings_size */ ++ u_int32_t header_size; /* Set header data of header_size */ ++ u_int32_t members_size; /* Set members data of members_size */ ++ u_int32_t bindings_size;/* Set bindings data of bindings_size */ +}; + +struct ip_set_hash_list { @@ -272,8 +319,8 @@ index 0000000..92a746e +struct ip_set_save { + ip_set_id_t index; + ip_set_id_t binding; -+ size_t header_size; /* Set header data of header_size */ -+ size_t members_size; /* Set members data of members_size */ ++ u_int32_t header_size; /* Set header data of header_size */ ++ u_int32_t members_size; /* Set members data of members_size */ +}; + +/* At restoring, ip == 0 means default binding for the given set: */ @@ -293,8 +340,8 @@ index 0000000..92a746e + char name[IP_SET_MAXNAMELEN]; + char typename[IP_SET_MAXNAMELEN]; + ip_set_id_t index; -+ size_t header_size; /* Create data of header_size */ -+ size_t members_size; /* Set members data of members_size */ ++ u_int32_t header_size; /* Create data of header_size */ ++ u_int32_t members_size; /* Set members data of members_size */ +}; + +static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) @@ -302,7 +349,17 @@ index 0000000..92a746e + return 4 * ((((b - a + 8) / 8) + 3) / 4); +} + ++/* General limit for the elements in a set */ ++#define MAX_RANGE 0x0000FFFF ++ ++/* Alignment: 'unsigned long' unsupported */ ++#define IPSET_ALIGNTO 4 ++#define IPSET_ALIGN(len) (((len) + IPSET_ALIGNTO - 1) & ~(IPSET_ALIGNTO - 1)) ++#define IPSET_VALIGN(len, old) ((old) ? (len) : IPSET_ALIGN(len)) ++ +#ifdef __KERNEL__ ++#include ++#include + +#define ip_set_printk(format, args...) \ + do { \ @@ -348,22 +405,19 @@ index 0000000..92a746e + */ + int (*testip_kernel) (struct ip_set *set, + const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); ++ const u_int32_t *flags); + + /* test for IP in set (userspace: ipset -T set IP) + * return 0 if not in set, 1 if in set. + */ + int (*testip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); ++ const void *data, u_int32_t size); + + /* + * Size of the data structure passed by when + * adding/deletin/testing an entry. + */ -+ size_t reqsize; ++ u_int32_t reqsize; + + /* Add IP into set (userspace: ipset -A set IP) + * Return -EEXIST if the address is already in the set, @@ -371,8 +425,7 @@ index 0000000..92a746e + * If the address was not already in the set, 0 is returned. + */ + int (*addip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); ++ const void *data, u_int32_t size); + + /* Add IP into set (kernel: iptables ... -j SET set src|dst) + * Return -EEXIST if the address is already in the set, @@ -380,10 +433,8 @@ index 0000000..92a746e + * If the address was not already in the set, 0 is returned. + */ + int (*addip_kernel) (struct ip_set *set, -+ const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); ++ const struct sk_buff * skb, ++ const u_int32_t *flags); + + /* remove IP from set (userspace: ipset -D set --entry x) + * Return -EEXIST if the address is NOT in the set, @@ -391,8 +442,7 @@ index 0000000..92a746e + * If the address really was in the set, 0 is returned. + */ + int (*delip) (struct ip_set *set, -+ const void *data, size_t size, -+ ip_set_ip_t *ip); ++ const void *data, u_int32_t size); + + /* remove IP from set (kernel: iptables ... -j SET --entry x) + * Return -EEXIST if the address is NOT in the set, @@ -400,15 +450,13 @@ index 0000000..92a746e + * If the address really was in the set, 0 is returned. + */ + int (*delip_kernel) (struct ip_set *set, -+ const struct sk_buff * skb, -+ ip_set_ip_t *ip, -+ const u_int32_t *flags, -+ unsigned char index); ++ const struct sk_buff * skb, ++ const u_int32_t *flags); + + /* new set creation - allocated type specific items + */ + int (*create) (struct ip_set *set, -+ const void *data, size_t size); ++ const void *data, u_int32_t size); + + /* retry the operation after successfully tweaking the set + */ @@ -427,7 +475,7 @@ index 0000000..92a746e + + /* Listing: size needed for header + */ -+ size_t header_size; ++ u_int32_t header_size; + + /* Listing: Get the header + * @@ -441,7 +489,7 @@ index 0000000..92a746e + + /* Listing: Get the size for the set members + */ -+ int (*list_members_size) (const struct ip_set *set); ++ int (*list_members_size) (const struct ip_set *set, char dont_align); + + /* Listing: Get the set members + * @@ -451,7 +499,7 @@ index 0000000..92a746e + * correct. + */ + void (*list_members) (const struct ip_set *set, -+ void *data); ++ void *data, char dont_align); + + char typename[IP_SET_MAXNAMELEN]; + unsigned char features; @@ -469,42 +517,698 @@ index 0000000..92a746e + char name[IP_SET_MAXNAMELEN]; /* the name of the set */ + rwlock_t lock; /* lock for concurrency control */ + ip_set_id_t id; /* set id for swapping */ -+ ip_set_id_t binding; /* default binding for the set */ + atomic_t ref; /* in kernel and in hash references */ + struct ip_set_type *type; /* the set types */ + void *data; /* pooltype specific data */ +}; + -+/* Structure to bind set elements to sets */ -+struct ip_set_hash { -+ struct list_head list; /* list of clashing entries in hash */ -+ ip_set_ip_t ip; /* ip from set */ -+ ip_set_id_t id; /* set id */ -+ ip_set_id_t binding; /* set we bind the element to */ -+}; -+ +/* register and unregister set references */ +extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); -+extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); -+extern void ip_set_put(ip_set_id_t id); ++extern ip_set_id_t ip_set_get_byindex(ip_set_id_t index); ++extern void ip_set_put_byindex(ip_set_id_t index); ++extern ip_set_id_t ip_set_id(ip_set_id_t index); ++extern ip_set_id_t __ip_set_get_byname(const char name[IP_SET_MAXNAMELEN], ++ struct ip_set **set); ++extern void __ip_set_put_byindex(ip_set_id_t index); + +/* API for iptables set match, and SET target */ -+extern void ip_set_addip_kernel(ip_set_id_t id, -+ const struct sk_buff *skb, -+ const u_int32_t *flags); -+extern void ip_set_delip_kernel(ip_set_id_t id, -+ const struct sk_buff *skb, -+ const u_int32_t *flags); ++extern int ip_set_addip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern int ip_set_delip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); +extern int ip_set_testip_kernel(ip_set_id_t id, + const struct sk_buff *skb, + const u_int32_t *flags); + ++/* Macros to generate functions */ ++ ++#define STRUCT(pre, type) CONCAT2(pre, type) ++#define CONCAT2(pre, type) struct pre##type ++ ++#define FNAME(pre, mid, post) CONCAT3(pre, mid, post) ++#define CONCAT3(pre, mid, post) pre##mid##post ++ ++#define UADT0(type, adt, args...) \ ++static int \ ++FNAME(type,_u,adt)(struct ip_set *set, const void *data, u_int32_t size)\ ++{ \ ++ const STRUCT(ip_set_req_,type) *req = data; \ ++ \ ++ return FNAME(type,_,adt)(set , ## args); \ ++} ++ ++#define UADT(type, adt, args...) \ ++ UADT0(type, adt, req->ip , ## args) ++ ++#define KADT(type, adt, getfn, args...) \ ++static int \ ++FNAME(type,_k,adt)(struct ip_set *set, \ ++ const struct sk_buff *skb, \ ++ const u_int32_t *flags) \ ++{ \ ++ ip_set_ip_t ip = getfn(skb, flags); \ ++ \ ++ KADT_CONDITION \ ++ return FNAME(type,_,adt)(set, ip , ##args); \ ++} ++ ++#define REGISTER_MODULE(type) \ ++static int __init ip_set_##type##_init(void) \ ++{ \ ++ init_max_page_size(); \ ++ return ip_set_register_set_type(&ip_set_##type); \ ++} \ ++ \ ++static void __exit ip_set_##type##_fini(void) \ ++{ \ ++ /* FIXME: possible race with ip_set_create() */ \ ++ ip_set_unregister_set_type(&ip_set_##type); \ ++} \ ++ \ ++module_init(ip_set_##type##_init); \ ++module_exit(ip_set_##type##_fini); ++ ++/* Common functions */ ++ ++static inline ip_set_ip_t ++ipaddr(const struct sk_buff *skb, const u_int32_t *flags) ++{ ++ return ntohl(flags[0] & IPSET_SRC ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr); ++} ++ ++#define jhash_ip(map, i, ip) jhash_1word(ip, *(map->initval + i)) ++ ++#define pack_ip_port(map, ip, port) \ ++ (port + ((ip - ((map)->first_ip)) << 16)) ++ +#endif /* __KERNEL__ */ + ++#define UNUSED __attribute__ ((unused)) ++ +#endif /*_IP_SET_H*/ +diff --git a/include/linux/netfilter_ipv4/ip_set_bitmaps.h b/include/linux/netfilter_ipv4/ip_set_bitmaps.h +new file mode 100644 +index 0000000..da3493f +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_bitmaps.h +@@ -0,0 +1,120 @@ ++#ifndef __IP_SET_BITMAPS_H ++#define __IP_SET_BITMAPS_H ++ ++/* Macros to generate functions */ ++ ++#ifdef __KERNEL__ ++#define BITMAP_CREATE(type) \ ++static int \ ++type##_create(struct ip_set *set, const void *data, u_int32_t size) \ ++{ \ ++ int newbytes; \ ++ const struct ip_set_req_##type##_create *req = data; \ ++ struct ip_set_##type *map; \ ++ \ ++ if (req->from > req->to) { \ ++ DP("bad range"); \ ++ return -ENOEXEC; \ ++ } \ ++ \ ++ map = kmalloc(sizeof(struct ip_set_##type), GFP_KERNEL); \ ++ if (!map) { \ ++ DP("out of memory for %zu bytes", \ ++ sizeof(struct ip_set_##type)); \ ++ return -ENOMEM; \ ++ } \ ++ map->first_ip = req->from; \ ++ map->last_ip = req->to; \ ++ \ ++ newbytes = __##type##_create(req, map); \ ++ if (newbytes < 0) { \ ++ kfree(map); \ ++ return newbytes; \ ++ } \ ++ \ ++ map->size = newbytes; \ ++ map->members = ip_set_malloc(newbytes); \ ++ if (!map->members) { \ ++ DP("out of memory for %i bytes", newbytes); \ ++ kfree(map); \ ++ return -ENOMEM; \ ++ } \ ++ memset(map->members, 0, newbytes); \ ++ \ ++ set->data = map; \ ++ return 0; \ ++} ++ ++#define BITMAP_DESTROY(type) \ ++static void \ ++type##_destroy(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data; \ ++ \ ++ ip_set_free(map->members, map->size); \ ++ kfree(map); \ ++ \ ++ set->data = NULL; \ ++} ++ ++#define BITMAP_FLUSH(type) \ ++static void \ ++type##_flush(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data; \ ++ memset(map->members, 0, map->size); \ ++} ++ ++#define BITMAP_LIST_HEADER(type) \ ++static void \ ++type##_list_header(const struct ip_set *set, void *data) \ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ struct ip_set_req_##type##_create *header = data; \ ++ \ ++ header->from = map->first_ip; \ ++ header->to = map->last_ip; \ ++ __##type##_list_header(map, header); \ ++} ++ ++#define BITMAP_LIST_MEMBERS_SIZE(type, dtype, sizeid, testfn) \ ++static int \ ++type##_list_members_size(const struct ip_set *set, char dont_align) \ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ ip_set_ip_t i, elements = 0; \ ++ \ ++ if (dont_align) \ ++ return map->size; \ ++ \ ++ for (i = 0; i < sizeid; i++) \ ++ if (testfn) \ ++ elements++; \ ++ \ ++ return elements * IPSET_ALIGN(sizeof(dtype)); \ ++} ++ ++#define IP_SET_TYPE(type, __features) \ ++struct ip_set_type ip_set_##type = { \ ++ .typename = #type, \ ++ .features = __features, \ ++ .protocol_version = IP_SET_PROTOCOL_VERSION, \ ++ .create = &type##_create, \ ++ .destroy = &type##_destroy, \ ++ .flush = &type##_flush, \ ++ .reqsize = sizeof(struct ip_set_req_##type), \ ++ .addip = &type##_uadd, \ ++ .addip_kernel = &type##_kadd, \ ++ .delip = &type##_udel, \ ++ .delip_kernel = &type##_kdel, \ ++ .testip = &type##_utest, \ ++ .testip_kernel = &type##_ktest, \ ++ .header_size = sizeof(struct ip_set_req_##type##_create),\ ++ .list_header = &type##_list_header, \ ++ .list_members_size = &type##_list_members_size, \ ++ .list_members = &type##_list_members, \ ++ .me = THIS_MODULE, \ ++}; ++#endif /* __KERNEL */ ++ ++#endif /* __IP_SET_BITMAPS_H */ +diff --git a/include/linux/netfilter_ipv4/ip_set_compat.h b/include/linux/netfilter_ipv4/ip_set_compat.h +new file mode 100644 +index 0000000..9f17397 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_compat.h +@@ -0,0 +1,92 @@ ++#ifndef _IP_SET_COMPAT_H ++#define _IP_SET_COMPAT_H ++ ++#ifdef __KERNEL__ ++#include ++ ++/* Arrgh */ ++#ifdef MODULE ++#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo) ++#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo) ++#else ++#define __MOD_INC(foo) 1 ++#define __MOD_DEC(foo) ++#endif ++ ++/* Backward compatibility */ ++#ifndef __nocast ++#define __nocast ++#endif ++#ifndef __bitwise__ ++#define __bitwise__ ++#endif ++ ++/* Compatibility glue code */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#include ++#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED ++#define try_module_get(x) __MOD_INC(x) ++#define module_put(x) __MOD_DEC(x) ++#define __clear_bit(nr, addr) clear_bit(nr, addr) ++#define __set_bit(nr, addr) set_bit(nr, addr) ++#define __test_and_set_bit(nr, addr) test_and_set_bit(nr, addr) ++#define __test_and_clear_bit(nr, addr) test_and_clear_bit(nr, addr) ++ ++typedef unsigned __bitwise__ gfp_t; ++ ++static inline void *kzalloc(size_t size, gfp_t flags) ++{ ++ void *data = kmalloc(size, flags); ++ ++ if (data) ++ memset(data, 0, size); ++ ++ return data; ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++#define __KMEM_CACHE_T__ kmem_cache_t ++#else ++#define __KMEM_CACHE_T__ struct kmem_cache ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ++#define ip_hdr(skb) ((skb)->nh.iph) ++#define skb_mac_header(skb) ((skb)->mac.raw) ++#define eth_hdr(skb) ((struct ethhdr *)skb_mac_header(skb)) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++#include ++#define KMEM_CACHE_CREATE(name, size) \ ++ kmem_cache_create(name, size, 0, 0, NULL, NULL) ++#else ++#define KMEM_CACHE_CREATE(name, size) \ ++ kmem_cache_create(name, size, 0, 0, NULL) ++#endif ++ ++#ifndef NIPQUAD ++#define NIPQUAD(addr) \ ++ ((unsigned char *)&addr)[0], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[3] ++#endif ++ ++#ifndef HIPQUAD ++#if defined(__LITTLE_ENDIAN) ++#define HIPQUAD(addr) \ ++ ((unsigned char *)&addr)[3], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[0] ++#elif defined(__BIG_ENDIAN) ++#define HIPQUAD NIPQUAD ++#else ++#error "Please fix asm/byteorder.h" ++#endif /* __LITTLE_ENDIAN */ ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* _IP_SET_COMPAT_H */ +diff --git a/include/linux/netfilter_ipv4/ip_set_getport.h b/include/linux/netfilter_ipv4/ip_set_getport.h +new file mode 100644 +index 0000000..18ed729 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_getport.h +@@ -0,0 +1,48 @@ ++#ifndef _IP_SET_GETPORT_H ++#define _IP_SET_GETPORT_H ++ ++#ifdef __KERNEL__ ++ ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, const u_int32_t *flags) ++{ ++ struct iphdr *iph = ip_hdr(skb); ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags[0] & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags[0] & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_GETPORT_H*/ +diff --git a/include/linux/netfilter_ipv4/ip_set_hashes.h b/include/linux/netfilter_ipv4/ip_set_hashes.h +new file mode 100644 +index 0000000..8eeced3 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_hashes.h +@@ -0,0 +1,314 @@ ++#ifndef __IP_SET_HASHES_H ++#define __IP_SET_HASHES_H ++ ++#define initval_t uint32_t ++ ++/* Macros to generate functions */ ++ ++#ifdef __KERNEL__ ++#define HASH_RETRY0(type, dtype, cond) \ ++static int \ ++type##_retry(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data, *tmp; \ ++ dtype *elem; \ ++ void *members; \ ++ u_int32_t i, hashsize = map->hashsize; \ ++ int res; \ ++ \ ++ if (map->resize == 0) \ ++ return -ERANGE; \ ++ \ ++ again: \ ++ res = 0; \ ++ \ ++ /* Calculate new hash size */ \ ++ hashsize += (hashsize * map->resize)/100; \ ++ if (hashsize == map->hashsize) \ ++ hashsize++; \ ++ \ ++ ip_set_printk("rehashing of set %s triggered: " \ ++ "hashsize grows from %lu to %lu", \ ++ set->name, \ ++ (long unsigned)map->hashsize, \ ++ (long unsigned)hashsize); \ ++ \ ++ tmp = kmalloc(sizeof(struct ip_set_##type) \ ++ + map->probes * sizeof(initval_t), GFP_ATOMIC); \ ++ if (!tmp) { \ ++ DP("out of memory for %zu bytes", \ ++ sizeof(struct ip_set_##type) \ ++ + map->probes * sizeof(initval_t)); \ ++ return -ENOMEM; \ ++ } \ ++ tmp->members = harray_malloc(hashsize, sizeof(dtype), GFP_ATOMIC);\ ++ if (!tmp->members) { \ ++ DP("out of memory for %zu bytes", hashsize * sizeof(dtype));\ ++ kfree(tmp); \ ++ return -ENOMEM; \ ++ } \ ++ tmp->hashsize = hashsize; \ ++ tmp->elements = 0; \ ++ tmp->probes = map->probes; \ ++ tmp->resize = map->resize; \ ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(initval_t));\ ++ __##type##_retry(tmp, map); \ ++ \ ++ write_lock_bh(&set->lock); \ ++ map = set->data; /* Play safe */ \ ++ for (i = 0; i < map->hashsize && res == 0; i++) { \ ++ elem = HARRAY_ELEM(map->members, dtype *, i); \ ++ if (cond) \ ++ res = __##type##_add(tmp, elem); \ ++ } \ ++ if (res) { \ ++ /* Failure, try again */ \ ++ write_unlock_bh(&set->lock); \ ++ harray_free(tmp->members); \ ++ kfree(tmp); \ ++ goto again; \ ++ } \ ++ \ ++ /* Success at resizing! */ \ ++ members = map->members; \ ++ \ ++ map->hashsize = tmp->hashsize; \ ++ map->members = tmp->members; \ ++ write_unlock_bh(&set->lock); \ ++ \ ++ harray_free(members); \ ++ kfree(tmp); \ ++ \ ++ return 0; \ ++} ++ ++#define HASH_RETRY(type, dtype) \ ++ HASH_RETRY0(type, dtype, *elem) ++ ++#define HASH_RETRY2(type, dtype) \ ++ HASH_RETRY0(type, dtype, elem->ip || elem->ip1) ++ ++#define HASH_CREATE(type, dtype) \ ++static int \ ++type##_create(struct ip_set *set, const void *data, u_int32_t size) \ ++{ \ ++ const struct ip_set_req_##type##_create *req = data; \ ++ struct ip_set_##type *map; \ ++ uint16_t i; \ ++ \ ++ if (req->hashsize < 1) { \ ++ ip_set_printk("hashsize too small"); \ ++ return -ENOEXEC; \ ++ } \ ++ \ ++ if (req->probes < 1) { \ ++ ip_set_printk("probes too small"); \ ++ return -ENOEXEC; \ ++ } \ ++ \ ++ map = kmalloc(sizeof(struct ip_set_##type) \ ++ + req->probes * sizeof(initval_t), GFP_KERNEL); \ ++ if (!map) { \ ++ DP("out of memory for %zu bytes", \ ++ sizeof(struct ip_set_##type) \ ++ + req->probes * sizeof(initval_t)); \ ++ return -ENOMEM; \ ++ } \ ++ for (i = 0; i < req->probes; i++) \ ++ get_random_bytes(((initval_t *) map->initval)+i, 4); \ ++ map->elements = 0; \ ++ map->hashsize = req->hashsize; \ ++ map->probes = req->probes; \ ++ map->resize = req->resize; \ ++ if (__##type##_create(req, map)) { \ ++ kfree(map); \ ++ return -ENOEXEC; \ ++ } \ ++ map->members = harray_malloc(map->hashsize, sizeof(dtype), GFP_KERNEL);\ ++ if (!map->members) { \ ++ DP("out of memory for %zu bytes", map->hashsize * sizeof(dtype));\ ++ kfree(map); \ ++ return -ENOMEM; \ ++ } \ ++ \ ++ set->data = map; \ ++ return 0; \ ++} ++ ++#define HASH_DESTROY(type) \ ++static void \ ++type##_destroy(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data; \ ++ \ ++ harray_free(map->members); \ ++ kfree(map); \ ++ \ ++ set->data = NULL; \ ++} ++ ++#define HASH_FLUSH(type, dtype) \ ++static void \ ++type##_flush(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data; \ ++ harray_flush(map->members, map->hashsize, sizeof(dtype)); \ ++ map->elements = 0; \ ++} ++ ++#define HASH_FLUSH_CIDR(type, dtype) \ ++static void \ ++type##_flush(struct ip_set *set) \ ++{ \ ++ struct ip_set_##type *map = set->data; \ ++ harray_flush(map->members, map->hashsize, sizeof(dtype)); \ ++ memset(map->cidr, 0, sizeof(map->cidr)); \ ++ memset(map->nets, 0, sizeof(map->nets)); \ ++ map->elements = 0; \ ++} ++ ++#define HASH_LIST_HEADER(type) \ ++static void \ ++type##_list_header(const struct ip_set *set, void *data) \ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ struct ip_set_req_##type##_create *header = data; \ ++ \ ++ header->hashsize = map->hashsize; \ ++ header->probes = map->probes; \ ++ header->resize = map->resize; \ ++ __##type##_list_header(map, header); \ ++} ++ ++#define HASH_LIST_MEMBERS_SIZE(type, dtype) \ ++static int \ ++type##_list_members_size(const struct ip_set *set, char dont_align) \ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ \ ++ return (map->elements * IPSET_VALIGN(sizeof(dtype), dont_align));\ ++} ++ ++#define HASH_LIST_MEMBERS(type, dtype) \ ++static void \ ++type##_list_members(const struct ip_set *set, void *data, char dont_align)\ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ dtype *elem, *d; \ ++ uint32_t i, n = 0; \ ++ \ ++ for (i = 0; i < map->hashsize; i++) { \ ++ elem = HARRAY_ELEM(map->members, dtype *, i); \ ++ if (*elem) { \ ++ d = data + n * IPSET_VALIGN(sizeof(dtype), dont_align);\ ++ *d = *elem; \ ++ n++; \ ++ } \ ++ } \ ++} ++ ++#define HASH_LIST_MEMBERS_MEMCPY(type, dtype, nonzero) \ ++static void \ ++type##_list_members(const struct ip_set *set, void *data, char dont_align)\ ++{ \ ++ const struct ip_set_##type *map = set->data; \ ++ dtype *elem; \ ++ uint32_t i, n = 0; \ ++ \ ++ for (i = 0; i < map->hashsize; i++) { \ ++ elem = HARRAY_ELEM(map->members, dtype *, i); \ ++ if (nonzero) { \ ++ memcpy(data + n * IPSET_VALIGN(sizeof(dtype), dont_align),\ ++ elem, sizeof(dtype)); \ ++ n++; \ ++ } \ ++ } \ ++} ++ ++#define IP_SET_RTYPE(type, __features) \ ++struct ip_set_type ip_set_##type = { \ ++ .typename = #type, \ ++ .features = __features, \ ++ .protocol_version = IP_SET_PROTOCOL_VERSION, \ ++ .create = &type##_create, \ ++ .retry = &type##_retry, \ ++ .destroy = &type##_destroy, \ ++ .flush = &type##_flush, \ ++ .reqsize = sizeof(struct ip_set_req_##type), \ ++ .addip = &type##_uadd, \ ++ .addip_kernel = &type##_kadd, \ ++ .delip = &type##_udel, \ ++ .delip_kernel = &type##_kdel, \ ++ .testip = &type##_utest, \ ++ .testip_kernel = &type##_ktest, \ ++ .header_size = sizeof(struct ip_set_req_##type##_create),\ ++ .list_header = &type##_list_header, \ ++ .list_members_size = &type##_list_members_size, \ ++ .list_members = &type##_list_members, \ ++ .me = THIS_MODULE, \ ++}; ++ ++/* Helper functions */ ++static inline void ++add_cidr_size(uint8_t *cidr, uint8_t size) ++{ ++ uint8_t next; ++ int i; ++ ++ for (i = 0; i < 30 && cidr[i]; i++) { ++ if (cidr[i] < size) { ++ next = cidr[i]; ++ cidr[i] = size; ++ size = next; ++ } ++ } ++ if (i < 30) ++ cidr[i] = size; ++} ++ ++static inline void ++del_cidr_size(uint8_t *cidr, uint8_t size) ++{ ++ int i; ++ ++ for (i = 0; i < 29 && cidr[i]; i++) { ++ if (cidr[i] == size) ++ cidr[i] = size = cidr[i+1]; ++ } ++ cidr[29] = 0; ++} ++#else ++#include ++#endif /* __KERNEL */ ++ ++#ifndef UINT16_MAX ++#define UINT16_MAX 65535 ++#endif ++ ++static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; ++ ++static inline ip_set_ip_t ++pack_ip_cidr(ip_set_ip_t ip, unsigned char cidr) ++{ ++ ip_set_ip_t addr, *paddr = &addr; ++ unsigned char n, t, *a; ++ ++ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); ++#ifdef __KERNEL__ ++ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); ++#endif ++ n = cidr / 8; ++ t = cidr % 8; ++ a = &((unsigned char *)paddr)[n]; ++ *a = *a /(1 << (8 - t)) + shifts[t]; ++#ifdef __KERNEL__ ++ DP("n: %u, t: %u, a: %u", n, t, *a); ++ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", ++ HIPQUAD(ip), cidr, NIPQUAD(addr)); ++#endif ++ ++ return ntohl(addr); ++} ++ ++ ++#endif /* __IP_SET_HASHES_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_iphash.h b/include/linux/netfilter_ipv4/ip_set_iphash.h new file mode 100644 -index 0000000..7de854b +index 0000000..0a0c7e8 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_iphash.h @@ -0,0 +1,30 @@ @@ -512,9 +1216,9 @@ index 0000000..7de854b +#define __IP_SET_IPHASH_H + +#include ++#include + -+#define SETTYPE_NAME "iphash" -+#define MAX_RANGE 0x0000FFFF ++#define SETTYPE_NAME "iphash" + +struct ip_set_iphash { + ip_set_ip_t *members; /* the iphash proper */ @@ -523,7 +1227,7 @@ index 0000000..7de854b + uint16_t probes; /* max number of probes */ + uint16_t resize; /* resize factor in percent */ + ip_set_ip_t netmask; /* netmask */ -+ void *initval[0]; /* initvals for jhash_1word */ ++ initval_t initval[0]; /* initvals for jhash_1word */ +}; + +struct ip_set_req_iphash_create { @@ -540,17 +1244,17 @@ index 0000000..7de854b +#endif /* __IP_SET_IPHASH_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_ipmap.h b/include/linux/netfilter_ipv4/ip_set_ipmap.h new file mode 100644 -index 0000000..2435102 +index 0000000..d16c0ae --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_ipmap.h -@@ -0,0 +1,56 @@ +@@ -0,0 +1,57 @@ +#ifndef __IP_SET_IPMAP_H +#define __IP_SET_IPMAP_H + +#include ++#include + -+#define SETTYPE_NAME "ipmap" -+#define MAX_RANGE 0x0000FFFF ++#define SETTYPE_NAME "ipmap" + +struct ip_set_ipmap { + void *members; /* the ipmap proper */ @@ -559,6 +1263,7 @@ index 0000000..2435102 + ip_set_ip_t netmask; /* subnet netmask */ + ip_set_ip_t sizeid; /* size of set in IPs */ + ip_set_ip_t hosts; /* number of hosts in a subnet */ ++ u_int32_t size; /* size of the ipmap proper */ +}; + +struct ip_set_req_ipmap_create { @@ -571,7 +1276,7 @@ index 0000000..2435102 + ip_set_ip_t ip; +}; + -+unsigned int ++static inline unsigned int +mask_to_bits(ip_set_ip_t mask) +{ + unsigned int bits = 32; @@ -581,19 +1286,19 @@ index 0000000..2435102 + return bits; + + maskaddr = 0xFFFFFFFE; -+ while (--bits >= 0 && maskaddr != mask) ++ while (--bits > 0 && maskaddr != mask) + maskaddr <<= 1; + + return bits; +} + -+ip_set_ip_t ++static inline ip_set_ip_t +range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) +{ + ip_set_ip_t mask = 0xFFFFFFFE; + + *bits = 32; -+ while (--(*bits) >= 0 && mask && (to & mask) != from) ++ while (--(*bits) > 0 && mask && (to & mask) != from) + mask <<= 1; + + return mask; @@ -602,18 +1307,17 @@ index 0000000..2435102 +#endif /* __IP_SET_IPMAP_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_ipporthash.h b/include/linux/netfilter_ipv4/ip_set_ipporthash.h new file mode 100644 -index 0000000..b715c56 +index 0000000..a3b781a --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_ipporthash.h -@@ -0,0 +1,34 @@ +@@ -0,0 +1,33 @@ +#ifndef __IP_SET_IPPORTHASH_H +#define __IP_SET_IPPORTHASH_H + +#include ++#include + -+#define SETTYPE_NAME "ipporthash" -+#define MAX_RANGE 0x0000FFFF -+#define INVALID_PORT (MAX_RANGE + 1) ++#define SETTYPE_NAME "ipporthash" + +struct ip_set_ipporthash { + ip_set_ip_t *members; /* the ipporthash proper */ @@ -623,7 +1327,7 @@ index 0000000..b715c56 + uint16_t resize; /* resize factor in percent */ + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ -+ void *initval[0]; /* initvals for jhash_1word */ ++ initval_t initval[0]; /* initvals for jhash_1word */ +}; + +struct ip_set_req_ipporthash_create { @@ -640,19 +1344,111 @@ index 0000000..b715c56 +}; + +#endif /* __IP_SET_IPPORTHASH_H */ +diff --git a/include/linux/netfilter_ipv4/ip_set_ipportiphash.h b/include/linux/netfilter_ipv4/ip_set_ipportiphash.h +new file mode 100644 +index 0000000..2202c51 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipportiphash.h +@@ -0,0 +1,39 @@ ++#ifndef __IP_SET_IPPORTIPHASH_H ++#define __IP_SET_IPPORTIPHASH_H ++ ++#include ++#include ++ ++#define SETTYPE_NAME "ipportiphash" ++ ++struct ipportip { ++ ip_set_ip_t ip; ++ ip_set_ip_t ip1; ++}; ++ ++struct ip_set_ipportiphash { ++ struct ipportip *members; /* the ipportip proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ initval_t initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_ipportiphash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_ipportiphash { ++ ip_set_ip_t ip; ++ ip_set_ip_t port; ++ ip_set_ip_t ip1; ++}; ++ ++#endif /* __IP_SET_IPPORTIPHASH_H */ +diff --git a/include/linux/netfilter_ipv4/ip_set_ipportnethash.h b/include/linux/netfilter_ipv4/ip_set_ipportnethash.h +new file mode 100644 +index 0000000..73b2430 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipportnethash.h +@@ -0,0 +1,42 @@ ++#ifndef __IP_SET_IPPORTNETHASH_H ++#define __IP_SET_IPPORTNETHASH_H ++ ++#include ++#include ++ ++#define SETTYPE_NAME "ipportnethash" ++ ++struct ipportip { ++ ip_set_ip_t ip; ++ ip_set_ip_t ip1; ++}; ++ ++struct ip_set_ipportnethash { ++ struct ipportip *members; /* the ipportip proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ uint8_t cidr[30]; /* CIDR sizes */ ++ uint16_t nets[30]; /* nr of nets by CIDR sizes */ ++ initval_t initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_ipportnethash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_ipportnethash { ++ ip_set_ip_t ip; ++ ip_set_ip_t port; ++ ip_set_ip_t ip1; ++ uint8_t cidr; ++}; ++ ++#endif /* __IP_SET_IPPORTNETHASH_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_iptree.h b/include/linux/netfilter_ipv4/ip_set_iptree.h new file mode 100644 -index 0000000..64e716b +index 0000000..36bf5ac --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_iptree.h -@@ -0,0 +1,40 @@ +@@ -0,0 +1,39 @@ +#ifndef __IP_SET_IPTREE_H +#define __IP_SET_IPTREE_H + +#include + -+#define SETTYPE_NAME "iptree" -+#define MAX_RANGE 0x0000FFFF ++#define SETTYPE_NAME "iptree" + +struct ip_set_iptreed { + unsigned long expires[256]; /* x.x.x.ADDR */ @@ -688,7 +1484,7 @@ index 0000000..64e716b +#endif /* __IP_SET_IPTREE_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_iptreemap.h b/include/linux/netfilter_ipv4/ip_set_iptreemap.h new file mode 100644 -index 0000000..bef576a +index 0000000..6ea771a --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_iptreemap.h @@ -0,0 +1,40 @@ @@ -697,7 +1493,7 @@ index 0000000..bef576a + +#include + -+#define SETTYPE_NAME "iptreemap" ++#define SETTYPE_NAME "iptreemap" + +#ifdef __KERNEL__ +struct ip_set_iptreemap_d { @@ -727,178 +1523,187 @@ index 0000000..bef576a +}; + +struct ip_set_req_iptreemap { -+ ip_set_ip_t start; ++ ip_set_ip_t ip; + ip_set_ip_t end; +}; + +#endif /* __IP_SET_IPTREEMAP_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_jhash.h b/include/linux/netfilter_ipv4/ip_set_jhash.h new file mode 100644 -index 0000000..25c6b97 +index 0000000..2000b9f --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_jhash.h -@@ -0,0 +1,148 @@ -+#ifndef _LINUX_IPSET_JHASH_H -+#define _LINUX_IPSET_JHASH_H -+ -+/* This is a copy of linux/jhash.h but the types u32/u8 are changed -+ * to __u32/__u8 so that the header file can be included into -+ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) -+ */ +@@ -0,0 +1,157 @@ ++#ifndef _LINUX_JHASH_H ++#define _LINUX_JHASH_H + +/* jhash.h: Jenkins hash support. + * -+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) ++ * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * -+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. -+ * hash(), hash2(), hash3, and mix() are externally useful functions. -+ * Routines to test the hash are included if SELF_TEST is defined. -+ * You can use this free for any purpose. It has no warranty. ++ * lookup3.c, by Bob Jenkins, May 2006, Public Domain. ++ * ++ * These are functions for producing 32-bit hashes for hash table lookup. ++ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() ++ * are externally useful functions. Routines to test the hash are included ++ * 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) 2003 David S. Miller (davem@redhat.com) ++ * Copyright (C) 2009 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * I've modified Bob's hash to be useful in the Linux kernel, and -+ * any bugs present are surely my fault. -DaveM ++ * any bugs present are my fault. Jozsef + */ + -+/* NOTE: Arguments are modified. */ -+#define __jhash_mix(a, b, c) \ ++#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; \ ++} ++ ++/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */ ++#define __jhash_final(a,b,c) \ +{ \ -+ a -= b; a -= c; a ^= (c>>13); \ -+ b -= c; b -= a; b ^= (a<<8); \ -+ c -= a; c -= b; c ^= (b>>13); \ -+ a -= b; a -= c; a ^= (c>>12); \ -+ b -= c; b -= a; b ^= (a<<16); \ -+ c -= a; c -= b; c ^= (b>>5); \ -+ a -= b; a -= c; a ^= (c>>3); \ -+ b -= c; b -= a; b ^= (a<<10); \ -+ c -= a; c -= b; c ^= (b>>15); \ ++ 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); \ +} + +/* The golden ration: an arbitrary value */ -+#define JHASH_GOLDEN_RATIO 0x9e3779b9 ++#define JHASH_GOLDEN_RATIO 0xdeadbeef + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about -+ * the input key. ++ * the input key. The result depends on endianness. + */ -+static inline __u32 jhash(void *key, __u32 length, __u32 initval) ++static inline u32 jhash(const void *key, u32 length, u32 initval) +{ -+ __u32 a, b, c, len; -+ __u8 *k = key; -+ -+ len = length; -+ a = b = JHASH_GOLDEN_RATIO; -+ c = initval; -+ -+ while (len >= 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)); ++ u32 a,b,c; ++ const u8 *k = key; + -+ __jhash_mix(a,b,c); ++ /* Set up the internal state */ ++ a = b = c = JHASH_GOLDEN_RATIO + 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; -+ len -= 12; + } + -+ c += length; -+ switch (len) { -+ case 11: c += ((__u32)k[10]<<24); -+ case 10: c += ((__u32)k[9]<<16); -+ case 9 : c += ((__u32)k[8]<<8); -+ case 8 : b += ((__u32)k[7]<<24); -+ case 7 : b += ((__u32)k[6]<<16); -+ case 6 : b += ((__u32)k[5]<<8); ++ /* 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 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_mix(a,b,c); ++ __jhash_final(a, b, c); ++ case 0 : ++ break; ++ } + + return c; +} + -+/* A special optimized version that handles 1 or more of __u32s. -+ * The length parameter here is the number of __u32s in the key. ++/* A special optimized version that handles 1 or more of u32s. ++ * The length parameter here is the number of u32s in the key. + */ -+static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) ++static inline u32 jhash2(const u32 *k, u32 length, u32 initval) +{ -+ __u32 a, b, c, len; ++ u32 a, b, c; + -+ a = b = JHASH_GOLDEN_RATIO; -+ c = initval; -+ len = length; ++ /* Set up the internal state */ ++ a = b = c = JHASH_GOLDEN_RATIO + (length<<2) + initval; + -+ while (len >= 3) { ++ /* handle most of the key */ ++ while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); -+ k += 3; len -= 3; ++ length -= 3; ++ k += 3; + } + -+ c += length * 4; -+ -+ switch (len) { -+ case 2 : b += k[1]; -+ case 1 : a += k[0]; -+ }; -+ -+ __jhash_mix(a,b,c); ++ /* 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 */ ++ break; ++ } + + return c; +} + -+ +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). -+ * -+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally -+ * done at the end is not done here. + */ -+static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) ++static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ -+ a += JHASH_GOLDEN_RATIO; -+ b += JHASH_GOLDEN_RATIO; -+ c += initval; ++ a += JHASH_GOLDEN_RATIO + initval; ++ b += JHASH_GOLDEN_RATIO + initval; ++ c += JHASH_GOLDEN_RATIO + initval; + -+ __jhash_mix(a, b, c); ++ __jhash_final(a, b, c); + + return c; +} + -+static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) ++static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ -+ return jhash_3words(a, b, 0, initval); ++ return jhash_3words(0, a, b, initval); +} + -+static inline __u32 jhash_1word(__u32 a, __u32 initval) ++static inline u32 jhash_1word(u32 a, u32 initval) +{ -+ return jhash_3words(a, 0, 0, initval); ++ return jhash_3words(0, 0, a, initval); +} + -+#endif /* _LINUX_IPSET_JHASH_H */ ++#endif /* _LINUX_JHASH_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_macipmap.h b/include/linux/netfilter_ipv4/ip_set_macipmap.h new file mode 100644 -index 0000000..ee34c9b +index 0000000..0615e9f --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_macipmap.h -@@ -0,0 +1,38 @@ +@@ -0,0 +1,39 @@ +#ifndef __IP_SET_MACIPMAP_H +#define __IP_SET_MACIPMAP_H + +#include ++#include + -+#define SETTYPE_NAME "macipmap" -+#define MAX_RANGE 0x0000FFFF ++#define SETTYPE_NAME "macipmap" + +/* general flags */ +#define IPSET_MACIP_MATCHUNSET 1 @@ -911,6 +1716,7 @@ index 0000000..ee34c9b + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ + u_int32_t flags; ++ u_int32_t size; /* size of the ipmap proper */ +}; + +struct ip_set_req_macipmap_create { @@ -925,46 +1731,51 @@ index 0000000..ee34c9b +}; + +struct ip_set_macip { -+ unsigned short flags; ++ unsigned short match; + unsigned char ethernet[ETH_ALEN]; +}; + +#endif /* __IP_SET_MACIPMAP_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_malloc.h b/include/linux/netfilter_ipv4/ip_set_malloc.h new file mode 100644 -index 0000000..ab97e14 +index 0000000..2a80443 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_malloc.h -@@ -0,0 +1,116 @@ +@@ -0,0 +1,153 @@ +#ifndef _IP_SET_MALLOC_H +#define _IP_SET_MALLOC_H + +#ifdef __KERNEL__ ++#include + -+/* Memory allocation and deallocation */ -+static size_t max_malloc_size = 0; ++static size_t max_malloc_size = 0, max_page_size = 0; ++static size_t default_max_malloc_size = 131072; /* Guaranteed: slab.c */ + -+static inline void init_max_malloc_size(void) ++static inline int init_max_page_size(void) +{ -+#define CACHE(x) max_malloc_size = x; ++/* Compatibility glues to support 2.4.36 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#define __GFP_NOWARN 0 ++ ++ /* Guaranteed: slab.c */ ++ max_malloc_size = max_page_size = default_max_malloc_size; ++#else ++ size_t page_size = 0; ++ ++#define CACHE(x) if (max_page_size == 0 || x < max_page_size) \ ++ page_size = x; +#include +#undef CACHE -+} -+ -+static inline void * ip_set_malloc(size_t bytes) -+{ -+ if (bytes > max_malloc_size) -+ return vmalloc(bytes); -+ else -+ return kmalloc(bytes, GFP_KERNEL); -+} -+ -+static inline void ip_set_free(void * data, size_t bytes) -+{ -+ if (bytes > max_malloc_size) -+ vfree(data); -+ else -+ kfree(data); ++ if (page_size) { ++ if (max_malloc_size == 0) ++ max_malloc_size = page_size; ++ ++ max_page_size = page_size; ++ ++ return 1; ++ } ++#endif ++ return 0; +} + +struct harray { @@ -973,18 +1784,17 @@ index 0000000..ab97e14 +}; + +static inline void * -+harray_malloc(size_t hashsize, size_t typesize, int flags) ++__harray_malloc(size_t hashsize, size_t typesize, gfp_t flags) +{ + struct harray *harray; + size_t max_elements, size, i, j; + -+ if (!max_malloc_size) -+ init_max_malloc_size(); ++ BUG_ON(max_page_size == 0); + -+ if (typesize > max_malloc_size) ++ if (typesize > max_page_size) + return NULL; + -+ max_elements = max_malloc_size/typesize; ++ max_elements = max_page_size/typesize; + size = hashsize/max_elements; + if (hashsize % max_elements) + size++; @@ -1021,6 +1831,18 @@ index 0000000..ab97e14 + return NULL; +} + ++static inline void * ++harray_malloc(size_t hashsize, size_t typesize, gfp_t flags) ++{ ++ void *harray; ++ ++ do { ++ harray = __harray_malloc(hashsize, typesize, flags|__GFP_NOWARN); ++ } while (harray == NULL && init_max_page_size()); ++ ++ return harray; ++} ++ +static inline void harray_free(void *h) +{ + struct harray *harray = (struct harray *) h; @@ -1049,22 +1871,43 @@ index 0000000..ab97e14 + + (which)%(__h)->max_elements); \ +}) + ++/* General memory allocation and deallocation */ ++static inline void * ip_set_malloc(size_t bytes) ++{ ++ BUG_ON(max_malloc_size == 0); ++ ++ if (bytes > default_max_malloc_size) ++ return vmalloc(bytes); ++ else ++ return kmalloc(bytes, GFP_KERNEL | __GFP_NOWARN); ++} ++ ++static inline void ip_set_free(void * data, size_t bytes) ++{ ++ BUG_ON(max_malloc_size == 0); ++ ++ if (bytes > default_max_malloc_size) ++ vfree(data); ++ else ++ kfree(data); ++} ++ +#endif /* __KERNEL__ */ + +#endif /*_IP_SET_MALLOC_H*/ diff --git a/include/linux/netfilter_ipv4/ip_set_nethash.h b/include/linux/netfilter_ipv4/ip_set_nethash.h new file mode 100644 -index 0000000..172ef02 +index 0000000..cf0b794 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_nethash.h -@@ -0,0 +1,55 @@ +@@ -0,0 +1,31 @@ +#ifndef __IP_SET_NETHASH_H +#define __IP_SET_NETHASH_H + +#include ++#include + -+#define SETTYPE_NAME "nethash" -+#define MAX_RANGE 0x0000FFFF ++#define SETTYPE_NAME "nethash" + +struct ip_set_nethash { + ip_set_ip_t *members; /* the nethash proper */ @@ -1072,8 +1915,9 @@ index 0000000..172ef02 + uint32_t hashsize; /* hash size */ + uint16_t probes; /* max number of probes */ + uint16_t resize; /* resize factor in percent */ -+ unsigned char cidr[30]; /* CIDR sizes */ -+ void *initval[0]; /* initvals for jhash_1word */ ++ uint8_t cidr[30]; /* CIDR sizes */ ++ uint16_t nets[30]; /* nr of nets by CIDR sizes */ ++ initval_t initval[0]; /* initvals for jhash_1word */ +}; + +struct ip_set_req_nethash_create { @@ -1084,38 +1928,13 @@ index 0000000..172ef02 + +struct ip_set_req_nethash { + ip_set_ip_t ip; -+ unsigned char cidr; ++ uint8_t cidr; +}; + -+static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; -+ -+static inline ip_set_ip_t -+pack(ip_set_ip_t ip, unsigned char cidr) -+{ -+ ip_set_ip_t addr, *paddr = &addr; -+ unsigned char n, t, *a; -+ -+ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); -+#ifdef __KERNEL__ -+ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); -+#endif -+ n = cidr / 8; -+ t = cidr % 8; -+ a = &((unsigned char *)paddr)[n]; -+ *a = *a /(1 << (8 - t)) + shifts[t]; -+#ifdef __KERNEL__ -+ DP("n: %u, t: %u, a: %u", n, t, *a); -+ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", -+ HIPQUAD(ip), cidr, NIPQUAD(addr)); -+#endif -+ -+ return ntohl(addr); -+} -+ +#endif /* __IP_SET_NETHASH_H */ diff --git a/include/linux/netfilter_ipv4/ip_set_portmap.h b/include/linux/netfilter_ipv4/ip_set_portmap.h new file mode 100644 -index 0000000..c17165c +index 0000000..37f411e --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_set_portmap.h @@ -0,0 +1,25 @@ @@ -1123,15 +1942,15 @@ index 0000000..c17165c +#define __IP_SET_PORTMAP_H + +#include ++#include + -+#define SETTYPE_NAME "portmap" -+#define MAX_RANGE 0x0000FFFF -+#define INVALID_PORT (MAX_RANGE + 1) ++#define SETTYPE_NAME "portmap" + +struct ip_set_portmap { + void *members; /* the portmap proper */ -+ ip_set_ip_t first_port; /* host byte order, included in range */ -+ ip_set_ip_t last_port; /* host byte order, included in range */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ u_int32_t size; /* size of the ipmap proper */ +}; + +struct ip_set_req_portmap_create { @@ -1140,10 +1959,42 @@ index 0000000..c17165c +}; + +struct ip_set_req_portmap { -+ ip_set_ip_t port; ++ ip_set_ip_t ip; +}; + +#endif /* __IP_SET_PORTMAP_H */ +diff --git a/include/linux/netfilter_ipv4/ip_set_setlist.h b/include/linux/netfilter_ipv4/ip_set_setlist.h +new file mode 100644 +index 0000000..7cc6ed0 +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_setlist.h +@@ -0,0 +1,26 @@ ++#ifndef __IP_SET_SETLIST_H ++#define __IP_SET_SETLIST_H ++ ++#include ++ ++#define SETTYPE_NAME "setlist" ++ ++#define IP_SET_SETLIST_ADD_AFTER 0 ++#define IP_SET_SETLIST_ADD_BEFORE 1 ++ ++struct ip_set_setlist { ++ uint8_t size; ++ ip_set_id_t index[0]; ++}; ++ ++struct ip_set_req_setlist_create { ++ uint8_t size; ++}; ++ ++struct ip_set_req_setlist { ++ char name[IP_SET_MAXNAMELEN]; ++ char ref[IP_SET_MAXNAMELEN]; ++ uint8_t before; ++}; ++ ++#endif /* __IP_SET_SETLIST_H */ diff --git a/include/linux/netfilter_ipv4/ipt_set.h b/include/linux/netfilter_ipv4/ipt_set.h new file mode 100644 index 0000000..2a18b93 @@ -1333,17 +2184,17 @@ index 4811159..fd4913a 100644 obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o diff --git a/net/ipv4/netfilter/ip_set.c b/net/ipv4/netfilter/ip_set.c new file mode 100644 -index 0000000..7d00a14 +index 0000000..7ecdc83 --- /dev/null +++ b/net/ipv4/netfilter/ip_set.c -@@ -0,0 +1,2005 @@ +@@ -0,0 +1,1531 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Copyright (C) 2003-2004 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module for IP set management */ @@ -1358,36 +2209,41 @@ index 0000000..7d00a14 +#include +#include +#include -+#include -+#include ++#include +#include ++#include +#include +#include -+// #include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ++#include ++#else ++#include ++#endif +#include -+#include + +#define ASSERT_READ_LOCK(x) +#define ASSERT_WRITE_LOCK(x) ++#include +#include + +static struct list_head set_type_list; /* all registered sets */ +static struct ip_set **ip_set_list; /* all individual sets */ +static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ -+static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ ++static struct semaphore ip_set_app_mutex; /* serializes user access */ +static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; -+static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; -+static struct list_head *ip_set_hash; /* hash of bindings */ -+static unsigned int ip_set_hash_random; /* random seed */ ++static int protocol_version = IP_SET_PROTOCOL_VERSION; ++ ++#define STREQ(a,b) (strncmp(a,b,IP_SET_MAXNAMELEN) == 0) ++#define DONT_ALIGN (protocol_version == IP_SET_PROTOCOL_UNALIGNED) ++#define ALIGNED(len) IPSET_VALIGN(len, DONT_ALIGN) + +/* + * Sets are identified either by the index in ip_set_list or by id. -+ * The id never changes and is used to find a key in the hash. -+ * The index may change by swapping and used at all other places -+ * (set/SET netfilter modules, binding value, etc.) ++ * The id never changes. The index may change by swapping and used ++ * by external references (set/SET netfilter modules, etc.) + * + * Userspace requests are serialized by ip_set_mutex and sets can -+ * be deleted only from userspace. Therefore ip_set_list locking ++ * be deleted only from userspace. Therefore ip_set_list locking + * must obey the following rules: + * + * - kernel requests: read and write locking mandatory @@ -1406,227 +2262,78 @@ index 0000000..7d00a14 + atomic_dec(&ip_set_list[index]->ref); +} + -+/* -+ * Binding routines -+ */ -+ -+static inline struct ip_set_hash * -+__ip_set_find(u_int32_t key, ip_set_id_t id, ip_set_ip_t ip) -+{ -+ struct ip_set_hash *set_hash; -+ -+ list_for_each_entry(set_hash, &ip_set_hash[key], list) -+ if (set_hash->id == id && set_hash->ip == ip) -+ return set_hash; -+ -+ return NULL; -+} -+ -+static ip_set_id_t -+ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ -+ ASSERT_READ_LOCK(&ip_set_lock); -+ IP_SET_ASSERT(ip_set_list[id]); -+ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); -+ -+ set_hash = __ip_set_find(key, id, ip); -+ -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), -+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); -+ -+ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); -+} -+ -+static inline void -+__set_hash_del(struct ip_set_hash *set_hash) -+{ -+ ASSERT_WRITE_LOCK(&ip_set_lock); -+ IP_SET_ASSERT(ip_set_list[set_hash->binding]); -+ -+ __ip_set_put(set_hash->binding); -+ list_del(&set_hash->list); -+ kfree(set_hash); -+} -+ -+static int -+ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ -+ IP_SET_ASSERT(ip_set_list[id]); -+ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); -+ write_lock_bh(&ip_set_lock); -+ set_hash = __ip_set_find(key, id, ip); -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), -+ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); -+ -+ if (set_hash != NULL) -+ __set_hash_del(set_hash); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+} -+ -+static int -+ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) -+{ -+ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) -+ % ip_set_bindings_hash_size; -+ struct ip_set_hash *set_hash; -+ int ret = 0; -+ -+ IP_SET_ASSERT(ip_set_list[id]); -+ IP_SET_ASSERT(ip_set_list[binding]); -+ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, -+ HIPQUAD(ip), ip_set_list[binding]->name); -+ write_lock_bh(&ip_set_lock); -+ set_hash = __ip_set_find(key, id, ip); -+ if (!set_hash) { -+ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_ATOMIC); -+ if (!set_hash) { -+ ret = -ENOMEM; -+ goto unlock; -+ } -+ INIT_LIST_HEAD(&set_hash->list); -+ set_hash->id = id; -+ set_hash->ip = ip; -+ list_add(&set_hash->list, &ip_set_hash[key]); -+ } else { -+ IP_SET_ASSERT(ip_set_list[set_hash->binding]); -+ DP("overwrite binding: %s", -+ ip_set_list[set_hash->binding]->name); -+ __ip_set_put(set_hash->binding); -+ } -+ set_hash->binding = binding; -+ __ip_set_get(set_hash->binding); -+ DP("stored: key %u, id %u (%s), ip %u.%u.%u.%u, binding %u (%s)", -+ key, id, ip_set_list[id]->name, -+ HIPQUAD(ip), binding, ip_set_list[binding]->name); -+ unlock: -+ write_unlock_bh(&ip_set_lock); -+ return ret; -+} -+ -+#define FOREACH_HASH_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __key; \ -+ struct ip_set_hash *__set_hash; \ -+ \ -+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ -+ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ -+ fn(__set_hash , ## args); \ -+ } \ -+}) -+ -+#define FOREACH_HASH_RW_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __key; \ -+ struct ip_set_hash *__set_hash, *__n; \ -+ \ -+ ASSERT_WRITE_LOCK(&ip_set_lock); \ -+ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ -+ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ -+ fn(__set_hash , ## args); \ -+ } \ -+}) -+ +/* Add, del and test set entries from kernel */ + -+#define follow_bindings(index, set, ip) \ -+((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ -+ || (index = (set)->binding) != IP_SET_INVALID_ID) -+ +int +ip_set_testip_kernel(ip_set_id_t index, + const struct sk_buff *skb, + const u_int32_t *flags) +{ + struct ip_set *set; -+ ip_set_ip_t ip; + int res; -+ unsigned char i = 0; -+ -+ IP_SET_ASSERT(flags[i]); ++ + read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ read_lock_bh(&set->lock); -+ res = set->type->testip_kernel(set, skb, &ip, flags, i++); -+ read_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while (res > 0 -+ && flags[i] -+ && follow_bindings(index, set, ip)); ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip_kernel(set, skb, flags); ++ read_unlock_bh(&set->lock); ++ + read_unlock_bh(&ip_set_lock); + -+ return res; ++ return (res < 0 ? 0 : res); +} + -+void ++int +ip_set_addip_kernel(ip_set_id_t index, + const struct sk_buff *skb, + const u_int32_t *flags) +{ + struct ip_set *set; -+ ip_set_ip_t ip; + int res; -+ unsigned char i = 0; + -+ IP_SET_ASSERT(flags[i]); + retry: + read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ write_lock_bh(&set->lock); -+ res = set->type->addip_kernel(set, skb, &ip, flags, i++); -+ write_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while ((res == 0 || res == -EEXIST) -+ && flags[i] -+ && follow_bindings(index, set, ip)); -+ read_unlock_bh(&ip_set_lock); ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); + ++ write_lock_bh(&set->lock); ++ res = set->type->addip_kernel(set, skb, flags); ++ write_unlock_bh(&set->lock); ++ ++ read_unlock_bh(&ip_set_lock); ++ /* Retry function called without holding any lock */ + if (res == -EAGAIN + && set->type->retry + && (res = set->type->retry(set)) == 0) + goto retry; ++ ++ return res; +} + -+void ++int +ip_set_delip_kernel(ip_set_id_t index, + const struct sk_buff *skb, + const u_int32_t *flags) +{ + struct ip_set *set; -+ ip_set_ip_t ip; + int res; -+ unsigned char i = 0; + -+ IP_SET_ASSERT(flags[i]); + read_lock_bh(&ip_set_lock); -+ do { -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ DP("set %s, index %u", set->name, index); -+ write_lock_bh(&set->lock); -+ res = set->type->delip_kernel(set, skb, &ip, flags, i++); -+ write_unlock_bh(&set->lock); -+ i += !!(set->type->features & IPSET_DATA_DOUBLE); -+ } while ((res == 0 || res == -EEXIST) -+ && flags[i] -+ && follow_bindings(index, set, ip)); ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ ++ write_lock_bh(&set->lock); ++ res = set->type->delip_kernel(set, skb, flags); ++ write_unlock_bh(&set->lock); ++ + read_unlock_bh(&ip_set_lock); ++ ++ return res; +} + +/* Register and deregister settype */ @@ -1637,12 +2344,12 @@ index 0000000..7d00a14 + struct ip_set_type *set_type; + + list_for_each_entry(set_type, &set_type_list, list) -+ if (!strncmp(set_type->typename, name, IP_SET_MAXNAMELEN - 1)) ++ if (STREQ(set_type->typename, name)) + return set_type; + return NULL; +} + -+int ++int +ip_set_register_set_type(struct ip_set_type *set_type) +{ + int ret = 0; @@ -1658,7 +2365,7 @@ index 0000000..7d00a14 + write_lock_bh(&ip_set_lock); + if (find_set_type(set_type->typename)) { + /* Duplicate! */ -+ ip_set_printk("'%s' already registered!", ++ ip_set_printk("'%s' already registered!", + set_type->typename); + ret = -EINVAL; + goto unlock; @@ -1691,6 +2398,30 @@ index 0000000..7d00a14 + +} + ++ip_set_id_t ++__ip_set_get_byname(const char *name, struct ip_set **set) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && STREQ(ip_set_list[i]->name, name)) { ++ __ip_set_get(i); ++ index = i; ++ *set = ip_set_list[i]; ++ break; ++ } ++ } ++ return index; ++} ++ ++void ++__ip_set_put_byindex(ip_set_id_t index) ++{ ++ if (ip_set_list[index]) ++ __ip_set_put(index); ++} ++ +/* + * Userspace routines + */ @@ -1708,7 +2439,7 @@ index 0000000..7d00a14 + down(&ip_set_app_mutex); + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL -+ && strcmp(ip_set_list[i]->name, name) == 0) { ++ && STREQ(ip_set_list[i]->name, name)) { + __ip_set_get(i); + index = i; + break; @@ -1741,11 +2472,26 @@ index 0000000..7d00a14 +} + +/* ++ * Find the set id belonging to the index. ++ * We are protected by the mutex, so we do not need to use ++ * ip_set_lock. There is no need to reference the sets either. ++ */ ++ip_set_id_t ++ip_set_id(ip_set_id_t index) ++{ ++ if (index >= ip_set_max || !ip_set_list[index]) ++ return IP_SET_INVALID_ID; ++ ++ return ip_set_list[index]->id; ++} ++ ++/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the index + * to be valid, after calling this function. + */ -+void ip_set_put(ip_set_id_t index) ++void ++ip_set_put_byindex(ip_set_id_t index) +{ + down(&ip_set_app_mutex); + if (ip_set_list[index]) @@ -1761,7 +2507,7 @@ index 0000000..7d00a14 + + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL -+ && strcmp(ip_set_list[i]->name, name) == 0) { ++ && STREQ(ip_set_list[i]->name, name)) { + index = i; + break; + } @@ -1779,37 +2525,18 @@ index 0000000..7d00a14 +} + +/* -+ * Add, del, test, bind and unbind ++ * Add, del and test + */ + -+static inline int -+__ip_set_testip(struct ip_set *set, -+ const void *data, -+ size_t size, -+ ip_set_ip_t *ip) -+{ -+ int res; -+ -+ read_lock_bh(&set->lock); -+ res = set->type->testip(set, data, size, ip); -+ read_unlock_bh(&set->lock); -+ -+ return res; -+} -+ +static int -+__ip_set_addip(ip_set_id_t index, -+ const void *data, -+ size_t size) ++ip_set_addip(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; + int res; + + IP_SET_ASSERT(set); + do { + write_lock_bh(&set->lock); -+ res = set->type->addip(set, data, size, &ip); ++ res = set->type->addip(set, data, size); + write_unlock_bh(&set->lock); + } while (res == -EAGAIN + && set->type->retry @@ -1819,283 +2546,45 @@ index 0000000..7d00a14 +} + +static int -+ip_set_addip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ -+ return __ip_set_addip(index, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt)); -+} -+ -+static int -+ip_set_delip(ip_set_id_t index, -+ const void *data, -+ size_t size) ++ip_set_delip(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; + int res; + + IP_SET_ASSERT(set); ++ + write_lock_bh(&set->lock); -+ res = set->type->delip(set, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt), -+ &ip); ++ res = set->type->delip(set, data, size); + write_unlock_bh(&set->lock); + + return res; +} + +static int -+ip_set_testip(ip_set_id_t index, -+ const void *data, -+ size_t size) ++ip_set_testip(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set *set = ip_set_list[index]; -+ ip_set_ip_t ip; + int res; + + IP_SET_ASSERT(set); -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_adt), -+ size - sizeof(struct ip_set_req_adt), -+ &ip); ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip(set, data, size); ++ read_unlock_bh(&set->lock); + + return (res > 0 ? -EEXIST : res); +} + -+static int -+ip_set_bindip(ip_set_id_t index, -+ const void *data, -+ size_t size) ++static struct ip_set_type * ++find_set_type_rlock(const char *typename) +{ -+ struct ip_set *set = ip_set_list[index]; -+ struct ip_set_req_bind *req_bind; -+ ip_set_id_t binding; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of a set */ -+ char *binding_name; -+ -+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) -+ return -EINVAL; ++ struct ip_set_type *type; ++ ++ read_lock_bh(&ip_set_lock); ++ type = find_set_type(typename); ++ if (type == NULL) ++ read_unlock_bh(&ip_set_lock); + -+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); -+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ binding = ip_set_find_byname(binding_name); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ write_lock_bh(&ip_set_lock); -+ /* Sets as binding values are referenced */ -+ if (set->binding != IP_SET_INVALID_ID) -+ __ip_set_put(set->binding); -+ set->binding = binding; -+ __ip_set_get(set->binding); -+ write_unlock_bh(&ip_set_lock); -+ -+ return 0; -+ } -+ binding = ip_set_find_byname(req_bind->binding); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ DP("set %s, ip: %u.%u.%u.%u, binding %s", -+ set->name, HIPQUAD(ip), ip_set_list[binding]->name); -+ -+ if (res >= 0) -+ res = ip_set_hash_add(set->id, ip, binding); -+ -+ return res; -+} -+ -+#define FOREACH_SET_DO(fn, args...) \ -+({ \ -+ ip_set_id_t __i; \ -+ struct ip_set *__set; \ -+ \ -+ for (__i = 0; __i < ip_set_max; __i++) { \ -+ __set = ip_set_list[__i]; \ -+ if (__set != NULL) \ -+ fn(__set , ##args); \ -+ } \ -+}) -+ -+static inline void -+__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) -+{ -+ if (set_hash->id == id) -+ __set_hash_del(set_hash); -+} -+ -+static inline void -+__unbind_default(struct ip_set *set) -+{ -+ if (set->binding != IP_SET_INVALID_ID) { -+ /* Sets as binding values are referenced */ -+ __ip_set_put(set->binding); -+ set->binding = IP_SET_INVALID_ID; -+ } -+} -+ -+static int -+ip_set_unbindip(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set; -+ struct ip_set_req_bind *req_bind; -+ ip_set_ip_t ip; -+ int res; -+ -+ DP(""); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ DP("%u %s", index, req_bind->binding); -+ if (index == IP_SET_INVALID_ID) { -+ /* unbind :all: */ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of sets */ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_SET_DO(__unbind_default); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { -+ /* Flush all bindings of all sets*/ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } -+ DP("unreachable reached!"); -+ return -EINVAL; -+ } -+ -+ set = ip_set_list[index]; -+ IP_SET_ASSERT(set); -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of set */ -+ ip_set_id_t binding = ip_set_find_byindex(set->binding); -+ -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ write_lock_bh(&ip_set_lock); -+ /* Sets in hash values are referenced */ -+ __ip_set_put(set->binding); -+ set->binding = IP_SET_INVALID_ID; -+ write_unlock_bh(&ip_set_lock); -+ -+ return 0; -+ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { -+ /* Flush all bindings */ -+ -+ write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); -+ write_unlock_bh(&ip_set_lock); -+ return 0; -+ } -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ -+ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); -+ if (res >= 0) -+ res = ip_set_hash_del(set->id, ip); -+ -+ return res; -+} -+ -+static int -+ip_set_testbind(ip_set_id_t index, -+ const void *data, -+ size_t size) -+{ -+ struct ip_set *set = ip_set_list[index]; -+ struct ip_set_req_bind *req_bind; -+ ip_set_id_t binding; -+ ip_set_ip_t ip; -+ int res; -+ -+ IP_SET_ASSERT(set); -+ if (size < sizeof(struct ip_set_req_bind)) -+ return -EINVAL; -+ -+ req_bind = (struct ip_set_req_bind *) data; -+ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { -+ /* Default binding of set */ -+ char *binding_name; -+ -+ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) -+ return -EINVAL; -+ -+ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); -+ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ -+ binding = ip_set_find_byname(binding_name); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ res = (set->binding == binding) ? -EEXIST : 0; -+ -+ return res; -+ } -+ binding = ip_set_find_byname(req_bind->binding); -+ if (binding == IP_SET_INVALID_ID) -+ return -ENOENT; -+ -+ -+ res = __ip_set_testip(set, -+ data + sizeof(struct ip_set_req_bind), -+ size - sizeof(struct ip_set_req_bind), -+ &ip); -+ DP("set %s, ip: %u.%u.%u.%u, binding %s", -+ set->name, HIPQUAD(ip), ip_set_list[binding]->name); -+ -+ if (res >= 0) -+ res = (ip_set_find_in_hash(set->id, ip) == binding) -+ ? -EEXIST : 0; -+ -+ return res; -+} -+ -+static struct ip_set_type * -+find_set_type_rlock(const char *typename) -+{ -+ struct ip_set_type *type; -+ -+ read_lock_bh(&ip_set_lock); -+ type = find_set_type(typename); -+ if (type == NULL) -+ read_unlock_bh(&ip_set_lock); -+ -+ return type; -+} ++ return type; ++} + +static int +find_free_id(const char *name, @@ -2109,7 +2598,7 @@ index 0000000..7d00a14 + if (ip_set_list[i] == NULL) { + if (*id == IP_SET_INVALID_ID) + *id = *index = i; -+ } else if (strcmp(name, ip_set_list[i]->name) == 0) ++ } else if (STREQ(name, ip_set_list[i]->name)) + /* Name clash */ + return -EEXIST; + } @@ -2136,13 +2625,14 @@ index 0000000..7d00a14 + const char *typename, + ip_set_id_t restore, + const void *data, -+ size_t size) ++ u_int32_t size) +{ + struct ip_set *set; + ip_set_id_t index = 0, id; + int res = 0; + + DP("setname: %s, typename: %s, id: %u", name, typename, restore); ++ + /* + * First, and without any locks, allocate and initialize + * a normal base set structure. @@ -2150,9 +2640,8 @@ index 0000000..7d00a14 + set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); + if (!set) + return -ENOMEM; -+ set->lock = RW_LOCK_UNLOCKED; ++ rwlock_init(&set->lock); + strncpy(set->name, name, IP_SET_MAXNAMELEN); -+ set->binding = IP_SET_INVALID_ID; + atomic_set(&set->ref, 0); + + /* @@ -2186,6 +2675,14 @@ index 0000000..7d00a14 + } + read_unlock_bh(&ip_set_lock); + ++ /* Check request size */ ++ if (size != set->type->header_size) { ++ ip_set_printk("data length wrong (want %lu, have %lu)", ++ (long unsigned)set->type->header_size, ++ (long unsigned)size); ++ goto put_out; ++ } ++ + /* + * Without holding any locks, create private part. + */ @@ -2197,7 +2694,7 @@ index 0000000..7d00a14 + + /* + * Here, we have a valid, constructed set. &ip_set_lock again, -+ * find free id/index and check that it is not already in ++ * find free id/index and check that it is not already in + * ip_set_list. + */ + write_lock_bh(&ip_set_lock); @@ -2212,7 +2709,7 @@ index 0000000..7d00a14 + res = -ERANGE; + goto cleanup; + } -+ ++ + /* + * Finally! Add our shiny new set to the list, and be done. + */ @@ -2243,9 +2740,6 @@ index 0000000..7d00a14 + IP_SET_ASSERT(set); + DP("set: %s", set->name); + write_lock_bh(&ip_set_lock); -+ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); -+ if (set->binding != IP_SET_INVALID_ID) -+ __ip_set_put(set->binding); + ip_set_list[index] = NULL; + write_unlock_bh(&ip_set_lock); + @@ -2271,7 +2765,7 @@ index 0000000..7d00a14 + ip_set_destroy_set(index); + } else { + for (i = 0; i < ip_set_max; i++) { -+ if (ip_set_list[i] != NULL ++ if (ip_set_list[i] != NULL + && (atomic_read(&ip_set_list[i]->ref))) + return -EBUSY; + } @@ -2294,7 +2788,7 @@ index 0000000..7d00a14 + write_unlock_bh(&set->lock); +} + -+/* ++/* + * Flush data in a set - or in all sets + */ +static int @@ -2303,8 +2797,13 @@ index 0000000..7d00a14 + if (index != IP_SET_INVALID_ID) { + IP_SET_ASSERT(ip_set_list[index]); + ip_set_flush_set(ip_set_list[index]); -+ } else -+ FOREACH_SET_DO(ip_set_flush_set); ++ } else { ++ ip_set_id_t i; ++ ++ for (i = 0; i < ip_set_max; i++) ++ if (ip_set_list[i] != NULL) ++ ip_set_flush_set(ip_set_list[i]); ++ } + + return 0; +} @@ -2321,9 +2820,7 @@ index 0000000..7d00a14 + write_lock_bh(&ip_set_lock); + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL -+ && strncmp(ip_set_list[i]->name, -+ name, -+ IP_SET_MAXNAMELEN - 1) == 0) { ++ && STREQ(ip_set_list[i]->name, name)) { + res = -EEXIST; + goto unlock; + } @@ -2347,7 +2844,9 @@ index 0000000..7d00a14 + u_int32_t from_ref; + + DP("set: %s to %s", from->name, to->name); -+ /* Features must not change. Artifical restriction. */ ++ /* Features must not change. ++ * Not an artifical restriction anymore, as we must prevent ++ * possible loops created by swapping in setlist type of sets. */ + if (from->type->features != to->type->features) + return -ENOEXEC; + @@ -2372,63 +2871,31 @@ index 0000000..7d00a14 + * List set data + */ + -+static inline void -+__set_hash_bindings_size_list(struct ip_set_hash *set_hash, -+ ip_set_id_t id, size_t *size) -+{ -+ if (set_hash->id == id) -+ *size += sizeof(struct ip_set_hash_list); -+} -+ -+static inline void -+__set_hash_bindings_size_save(struct ip_set_hash *set_hash, -+ ip_set_id_t id, size_t *size) -+{ -+ if (set_hash->id == id) -+ *size += sizeof(struct ip_set_hash_save); -+} -+ -+static inline void -+__set_hash_bindings(struct ip_set_hash *set_hash, -+ ip_set_id_t id, void *data, int *used) -+{ -+ if (set_hash->id == id) { -+ struct ip_set_hash_list *hash_list = -+ (struct ip_set_hash_list *)(data + *used); -+ -+ hash_list->ip = set_hash->ip; -+ hash_list->binding = set_hash->binding; -+ *used += sizeof(struct ip_set_hash_list); -+ } -+} -+ -+static int ip_set_list_set(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) ++static int ++ip_set_list_set(ip_set_id_t index, void *data, int *used, int len) +{ + struct ip_set *set = ip_set_list[index]; + struct ip_set_list *set_list; + + /* Pointer to our header */ -+ set_list = (struct ip_set_list *) (data + *used); ++ set_list = data + *used; + -+ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); ++ DP("set: %s, used: %d len %u %p %p", set->name, *used, len, data, data + *used); + + /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_list) > len) ++ if (*used + ALIGNED(sizeof(struct ip_set_list)) > len) + goto not_enough_mem; -+ *used += sizeof(struct ip_set_list); ++ *used += ALIGNED(sizeof(struct ip_set_list)); + + read_lock_bh(&set->lock); + /* Get and ensure set specific header size */ -+ set_list->header_size = set->type->header_size; ++ set_list->header_size = ALIGNED(set->type->header_size); + if (*used + set_list->header_size > len) + goto unlock_set; + + /* Fill in the header */ + set_list->index = index; -+ set_list->binding = set->binding; ++ set_list->binding = IP_SET_INVALID_ID; + set_list->ref = atomic_read(&set->ref); + + /* Fill in set spefific header data */ @@ -2436,27 +2903,18 @@ index 0000000..7d00a14 + *used += set_list->header_size; + + /* Get and ensure set specific members size */ -+ set_list->members_size = set->type->list_members_size(set); ++ set_list->members_size = set->type->list_members_size(set, DONT_ALIGN); + if (*used + set_list->members_size > len) + goto unlock_set; + + /* Fill in set spefific members data */ -+ set->type->list_members(set, data + *used); ++ set->type->list_members(set, data + *used, DONT_ALIGN); + *used += set_list->members_size; + read_unlock_bh(&set->lock); + + /* Bindings */ -+ -+ /* Get and ensure set specific bindings size */ + set_list->bindings_size = 0; -+ FOREACH_HASH_DO(__set_hash_bindings_size_list, -+ set->id, &set_list->bindings_size); -+ if (*used + set_list->bindings_size > len) -+ goto not_enough_mem; + -+ /* Fill in set spefific bindings data */ -+ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); -+ + return 0; + + unlock_set: @@ -2469,53 +2927,71 @@ index 0000000..7d00a14 +/* + * Save sets + */ -+static int ip_set_save_set(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) ++static inline int ++ip_set_save_marker(void *data, int *used, int len) ++{ ++ struct ip_set_save *set_save; ++ ++ DP("used %u, len %u", *used, len); ++ /* Get and ensure header size */ ++ if (*used + ALIGNED(sizeof(struct ip_set_save)) > len) ++ return -ENOMEM; ++ ++ /* Marker: just for backward compatibility */ ++ set_save = data + *used; ++ set_save->index = IP_SET_INVALID_ID; ++ set_save->header_size = 0; ++ set_save->members_size = 0; ++ *used += ALIGNED(sizeof(struct ip_set_save)); ++ ++ return 0; ++} ++ ++static int ++ip_set_save_set(ip_set_id_t index, void *data, int *used, int len) +{ + struct ip_set *set; + struct ip_set_save *set_save; + + /* Pointer to our header */ -+ set_save = (struct ip_set_save *) (data + *used); ++ set_save = data + *used; + + /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_save) > len) ++ if (*used + ALIGNED(sizeof(struct ip_set_save)) > len) + goto not_enough_mem; -+ *used += sizeof(struct ip_set_save); ++ *used += ALIGNED(sizeof(struct ip_set_save)); + + set = ip_set_list[index]; -+ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, ++ DP("set: %s, used: %d(%d) %p %p", set->name, *used, len, + data, data + *used); + + read_lock_bh(&set->lock); + /* Get and ensure set specific header size */ -+ set_save->header_size = set->type->header_size; ++ set_save->header_size = ALIGNED(set->type->header_size); + if (*used + set_save->header_size > len) + goto unlock_set; + + /* Fill in the header */ + set_save->index = index; -+ set_save->binding = set->binding; ++ set_save->binding = IP_SET_INVALID_ID; + + /* Fill in set spefific header data */ + set->type->list_header(set, data + *used); + *used += set_save->header_size; + -+ DP("set header filled: %s, used: %u(%u) %p %p", set->name, *used, -+ set_save->header_size, data, data + *used); ++ DP("set header filled: %s, used: %d(%lu) %p %p", set->name, *used, ++ (unsigned long)set_save->header_size, data, data + *used); + /* Get and ensure set specific members size */ -+ set_save->members_size = set->type->list_members_size(set); ++ set_save->members_size = set->type->list_members_size(set, DONT_ALIGN); + if (*used + set_save->members_size > len) + goto unlock_set; + + /* Fill in set spefific members data */ -+ set->type->list_members(set, data + *used); ++ set->type->list_members(set, data + *used, DONT_ALIGN); + *used += set_save->members_size; + read_unlock_bh(&set->lock); -+ DP("set members filled: %s, used: %u(%u) %p %p", set->name, *used, -+ set_save->members_size, data, data + *used); ++ DP("set members filled: %s, used: %d(%lu) %p %p", set->name, *used, ++ (unsigned long)set_save->members_size, data, data + *used); + return 0; + + unlock_set: @@ -2525,70 +3001,15 @@ index 0000000..7d00a14 + return -EAGAIN; +} + -+static inline void -+__set_hash_save_bindings(struct ip_set_hash *set_hash, -+ ip_set_id_t id, -+ void *data, -+ int *used, -+ int len, -+ int *res) -+{ -+ if (*res == 0 -+ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { -+ struct ip_set_hash_save *hash_save = -+ (struct ip_set_hash_save *)(data + *used); -+ /* Ensure bindings size */ -+ if (*used + sizeof(struct ip_set_hash_save) > len) { -+ *res = -ENOMEM; -+ return; -+ } -+ hash_save->id = set_hash->id; -+ hash_save->ip = set_hash->ip; -+ hash_save->binding = set_hash->binding; -+ *used += sizeof(struct ip_set_hash_save); -+ } -+} -+ -+static int ip_set_save_bindings(ip_set_id_t index, -+ void *data, -+ int *used, -+ int len) -+{ -+ int res = 0; -+ struct ip_set_save *set_save; -+ -+ DP("used %u, len %u", *used, len); -+ /* Get and ensure header size */ -+ if (*used + sizeof(struct ip_set_save) > len) -+ return -ENOMEM; -+ -+ /* Marker */ -+ set_save = (struct ip_set_save *) (data + *used); -+ set_save->index = IP_SET_INVALID_ID; -+ set_save->header_size = 0; -+ set_save->members_size = 0; -+ *used += sizeof(struct ip_set_save); -+ -+ DP("marker added used %u, len %u", *used, len); -+ /* Fill in bindings data */ -+ if (index != IP_SET_INVALID_ID) -+ /* Sets are identified by id in hash */ -+ index = ip_set_list[index]->id; -+ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); -+ -+ return res; -+} -+ +/* + * Restore sets + */ -+static int ip_set_restore(void *data, -+ int len) ++static int ++ip_set_restore(void *data, int len) +{ + int res = 0; + int line = 0, used = 0, members_size; + struct ip_set *set; -+ struct ip_set_hash_save *hash_save; + struct ip_set_restore *set_restore; + ip_set_id_t index; + @@ -2596,23 +3017,23 @@ index 0000000..7d00a14 + while (1) { + line++; + -+ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); ++ DP("%d %zu %d", used, ALIGNED(sizeof(struct ip_set_restore)), len); + /* Get and ensure header size */ -+ if (used + sizeof(struct ip_set_restore) > len) ++ if (used + ALIGNED(sizeof(struct ip_set_restore)) > len) + return line; -+ set_restore = (struct ip_set_restore *) (data + used); -+ used += sizeof(struct ip_set_restore); ++ set_restore = data + used; ++ used += ALIGNED(sizeof(struct ip_set_restore)); + + /* Ensure data size */ -+ if (used -+ + set_restore->header_size ++ if (used ++ + set_restore->header_size + + set_restore->members_size > len) + return line; + + /* Check marker */ + if (set_restore->index == IP_SET_INVALID_ID) { + line--; -+ goto bindings; ++ goto finish; + } + + /* Try to create the set */ @@ -2625,7 +3046,7 @@ index 0000000..7d00a14 + + if (res != 0) + return line; -+ used += set_restore->header_size; ++ used += ALIGNED(set_restore->header_size); + + index = ip_set_find_byindex(set_restore->index); + DP("index %u, restore_index %u", index, set_restore->index); @@ -2634,66 +3055,29 @@ index 0000000..7d00a14 + /* Try to restore members data */ + set = ip_set_list[index]; + members_size = 0; -+ DP("members_size %u reqsize %u", -+ set_restore->members_size, set->type->reqsize); -+ while (members_size + set->type->reqsize <= ++ DP("members_size %lu reqsize %lu", ++ (unsigned long)set_restore->members_size, ++ (unsigned long)set->type->reqsize); ++ while (members_size + ALIGNED(set->type->reqsize) <= + set_restore->members_size) { + line++; -+ DP("members: %u, line %u", members_size, line); -+ res = __ip_set_addip(index, ++ DP("members: %d, line %d", members_size, line); ++ res = ip_set_addip(set, + data + used + members_size, + set->type->reqsize); -+ if (!(res == 0 || res == -EEXIST)) ++ if (!(res == 0 || res == -EEXIST)) + return line; -+ members_size += set->type->reqsize; ++ members_size += ALIGNED(set->type->reqsize); + } + -+ DP("members_size %u %u", -+ set_restore->members_size, members_size); ++ DP("members_size %lu %d", ++ (unsigned long)set_restore->members_size, members_size); + if (members_size != set_restore->members_size) + return line++; + used += set_restore->members_size; + } + -+ bindings: -+ /* Loop to restore bindings */ -+ while (used < len) { -+ line++; -+ -+ DP("restore binding, line %u", line); -+ /* Get and ensure size */ -+ if (used + sizeof(struct ip_set_hash_save) > len) -+ return line; -+ hash_save = (struct ip_set_hash_save *) (data + used); -+ used += sizeof(struct ip_set_hash_save); -+ -+ /* hash_save->id is used to store the index */ -+ index = ip_set_find_byindex(hash_save->id); -+ DP("restore binding index %u, id %u, %u -> %u", -+ index, hash_save->id, hash_save->ip, hash_save->binding); -+ if (index != hash_save->id) -+ return line; -+ if (ip_set_find_byindex(hash_save->binding) == IP_SET_INVALID_ID) { -+ DP("corrupt binding set index %u", hash_save->binding); -+ return line; -+ } -+ set = ip_set_list[hash_save->id]; -+ /* Null valued IP means default binding */ -+ if (hash_save->ip) -+ res = ip_set_hash_add(set->id, -+ hash_save->ip, -+ hash_save->binding); -+ else { -+ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); -+ write_lock_bh(&ip_set_lock); -+ set->binding = hash_save->binding; -+ __ip_set_get(set->binding); -+ write_unlock_bh(&ip_set_lock); -+ DP("default binding: %u", set->binding); -+ } -+ if (res != 0) -+ return line; -+ } ++ finish: + if (used != len) + return line; + @@ -2705,17 +3089,17 @@ index 0000000..7d00a14 +{ + void *data; + int res = 0; /* Assume OK */ ++ size_t offset; + unsigned *op; + struct ip_set_req_adt *req_adt; + ip_set_id_t index = IP_SET_INVALID_ID; -+ int (*adtfn)(ip_set_id_t index, -+ const void *data, size_t size); ++ int (*adtfn)(struct ip_set *set, ++ const void *data, u_int32_t size); + struct fn_table { -+ int (*fn)(ip_set_id_t index, -+ const void *data, size_t size); ++ int (*fn)(struct ip_set *set, ++ const void *data, u_int32_t size); + } adtfn_table[] = -+ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, -+ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, ++ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, + }; + + DP("optval=%d, user=%p, len=%d", optval, user, len); @@ -2735,11 +3119,11 @@ index 0000000..7d00a14 + } + if (copy_from_user(data, user, len) != 0) { + res = -EFAULT; -+ goto done; ++ goto cleanup; + } + if (down_interruptible(&ip_set_app_mutex)) { + res = -EINTR; -+ goto done; ++ goto cleanup; + } + + op = (unsigned *)data; @@ -2747,22 +3131,23 @@ index 0000000..7d00a14 + + if (*op < IP_SET_OP_VERSION) { + /* Check the version at the beginning of operations */ -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; -+ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ struct ip_set_req_version *req_version = data; ++ if (!(req_version->version == IP_SET_PROTOCOL_UNALIGNED ++ || req_version->version == IP_SET_PROTOCOL_VERSION)) { + res = -EPROTO; + goto done; + } ++ protocol_version = req_version->version; + } + + switch (*op) { + case IP_SET_OP_CREATE:{ -+ struct ip_set_req_create *req_create -+ = (struct ip_set_req_create *) data; ++ struct ip_set_req_create *req_create = data; ++ offset = ALIGNED(sizeof(struct ip_set_req_create)); + -+ if (len < sizeof(struct ip_set_req_create)) { ++ if (len < offset) { + ip_set_printk("short CREATE data (want >=%zu, got %u)", -+ sizeof(struct ip_set_req_create), len); ++ offset, len); + res = -EINVAL; + goto done; + } @@ -2771,13 +3156,12 @@ index 0000000..7d00a14 + res = ip_set_create(req_create->name, + req_create->typename, + IP_SET_INVALID_ID, -+ data + sizeof(struct ip_set_req_create), -+ len - sizeof(struct ip_set_req_create)); ++ data + offset, ++ len - offset); + goto done; + } + case IP_SET_OP_DESTROY:{ -+ struct ip_set_req_std *req_destroy -+ = (struct ip_set_req_std *) data; ++ struct ip_set_req_std *req_destroy = data; + + if (len != sizeof(struct ip_set_req_std)) { + ip_set_printk("invalid DESTROY data (want %zu, got %u)", @@ -2785,7 +3169,7 @@ index 0000000..7d00a14 + res = -EINVAL; + goto done; + } -+ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { ++ if (STREQ(req_destroy->name, IPSET_TOKEN_ALL)) { + /* Destroy all sets */ + index = IP_SET_INVALID_ID; + } else { @@ -2802,8 +3186,7 @@ index 0000000..7d00a14 + goto done; + } + case IP_SET_OP_FLUSH:{ -+ struct ip_set_req_std *req_flush = -+ (struct ip_set_req_std *) data; ++ struct ip_set_req_std *req_flush = data; + + if (len != sizeof(struct ip_set_req_std)) { + ip_set_printk("invalid FLUSH data (want %zu, got %u)", @@ -2811,7 +3194,7 @@ index 0000000..7d00a14 + res = -EINVAL; + goto done; + } -+ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { ++ if (STREQ(req_flush->name, IPSET_TOKEN_ALL)) { + /* Flush all sets */ + index = IP_SET_INVALID_ID; + } else { @@ -2827,8 +3210,7 @@ index 0000000..7d00a14 + goto done; + } + case IP_SET_OP_RENAME:{ -+ struct ip_set_req_create *req_rename -+ = (struct ip_set_req_create *) data; ++ struct ip_set_req_create *req_rename = data; + + if (len != sizeof(struct ip_set_req_create)) { + ip_set_printk("invalid RENAME data (want %zu, got %u)", @@ -2849,8 +3231,7 @@ index 0000000..7d00a14 + goto done; + } + case IP_SET_OP_SWAP:{ -+ struct ip_set_req_create *req_swap -+ = (struct ip_set_req_create *) data; ++ struct ip_set_req_create *req_swap = data; + ip_set_id_t to_index; + + if (len != sizeof(struct ip_set_req_create)) { @@ -2876,38 +3257,49 @@ index 0000000..7d00a14 + res = ip_set_swap(index, to_index); + goto done; + } -+ default: ++ default: + break; /* Set identified by id */ + } + + /* There we may have add/del/test/bind/unbind/test_bind operations */ -+ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { ++ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_IP) { + res = -EBADMSG; + goto done; + } + adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; + -+ if (len < sizeof(struct ip_set_req_adt)) { ++ if (len < ALIGNED(sizeof(struct ip_set_req_adt))) { + ip_set_printk("short data in adt request (want >=%zu, got %u)", -+ sizeof(struct ip_set_req_adt), len); ++ ALIGNED(sizeof(struct ip_set_req_adt)), len); + res = -EINVAL; + goto done; + } -+ req_adt = (struct ip_set_req_adt *) data; ++ req_adt = data; + -+ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ -+ if (!(*op == IP_SET_OP_UNBIND_SET -+ && req_adt->index == IP_SET_INVALID_ID)) { -+ index = ip_set_find_byindex(req_adt->index); -+ if (index == IP_SET_INVALID_ID) { -+ res = -ENOENT; ++ index = ip_set_find_byindex(req_adt->index); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ do { ++ struct ip_set *set = ip_set_list[index]; ++ size_t offset = ALIGNED(sizeof(struct ip_set_req_adt)); ++ ++ IP_SET_ASSERT(set); ++ ++ if (len - offset != set->type->reqsize) { ++ ip_set_printk("data length wrong (want %lu, have %zu)", ++ (long unsigned)set->type->reqsize, ++ len - offset); ++ res = -EINVAL; + goto done; + } -+ } -+ res = adtfn(index, data, len); ++ res = adtfn(set, data + offset, len - offset); ++ } while (0); + + done: + up(&ip_set_app_mutex); ++ cleanup: + vfree(data); + if (res > 0) + res = 0; @@ -2915,7 +3307,7 @@ index 0000000..7d00a14 + return res; +} + -+static int ++static int +ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) +{ + int res = 0; @@ -2941,11 +3333,11 @@ index 0000000..7d00a14 + } + if (copy_from_user(data, user, *len) != 0) { + res = -EFAULT; -+ goto done; ++ goto cleanup; + } + if (down_interruptible(&ip_set_app_mutex)) { + res = -EINTR; -+ goto done; ++ goto cleanup; + } + + op = (unsigned *) data; @@ -2953,18 +3345,18 @@ index 0000000..7d00a14 + + if (*op < IP_SET_OP_VERSION) { + /* Check the version at the beginning of operations */ -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; -+ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ struct ip_set_req_version *req_version = data; ++ if (!(req_version->version == IP_SET_PROTOCOL_UNALIGNED ++ || req_version->version == IP_SET_PROTOCOL_VERSION)) { + res = -EPROTO; + goto done; + } ++ protocol_version = req_version->version; + } + + switch (*op) { + case IP_SET_OP_VERSION: { -+ struct ip_set_req_version *req_version = -+ (struct ip_set_req_version *) data; ++ struct ip_set_req_version *req_version = data; + + if (*len != sizeof(struct ip_set_req_version)) { + ip_set_printk("invalid VERSION (want %zu, got %d)", @@ -2980,8 +3372,7 @@ index 0000000..7d00a14 + goto done; + } + case IP_SET_OP_GET_BYNAME: { -+ struct ip_set_req_get_set *req_get -+ = (struct ip_set_req_get_set *) data; ++ struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set)) { + ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", @@ -2995,8 +3386,7 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_GET_BYINDEX: { -+ struct ip_set_req_get_set *req_get -+ = (struct ip_set_req_get_set *) data; ++ struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set)) { + ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", @@ -3012,8 +3402,7 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_ADT_GET: { -+ struct ip_set_req_adt_get *req_get -+ = (struct ip_set_req_adt_get *) data; ++ struct ip_set_req_adt_get *req_get = data; + + if (*len != sizeof(struct ip_set_req_adt_get)) { + ip_set_printk("invalid ADT_GET (want %zu, got %d)", @@ -3035,8 +3424,7 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_MAX_SETS: { -+ struct ip_set_req_max_sets *req_max_sets -+ = (struct ip_set_req_max_sets *) data; ++ struct ip_set_req_max_sets *req_max_sets = data; + ip_set_id_t i; + + if (*len != sizeof(struct ip_set_req_max_sets)) { @@ -3046,11 +3434,11 @@ index 0000000..7d00a14 + goto done; + } + -+ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { ++ if (STREQ(req_max_sets->set.name, IPSET_TOKEN_ALL)) { + req_max_sets->set.index = IP_SET_INVALID_ID; + } else { + req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; -+ req_max_sets->set.index = ++ req_max_sets->set.index = + ip_set_find_byname(req_max_sets->set.name); + if (req_max_sets->set.index == IP_SET_INVALID_ID) { + res = -ENOENT; @@ -3065,30 +3453,29 @@ index 0000000..7d00a14 + } + goto copy; + } -+ case IP_SET_OP_LIST_SIZE: ++ case IP_SET_OP_LIST_SIZE: + case IP_SET_OP_SAVE_SIZE: { -+ struct ip_set_req_setnames *req_setnames -+ = (struct ip_set_req_setnames *) data; ++ struct ip_set_req_setnames *req_setnames = data; + struct ip_set_name_list *name_list; + struct ip_set *set; + ip_set_id_t i; + int used; + -+ if (*len < sizeof(struct ip_set_req_setnames)) { ++ if (*len < ALIGNED(sizeof(struct ip_set_req_setnames))) { + ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", -+ sizeof(struct ip_set_req_setnames), *len); ++ ALIGNED(sizeof(struct ip_set_req_setnames)), ++ *len); + res = -EINVAL; + goto done; + } + + req_setnames->size = 0; -+ used = sizeof(struct ip_set_req_setnames); ++ used = ALIGNED(sizeof(struct ip_set_req_setnames)); + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] == NULL) + continue; -+ name_list = (struct ip_set_name_list *) -+ (data + used); -+ used += sizeof(struct ip_set_name_list); ++ name_list = data + used; ++ used += ALIGNED(sizeof(struct ip_set_name_list)); + if (used > copylen) { + res = -EAGAIN; + goto done; @@ -3110,27 +3497,12 @@ index 0000000..7d00a14 + || req_setnames->index == i)) + continue; + /* Update size */ -+ switch (*op) { -+ case IP_SET_OP_LIST_SIZE: { -+ req_setnames->size += sizeof(struct ip_set_list) -+ + set->type->header_size -+ + set->type->list_members_size(set); -+ /* Sets are identified by id in the hash */ -+ FOREACH_HASH_DO(__set_hash_bindings_size_list, -+ set->id, &req_setnames->size); -+ break; -+ } -+ case IP_SET_OP_SAVE_SIZE: { -+ req_setnames->size += sizeof(struct ip_set_save) -+ + set->type->header_size -+ + set->type->list_members_size(set); -+ FOREACH_HASH_DO(__set_hash_bindings_size_save, -+ set->id, &req_setnames->size); -+ break; -+ } -+ default: -+ break; -+ } ++ req_setnames->size += ++ (*op == IP_SET_OP_LIST_SIZE ? ++ ALIGNED(sizeof(struct ip_set_list)) : ++ ALIGNED(sizeof(struct ip_set_save))) ++ + ALIGNED(set->type->header_size) ++ + set->type->list_members_size(set, DONT_ALIGN); + } + if (copylen != used) { + res = -EAGAIN; @@ -3139,8 +3511,7 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_LIST: { -+ struct ip_set_req_list *req_list -+ = (struct ip_set_req_list *) data; ++ struct ip_set_req_list *req_list = data; + ip_set_id_t i; + int used; + @@ -3176,8 +3547,7 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_SAVE: { -+ struct ip_set_req_list *req_save -+ = (struct ip_set_req_list *) data; ++ struct ip_set_req_list *req_save = data; + ip_set_id_t i; + int used; + @@ -3193,19 +3563,29 @@ index 0000000..7d00a14 + res = -ENOENT; + goto done; + } ++ ++#define SETLIST(set) (strcmp(set->type->typename, "setlist") == 0) ++ + used = 0; + if (index == IP_SET_INVALID_ID) { -+ /* Save all sets */ ++ /* Save all sets: ugly setlist type dependency */ ++ int setlist = 0; ++ setlists: + for (i = 0; i < ip_set_max && res == 0; i++) { -+ if (ip_set_list[i] != NULL) ++ if (ip_set_list[i] != NULL ++ && !(setlist ^ SETLIST(ip_set_list[i]))) + res = ip_set_save_set(i, data, &used, *len); + } ++ if (!setlist) { ++ setlist = 1; ++ goto setlists; ++ } + } else { + /* Save an individual set */ + res = ip_set_save_set(index, data, &used, *len); + } + if (res == 0) -+ res = ip_set_save_bindings(index, data, &used, *len); ++ res = ip_set_save_marker(data, &used, *len); + + if (res != 0) + goto done; @@ -3216,20 +3596,18 @@ index 0000000..7d00a14 + goto copy; + } + case IP_SET_OP_RESTORE: { -+ struct ip_set_req_setnames *req_restore -+ = (struct ip_set_req_setnames *) data; ++ struct ip_set_req_setnames *req_restore = data; ++ size_t offset = ALIGNED(sizeof(struct ip_set_req_setnames)); + int line; + -+ if (*len < sizeof(struct ip_set_req_setnames) -+ || *len != req_restore->size) { -+ ip_set_printk("invalid RESTORE (want =%zu, got %d)", -+ req_restore->size, *len); ++ if (*len < offset || *len != req_restore->size) { ++ ip_set_printk("invalid RESTORE (want =%lu, got %d)", ++ (long unsigned)req_restore->size, *len); + res = -EINVAL; + goto done; + } -+ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), -+ req_restore->size - sizeof(struct ip_set_req_setnames)); -+ DP("ip_set_restore: %u", line); ++ line = ip_set_restore(data + offset, req_restore->size - offset); ++ DP("ip_set_restore: %d", line); + if (line != 0) { + res = -EAGAIN; + req_restore->size = line; @@ -3244,7 +3622,7 @@ index 0000000..7d00a14 + } /* end of switch(op) */ + + copy: -+ DP("set %s, copylen %u", index != IP_SET_INVALID_ID ++ DP("set %s, copylen %d", index != IP_SET_INVALID_ID + && ip_set_list[index] + ? ip_set_list[index]->name + : ":all:", copylen); @@ -3252,6 +3630,7 @@ index 0000000..7d00a14 + + done: + up(&ip_set_app_mutex); ++ cleanup: + vfree(data); + if (res > 0) + res = 0; @@ -3274,39 +3653,33 @@ index 0000000..7d00a14 +#endif +}; + -+static int max_sets, hash_size; ++static int max_sets; ++ +module_param(max_sets, int, 0600); +MODULE_PARM_DESC(max_sets, "maximal number of sets"); -+module_param(hash_size, int, 0600); -+MODULE_PARM_DESC(hash_size, "hash size for bindings"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("module implementing core IP set support"); + -+static int __init ip_set_init(void) ++static int __init ++ip_set_init(void) +{ + int res; -+ ip_set_id_t i; + -+ get_random_bytes(&ip_set_hash_random, 4); ++ /* For the -rt branch, DECLARE_MUTEX/init_MUTEX avoided */ ++ sema_init(&ip_set_app_mutex, 1); ++ + if (max_sets) + ip_set_max = max_sets; ++ if (ip_set_max >= IP_SET_INVALID_ID) ++ ip_set_max = IP_SET_INVALID_ID - 1; ++ + ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); + if (!ip_set_list) { + printk(KERN_ERR "Unable to create ip_set_list\n"); + return -ENOMEM; + } + memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); -+ if (hash_size) -+ ip_set_bindings_hash_size = hash_size; -+ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); -+ if (!ip_set_hash) { -+ printk(KERN_ERR "Unable to create ip_set_hash\n"); -+ vfree(ip_set_list); -+ return -ENOMEM; -+ } -+ for (i = 0; i < ip_set_bindings_hash_size; i++) -+ INIT_LIST_HEAD(&ip_set_hash[i]); + + INIT_LIST_HEAD(&set_type_list); + @@ -3314,18 +3687,19 @@ index 0000000..7d00a14 + if (res != 0) { + ip_set_printk("SO_SET registry failed: %d", res); + vfree(ip_set_list); -+ vfree(ip_set_hash); + return res; + } ++ ++ printk("ip_set version %u loaded\n", IP_SET_PROTOCOL_VERSION); + return 0; +} + -+static void __exit ip_set_fini(void) ++static void __exit ++ip_set_fini(void) +{ + /* There can't be any existing set or binding */ + nf_unregister_sockopt(&so_set); + vfree(ip_set_list); -+ vfree(ip_set_hash); + DP("these are the famous last words"); +} + @@ -3334,7 +3708,10 @@ index 0000000..7d00a14 + +EXPORT_SYMBOL(ip_set_get_byname); +EXPORT_SYMBOL(ip_set_get_byindex); -+EXPORT_SYMBOL(ip_set_put); ++EXPORT_SYMBOL(ip_set_put_byindex); ++EXPORT_SYMBOL(ip_set_id); ++EXPORT_SYMBOL(__ip_set_get_byname); ++EXPORT_SYMBOL(__ip_set_put_byindex); + +EXPORT_SYMBOL(ip_set_addip_kernel); +EXPORT_SYMBOL(ip_set_delip_kernel); @@ -3344,256 +3721,127 @@ index 0000000..7d00a14 +module_exit(ip_set_fini); diff --git a/net/ipv4/netfilter/ip_set_iphash.c b/net/ipv4/netfilter/ip_set_iphash.c new file mode 100644 -index 0000000..63f7ac9 +index 0000000..1accbe3 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_iphash.c -@@ -0,0 +1,429 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik +@@ -0,0 +1,164 @@ ++/* Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing an ip hash set */ + +#include ++#include +#include +#include -+#include -+#include -+#include -+#include ++#include +#include +#include +#include +#include -+#include +#include + +#include + -+#include +#include + +static int limit = MAX_RANGE; + +static inline __u32 -+jhash_ip(const struct ip_set_iphash *map, uint16_t i, ip_set_ip_t ip) -+{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++iphash_id(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_iphash *map = set->data; + __u32 id; + u_int16_t i; + ip_set_ip_t *elem; + -+ *hash_ip = ip & map->netmask; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); -+ ++ ++ ip &= map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(ip)); + for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ id = jhash_ip(map, i, ip) % map->hashsize; + DP("hash key: %u", id); + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) ++ if (*elem == ip) + return id; -+ /* No shortcut at testing - there can be deleted -+ * entries. */ ++ /* No shortcut - there can be deleted entries. */ + } + return UINT_MAX; +} + +static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++iphash_test(struct ip_set *set, ip_set_ip_t ip) +{ -+ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); ++ return (ip && iphash_id(set, ip) != UINT_MAX); +} + -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; -+ -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} ++#define KADT_CONDITION + -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(iphash, test) ++KADT(iphash, test, ipaddr) + +static inline int -+__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++__iphash_add(struct ip_set_iphash *map, ip_set_ip_t *ip) +{ + __u32 probe; + u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ if (!ip || map->elements >= limit) -+ return -ERANGE; -+ -+ *hash_ip = ip & map->netmask; ++ ip_set_ip_t *elem, *slot = NULL; + + for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ probe = jhash_ip(map, i, *ip) % map->hashsize; + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == *hash_ip) ++ if (*elem == *ip) + return -EEXIST; -+ if (!*elem) { -+ *elem = *hash_ip; -+ map->elements++; -+ return 0; -+ } ++ if (!(slot || *elem)) ++ slot = elem; ++ /* There can be deleted entries, must check all slots */ ++ } ++ if (slot) { ++ *slot = *ip; ++ map->elements++; ++ return 0; + } + /* Trigger rehashing */ + return -EAGAIN; +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) ++static inline int ++iphash_add(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; ++ struct ip_set_iphash *map = set->data; ++ ++ if (!ip || map->elements >= limit) ++ return -ERANGE; + -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); ++ ip &= map->netmask; ++ return __iphash_add(map, &ip); +} + -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __addip((struct ip_set_iphash *) set->data, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(iphash, add) ++KADT(iphash, add, ipaddr) + -+static int retry(struct ip_set *set) ++static inline void ++__iphash_retry(struct ip_set_iphash *tmp, struct ip_set_iphash *map) +{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ ip_set_ip_t hash_ip, *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_iphash *tmp; -+ -+ if (map->resize == 0) -+ return -ERANGE; -+ -+ again: -+ res = 0; -+ -+ /* Calculate new hash size */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; -+ -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_iphash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_iphash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; -+ } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; + tmp->netmask = map->netmask; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); -+ -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_iphash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __addip(tmp, *elem, &hash_ip); -+ } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; -+ } -+ -+ /* Success at resizing! */ -+ members = map->members; -+ -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); -+ -+ harray_free(members); -+ kfree(tmp); -+ -+ return 0; +} + ++HASH_RETRY(iphash, ip_set_ip_t) ++ +static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++iphash_del(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_iphash *map = set->data; + ip_set_ip_t id, *elem; + + if (!ip) + return -ERANGE; + -+ id = hash_id(set, ip, hash_ip); ++ id = iphash_id(set, ip); + if (id == UINT_MAX) + return -EEXIST; + @@ -3604,159 +3852,35 @@ index 0000000..63f7ac9 + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iphash *req = -+ (struct ip_set_req_iphash *) data; -+ -+ if (size != sizeof(struct ip_set_req_iphash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(iphash, del) ++KADT(iphash, del, ipaddr) + -+static int create(struct ip_set *set, const void *data, size_t size) ++static inline int ++__iphash_create(const struct ip_set_req_iphash_create *req, ++ struct ip_set_iphash *map) +{ -+ struct ip_set_req_iphash_create *req = -+ (struct ip_set_req_iphash_create *) data; -+ struct ip_set_iphash *map; -+ uint16_t i; -+ -+ if (size != sizeof(struct ip_set_req_iphash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iphash_create), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } -+ -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_iphash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_iphash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; + map->netmask = req->netmask; -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } -+ -+ set->data = map; ++ + return 0; +} + -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ map->elements = 0; -+} ++HASH_CREATE(iphash, ip_set_ip_t) ++HASH_DESTROY(iphash) + -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ struct ip_set_req_iphash_create *header = -+ (struct ip_set_req_iphash_create *) data; ++HASH_FLUSH(iphash, ip_set_ip_t) + -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; ++static inline void ++__iphash_list_header(const struct ip_set_iphash *map, ++ struct ip_set_req_iphash_create *header) ++{ + header->netmask = map->netmask; +} + -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ -+ return (map->hashsize * sizeof(ip_set_ip_t)); -+} -+ -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; -+ ip_set_ip_t i, *elem; -+ -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; -+ } -+} ++HASH_LIST_HEADER(iphash) ++HASH_LIST_MEMBERS_SIZE(iphash, ip_set_ip_t) ++HASH_LIST_MEMBERS(iphash, ip_set_ip_t) + -+static struct ip_set_type ip_set_iphash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_iphash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iphash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_RTYPE(iphash, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); @@ -3764,32 +3888,20 @@ index 0000000..63f7ac9 +module_param(limit, int, 0600); +MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + -+static int __init ip_set_iphash_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_iphash); -+} -+ -+static void __exit ip_set_iphash_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_iphash); -+} -+ -+module_init(ip_set_iphash_init); -+module_exit(ip_set_iphash_fini); ++REGISTER_MODULE(iphash) diff --git a/net/ipv4/netfilter/ip_set_ipmap.c b/net/ipv4/netfilter/ip_set_ipmap.c new file mode 100644 -index 0000000..0fe8d3c +index 0000000..be3c538 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_ipmap.c -@@ -0,0 +1,336 @@ +@@ -0,0 +1,158 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the single bitmap type */ @@ -3797,9 +3909,6 @@ index 0000000..0fe8d3c +#include +#include +#include -+#include -+#include -+#include +#include +#include +#include @@ -3810,192 +3919,66 @@ index 0000000..0fe8d3c +static inline ip_set_ip_t +ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) +{ -+ return (ip - map->first_ip)/map->hosts; ++ return ((ip & map->netmask) - map->first_ip)/map->hosts; +} + +static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++ipmap_test(const struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ const struct ip_set_ipmap *map = set->data; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ *hash_ip = ip & map->netmask; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ return !!test_bit(ip_to_id(map, *hash_ip), map->members); ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ return !!test_bit(ip_to_id(map, ip), map->members); +} + -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} ++#define KADT_CONDITION + -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ int res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+ return (res < 0 ? 0 : res); -+} ++UADT(ipmap, test) ++KADT(ipmap, test, ipaddr) + +static inline int -+__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++ipmap_add(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_ipmap *map = set->data; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ *hash_ip = ip & map->netmask; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (test_and_set_bit(ip_to_id(map, ip), map->members)) + return -EEXIST; + + return 0; +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); -+ return __addip(set, req->ip, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __addip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(ipmap, add) ++KADT(ipmap, add, ipaddr) + -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++static inline int ++ipmap_del(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_ipmap *map = set->data; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ *hash_ip = ip & map->netmask; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (!test_and_clear_bit(ip_to_id(map, ip), map->members)) + return -EEXIST; + + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipmap *req = -+ (struct ip_set_req_ipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_ipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(ipmap, del) ++KADT(ipmap, del, ipaddr) + -+static int create(struct ip_set *set, const void *data, size_t size) ++static inline int ++__ipmap_create(const struct ip_set_req_ipmap_create *req, ++ struct ip_set_ipmap *map) +{ -+ int newbytes; -+ struct ip_set_req_ipmap_create *req = -+ (struct ip_set_req_ipmap_create *) data; -+ struct ip_set_ipmap *map; -+ -+ if (size != sizeof(struct ip_set_req_ipmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u.%u.%u.%u to %u.%u.%u.%u", -+ HIPQUAD(req->from), HIPQUAD(req->to)); -+ -+ if (req->from > req->to) { -+ DP("bad ip range"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipmap)); -+ return -ENOMEM; -+ } -+ map->first_ip = req->from; -+ map->last_ip = req->to; + map->netmask = req->netmask; + + if (req->netmask == 0xFFFFFFFF) { @@ -4004,12 +3987,12 @@ index 0000000..0fe8d3c + } else { + unsigned int mask_bits, netmask_bits; + ip_set_ip_t mask; -+ ++ + map->first_ip &= map->netmask; /* Should we better bark? */ -+ ++ + mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); + netmask_bits = mask_to_bits(map->netmask); -+ ++ + if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF)) + || netmask_bits <= mask_bits) + return -ENOEXEC; @@ -4020,742 +4003,827 @@ index 0000000..0fe8d3c + map->sizeid = 2 << (netmask_bits - mask_bits - 1); + } + if (map->sizeid > MAX_RANGE + 1) { -+ ip_set_printk("range too big (max %d addresses)", -+ MAX_RANGE+1); -+ kfree(map); ++ ip_set_printk("range too big, %d elements (max %d)", ++ map->sizeid, MAX_RANGE+1); + return -ENOEXEC; + } + DP("hosts %u, sizeid %u", map->hosts, map->sizeid); -+ newbytes = bitmap_bytes(0, map->sizeid - 1); -+ map->members = kmalloc(newbytes, GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); -+ return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ -+ kfree(map->members); -+ kfree(map); -+ -+ set->data = NULL; ++ return bitmap_bytes(0, map->sizeid - 1); +} + -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); -+} ++BITMAP_CREATE(ipmap) ++BITMAP_DESTROY(ipmap) ++BITMAP_FLUSH(ipmap) + -+static void list_header(const struct ip_set *set, void *data) ++static inline void ++__ipmap_list_header(const struct ip_set_ipmap *map, ++ struct ip_set_req_ipmap_create *header) +{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ struct ip_set_req_ipmap_create *header = -+ (struct ip_set_req_ipmap_create *) data; -+ -+ header->from = map->first_ip; -+ header->to = map->last_ip; + header->netmask = map->netmask; +} + -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++BITMAP_LIST_HEADER(ipmap) ++BITMAP_LIST_MEMBERS_SIZE(ipmap, ip_set_ip_t, map->sizeid, ++ test_bit(i, map->members)) + -+ return bitmap_bytes(0, map->sizeid - 1); ++static void ++ipmap_list_members(const struct ip_set *set, void *data, char dont_align) ++{ ++ const struct ip_set_ipmap *map = set->data; ++ uint32_t i, n = 0; ++ ip_set_ip_t *d; ++ ++ if (dont_align) { ++ memcpy(data, map->members, map->size); ++ return; ++ } ++ ++ for (i = 0; i < map->sizeid; i++) ++ if (test_bit(i, map->members)) { ++ d = data + n * IPSET_ALIGN(sizeof(ip_set_ip_t)); ++ *d = map->first_ip + i * map->hosts; ++ n++; ++ } +} + -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; -+ int bytes = bitmap_bytes(0, map->sizeid - 1); -+ -+ memcpy(data, map->members, bytes); -+} -+ -+static struct ip_set_type ip_set_ipmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_ipmap), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_ipmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_TYPE(ipmap, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("ipmap type of IP sets"); + -+static int __init ip_set_ipmap_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_ipmap); -+} -+ -+static void __exit ip_set_ipmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_ipmap); -+} -+ -+module_init(ip_set_ipmap_init); -+module_exit(ip_set_ipmap_fini); ++REGISTER_MODULE(ipmap) diff --git a/net/ipv4/netfilter/ip_set_ipporthash.c b/net/ipv4/netfilter/ip_set_ipporthash.c new file mode 100644 -index 0000000..0d562b8 +index 0000000..4a79c8b --- /dev/null +++ b/net/ipv4/netfilter/ip_set_ipporthash.c -@@ -0,0 +1,581 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik +@@ -0,0 +1,197 @@ ++/* Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing an ip+port hash set */ + +#include ++#include +#include +#include +#include +#include -+#include -+#include -+#include -+#include ++#include +#include +#include +#include +#include -+#include +#include + +#include + -+#include +#include ++#include + +static int limit = MAX_RANGE; + -+/* We must handle non-linear skbs */ -+static inline ip_set_ip_t -+get_port(const struct sk_buff *skb, u_int32_t flags) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ struct iphdr *iph = ip_hdr(skb); -+#else -+ struct iphdr *iph = skb->nh.iph; -+#endif -+ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; -+ -+ switch (iph->protocol) { -+ case IPPROTO_TCP: { -+ struct tcphdr tcph; -+ -+ /* See comments at tcp_match in ip_tables.c */ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ tcph.source : tcph.dest); -+ } -+ case IPPROTO_UDP: { -+ struct udphdr udph; -+ -+ if (offset) -+ return INVALID_PORT; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ udph.source : udph.dest); -+ } -+ default: -+ return INVALID_PORT; -+ } -+} -+ +static inline __u32 -+jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip) ++ipporthash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+#define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16)) -+ -+static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_ipporthash *map = -+ (struct ip_set_ipporthash *) set->data; ++ struct ip_set_ipporthash *map = set->data; + __u32 id; + u_int16_t i; + ip_set_ip_t *elem; + -+ *hash_ip = HASH_IP(map, ip, port); -+ DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip)); ++ ip = pack_ip_port(map, ip, port); ++ ++ if (!ip) ++ return UINT_MAX; + + for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ id = jhash_ip(map, i, ip) % map->hashsize; + DP("hash key: %u", id); + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) ++ if (*elem == ip) + return id; -+ /* No shortcut at testing - there can be deleted -+ * entries. */ ++ /* No shortcut - there can be deleted entries. */ + } + return UINT_MAX; +} + +static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) ++ipporthash_test(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ struct ip_set_ipporthash *map = set->data; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ return (hash_id(set, ip, port, hash_ip) != UINT_MAX); ++ return (ipporthash_id(set, ip, port) != UINT_MAX); +} + -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; ++#define KADT_CONDITION \ ++ ip_set_ip_t port; \ ++ \ ++ if (flags[1] == 0) \ ++ return 0; \ ++ \ ++ port = get_port(skb, ++flags); \ ++ \ ++ if (port == INVALID_PORT) \ ++ return 0; + -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, req->port, hash_ip); -+} ++UADT(ipporthash, test, req->port) ++KADT(ipporthash, test, ipaddr, port) + -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) ++static inline int ++__ipporthash_add(struct ip_set_ipporthash *map, ip_set_ip_t *ip) +{ -+ ip_set_ip_t port; -+ int res; -+ -+ if (flags[index+1] == 0) -+ return 0; -+ -+ port = get_port(skb, flags[index+1]); -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return 0; -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); -+ return (res < 0 ? 0 : res); -+ -+} -+ -+static inline int -+__add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip) -+{ -+ __u32 probe; -+ u_int16_t i; -+ ip_set_ip_t *elem; ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem, *slot = NULL; + + for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, hash_ip) % map->hashsize; ++ probe = jhash_ip(map, i, *ip) % map->hashsize; + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == hash_ip) ++ if (*elem == *ip) + return -EEXIST; -+ if (!*elem) { -+ *elem = hash_ip; -+ map->elements++; -+ return 0; -+ } ++ if (!(slot || *elem)) ++ slot = elem; ++ /* There can be deleted entries, must check all slots */ ++ } ++ if (slot) { ++ *slot = *ip; ++ map->elements++; ++ return 0; + } + /* Trigger rehashing */ + return -EAGAIN; +} + +static inline int -+__addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) ++ipporthash_add(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +{ ++ struct ip_set_ipporthash *map = set->data; + if (map->elements > limit) + return -ERANGE; + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ *hash_ip = HASH_IP(map, ip, port); ++ ip = pack_ip_port(map, ip, port); ++ ++ if (!ip) ++ return -ERANGE; + -+ return __add_haship(map, *hash_ip); ++ return __ipporthash_add(map, &ip); +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; ++UADT(ipporthash, add, req->port) ++KADT(ipporthash, add, ipaddr, port) + -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; -+ } -+ return __addip((struct ip_set_ipporthash *) set->data, -+ req->ip, req->port, hash_ip); ++static inline void ++__ipporthash_retry(struct ip_set_ipporthash *tmp, ++ struct ip_set_ipporthash *map) ++{ ++ tmp->first_ip = map->first_ip; ++ tmp->last_ip = map->last_ip; +} + -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) ++HASH_RETRY(ipporthash, ip_set_ip_t) ++ ++static inline int ++ipporthash_del(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port) +{ -+ ip_set_ip_t port; ++ struct ip_set_ipporthash *map = set->data; ++ ip_set_ip_t id; ++ ip_set_ip_t *elem; + -+ if (flags[index+1] == 0) -+ return -EINVAL; ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ id = ipporthash_id(set, ip, port); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; + -+ port = get_port(skb, flags[index+1]); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; + -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return -EINVAL; ++ return 0; ++} + -+ return __addip((struct ip_set_ipporthash *) set->data, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); ++UADT(ipporthash, del, req->port) ++KADT(ipporthash, del, ipaddr, port) ++ ++static inline int ++__ipporthash_create(const struct ip_set_req_ipporthash_create *req, ++ struct ip_set_ipporthash *map) ++{ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big, %d elements (max %d)", ++ req->to - req->from + 1, MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ return 0; +} + -+static int retry(struct ip_set *set) ++HASH_CREATE(ipporthash, ip_set_ip_t) ++HASH_DESTROY(ipporthash) ++HASH_FLUSH(ipporthash, ip_set_ip_t) ++ ++static inline void ++__ipporthash_list_header(const struct ip_set_ipporthash *map, ++ struct ip_set_req_ipporthash_create *header) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ ip_set_ip_t *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_ipporthash *tmp; -+ -+ if (map->resize == 0) -+ return -ERANGE; ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++} + -+ again: -+ res = 0; -+ -+ /* Calculate new hash size */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; ++HASH_LIST_HEADER(ipporthash) ++HASH_LIST_MEMBERS_SIZE(ipporthash, ip_set_ip_t) ++HASH_LIST_MEMBERS(ipporthash, ip_set_ip_t) ++ ++IP_SET_RTYPE(ipporthash, IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE) ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipporthash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++REGISTER_MODULE(ipporthash) +diff --git a/net/ipv4/netfilter/ip_set_ipportiphash.c b/net/ipv4/netfilter/ip_set_ipportiphash.c +new file mode 100644 +index 0000000..c80087f +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipportiphash.c +@@ -0,0 +1,215 @@ ++/* Copyright (C) 2008 Jozsef Kadlecsik ++ * ++ * 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+port+ip hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++#define jhash_ip2(map, i, ipport, ip1) \ ++ jhash_2words(ipport, ip1, *(map->initval + i)) ++ ++static inline __u32 ++ipportiphash_id(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) ++{ ++ struct ip_set_ipportiphash *map = set->data; ++ __u32 id; ++ u_int16_t i; ++ struct ipportip *elem; ++ ++ ip = pack_ip_port(map, ip, port); ++ if (!(ip || ip1)) ++ return UINT_MAX; + -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_ipporthash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipporthash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip2(map, i, ip, ip1) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, struct ipportip *, id); ++ if (elem->ip == ip && elem->ip1 == ip1) ++ return id; ++ /* No shortcut - there can be deleted entries. */ + } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; -+ tmp->first_ip = map->first_ip; -+ tmp->last_ip = map->last_ip; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ return UINT_MAX; ++} ++ ++static inline int ++ipportiphash_test(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) ++{ ++ struct ip_set_ipportiphash *map = set->data; + -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_ipporthash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __add_haship(tmp, *elem); ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ return (ipportiphash_id(set, ip, port, ip1) != UINT_MAX); ++} ++ ++#define KADT_CONDITION \ ++ ip_set_ip_t port, ip1; \ ++ \ ++ if (flags[2] == 0) \ ++ return 0; \ ++ \ ++ port = get_port(skb, ++flags); \ ++ ip1 = ipaddr(skb, ++flags); \ ++ \ ++ if (port == INVALID_PORT) \ ++ return 0; ++ ++UADT(ipportiphash, test, req->port, req->ip1) ++KADT(ipportiphash, test, ipaddr, port, ip1) ++ ++static inline int ++__ipportip_add(struct ip_set_ipportiphash *map, ++ ip_set_ip_t ip, ip_set_ip_t ip1) ++{ ++ __u32 probe; ++ u_int16_t i; ++ struct ipportip *elem, *slot = NULL; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip2(map, i, ip, ip1) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, struct ipportip *, probe); ++ if (elem->ip == ip && elem->ip1 == ip1) ++ return -EEXIST; ++ if (!(slot || elem->ip || elem->ip1)) ++ slot = elem; ++ /* There can be deleted entries, must check all slots */ + } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; ++ if (slot) { ++ slot->ip = ip; ++ slot->ip1 = ip1; ++ map->elements++; ++ return 0; + } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__ipportiphash_add(struct ip_set_ipportiphash *map, ++ struct ipportip *elem) ++{ ++ return __ipportip_add(map, elem->ip, elem->ip1); ++} ++ ++static inline int ++ipportiphash_add(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) ++{ ++ struct ip_set_ipportiphash *map = set->data; + -+ /* Success at resizing! */ -+ members = map->members; ++ if (map->elements > limit) ++ return -ERANGE; ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; + -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); ++ ip = pack_ip_port(map, ip, port); ++ if (!(ip || ip1)) ++ return -ERANGE; ++ ++ return __ipportip_add(map, ip, ip1); ++} + -+ harray_free(members); -+ kfree(tmp); ++UADT(ipportiphash, add, req->port, req->ip1) ++KADT(ipportiphash, add, ipaddr, port, ip1) + -+ return 0; ++static inline void ++__ipportiphash_retry(struct ip_set_ipportiphash *tmp, ++ struct ip_set_ipportiphash *map) ++{ ++ tmp->first_ip = map->first_ip; ++ tmp->last_ip = map->last_ip; +} + ++HASH_RETRY2(ipportiphash, struct ipportip) ++ +static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, -+ ip_set_ip_t *hash_ip) ++ipportiphash_del(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ struct ip_set_ipportiphash *map = set->data; + ip_set_ip_t id; -+ ip_set_ip_t *elem; ++ struct ipportip *elem; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + -+ id = hash_id(set, ip, port, hash_ip); ++ id = ipportiphash_id(set, ip, port, ip1); + + if (id == UINT_MAX) + return -EEXIST; + -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ *elem = 0; ++ elem = HARRAY_ELEM(map->members, struct ipportip *, id); ++ elem->ip = elem->ip1 = 0; + map->elements--; + + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_ipporthash *req = -+ (struct ip_set_req_ipporthash *) data; ++UADT(ipportiphash, del, req->port, req->ip1) ++KADT(ipportiphash, del, ipaddr, port, ip1) + -+ if (size != sizeof(struct ip_set_req_ipporthash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash), -+ size); -+ return -EINVAL; ++static inline int ++__ipportiphash_create(const struct ip_set_req_ipportiphash_create *req, ++ struct ip_set_ipportiphash *map) ++{ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big, %d elements (max %d)", ++ req->to - req->from + 1, MAX_RANGE+1); ++ return -ENOEXEC; + } -+ return __delip(set, req->ip, req->port, hash_ip); ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ return 0; +} + -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) ++HASH_CREATE(ipportiphash, struct ipportip) ++HASH_DESTROY(ipportiphash) ++HASH_FLUSH(ipportiphash, struct ipportip) ++ ++static inline void ++__ipportiphash_list_header(const struct ip_set_ipportiphash *map, ++ struct ip_set_req_ipportiphash_create *header) +{ -+ ip_set_ip_t port; ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++} + -+ if (flags[index+1] == 0) -+ return -EINVAL; -+ -+ port = get_port(skb, flags[index+1]); ++HASH_LIST_HEADER(ipportiphash) ++HASH_LIST_MEMBERS_SIZE(ipportiphash, struct ipportip) ++HASH_LIST_MEMBERS_MEMCPY(ipportiphash, struct ipportip, ++ (elem->ip || elem->ip1)) + -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ DP("flag %s port %u", -+ flags[index+1] & IPSET_SRC ? "SRC" : "DST", -+ port); -+ if (port == INVALID_PORT) -+ return -EINVAL; ++IP_SET_RTYPE(ipportiphash, IPSET_TYPE_IP | IPSET_TYPE_PORT ++ | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE) + -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ port, -+ hash_ip); -+} ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipportiphash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + -+static int create(struct ip_set *set, const void *data, size_t size) ++REGISTER_MODULE(ipportiphash) +diff --git a/net/ipv4/netfilter/ip_set_ipportnethash.c b/net/ipv4/netfilter/ip_set_ipportnethash.c +new file mode 100644 +index 0000000..2680cd9 +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipportnethash.c +@@ -0,0 +1,298 @@ ++/* Copyright (C) 2008 Jozsef Kadlecsik ++ * ++ * 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+port+net hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++#define jhash_ip2(map, i, ipport, ip1) \ ++ jhash_2words(ipport, ip1, *(map->initval + i)) ++ ++static inline __u32 ++ipportnethash_id_cidr(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t ip1, uint8_t cidr) +{ -+ struct ip_set_req_ipporthash_create *req = -+ (struct ip_set_req_ipporthash_create *) data; -+ struct ip_set_ipporthash *map; -+ uint16_t i; ++ struct ip_set_ipportnethash *map = set->data; ++ __u32 id; ++ u_int16_t i; ++ struct ipportip *elem; + -+ if (size != sizeof(struct ip_set_req_ipporthash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_ipporthash_create), -+ size); -+ return -EINVAL; ++ ip = pack_ip_port(map, ip, port); ++ ip1 = pack_ip_cidr(ip1, cidr); ++ if (!(ip || ip1)) ++ return UINT_MAX; ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip2(map, i, ip, ip1) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, struct ipportip *, id); ++ if (elem->ip == ip && elem->ip1 == ip1) ++ return id; ++ /* No shortcut - there can be deleted entries. */ + } ++ return UINT_MAX; ++} + -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } ++static inline __u32 ++ipportnethash_id(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) ++{ ++ struct ip_set_ipportnethash *map = set->data; ++ __u32 id = UINT_MAX; ++ int i; + -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ id = ipportnethash_id_cidr(set, ip, port, ip1, map->cidr[i]); ++ if (id != UINT_MAX) ++ break; + } ++ return id; ++} + -+ map = kmalloc(sizeof(struct ip_set_ipporthash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_ipporthash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; -+ map->first_ip = req->from; -+ map->last_ip = req->to; -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } ++static inline int ++ipportnethash_test_cidr(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t ip1, uint8_t cidr) ++{ ++ struct ip_set_ipportnethash *map = set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; + -+ set->data = map; -+ return 0; ++ return (ipportnethash_id_cidr(set, ip, port, ip1, cidr) != UINT_MAX); +} + -+static void destroy(struct ip_set *set) ++static inline int ++ipportnethash_test(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ip_set_ip_t ip1) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); ++ struct ip_set_ipportnethash *map = set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; + -+ set->data = NULL; ++ return (ipportnethash_id(set, ip, port, ip1) != UINT_MAX); +} + -+static void flush(struct ip_set *set) ++static int ++ipportnethash_utest(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ map->elements = 0; ++ const struct ip_set_req_ipportnethash *req = data; ++ ++ if (req->cidr <= 0 || req->cidr > 32) ++ return -EINVAL; ++ return (req->cidr == 32 ++ ? ipportnethash_test(set, req->ip, req->port, req->ip1) ++ : ipportnethash_test_cidr(set, req->ip, req->port, ++ req->ip1, req->cidr)); +} + -+static void list_header(const struct ip_set *set, void *data) ++#define KADT_CONDITION \ ++ ip_set_ip_t port, ip1; \ ++ \ ++ if (flags[2] == 0) \ ++ return 0; \ ++ \ ++ port = get_port(skb, ++flags); \ ++ ip1 = ipaddr(skb, ++flags); \ ++ \ ++ if (port == INVALID_PORT) \ ++ return 0; ++ ++KADT(ipportnethash, test, ipaddr, port, ip1) ++ ++static inline int ++__ipportnet_add(struct ip_set_ipportnethash *map, ++ ip_set_ip_t ip, ip_set_ip_t ip1) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ struct ip_set_req_ipporthash_create *header = -+ (struct ip_set_req_ipporthash_create *) data; ++ __u32 probe; ++ u_int16_t i; ++ struct ipportip *elem, *slot = NULL; + -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; -+ header->from = map->first_ip; -+ header->to = map->last_ip; ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip2(map, i, ip, ip1) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, struct ipportip *, probe); ++ if (elem->ip == ip && elem->ip1 == ip1) ++ return -EEXIST; ++ if (!(slot || elem->ip || elem->ip1)) ++ slot = elem; ++ /* There can be deleted entries, must check all slots */ ++ } ++ if (slot) { ++ slot->ip = ip; ++ slot->ip1 = ip1; ++ map->elements++; ++ return 0; ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; +} + -+static int list_members_size(const struct ip_set *set) ++static inline int ++__ipportnethash_add(struct ip_set_ipportnethash *map, ++ struct ipportip *elem) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ -+ return (map->hashsize * sizeof(ip_set_ip_t)); ++ return __ipportnet_add(map, elem->ip, elem->ip1); +} + -+static void list_members(const struct ip_set *set, void *data) ++static inline int ++ipportnethash_add(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t ip1, uint8_t cidr) +{ -+ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; -+ ip_set_ip_t i, *elem; ++ struct ip_set_ipportnethash *map = set->data; ++ struct ipportip; ++ int ret; ++ ++ if (map->elements > limit) ++ return -ERANGE; ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (cidr <= 0 || cidr >= 32) ++ return -EINVAL; ++ if (map->nets[cidr-1] == UINT16_MAX) ++ return -ERANGE; + -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; ++ ip = pack_ip_port(map, ip, port); ++ ip1 = pack_ip_cidr(ip1, cidr); ++ if (!(ip || ip1)) ++ return -ERANGE; ++ ++ ret =__ipportnet_add(map, ip, ip1); ++ if (ret == 0) { ++ if (!map->nets[cidr-1]++) ++ add_cidr_size(map->cidr, cidr); + } ++ return ret; +} + -+static struct ip_set_type ip_set_ipporthash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_ipporthash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_ipporthash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++#undef KADT_CONDITION ++#define KADT_CONDITION \ ++ struct ip_set_ipportnethash *map = set->data; \ ++ uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31; \ ++ ip_set_ip_t port, ip1; \ ++ \ ++ if (flags[2] == 0) \ ++ return 0; \ ++ \ ++ port = get_port(skb, flags++); \ ++ ip1 = ipaddr(skb, flags++); \ ++ \ ++ if (port == INVALID_PORT) \ ++ return 0; + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("ipporthash type of IP sets"); -+module_param(limit, int, 0600); -+MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++UADT(ipportnethash, add, req->port, req->ip1, req->cidr) ++KADT(ipportnethash, add, ipaddr, port, ip1, cidr) + -+static int __init ip_set_ipporthash_init(void) ++static inline void ++__ipportnethash_retry(struct ip_set_ipportnethash *tmp, ++ struct ip_set_ipportnethash *map) +{ -+ return ip_set_register_set_type(&ip_set_ipporthash); ++ tmp->first_ip = map->first_ip; ++ tmp->last_ip = map->last_ip; ++ memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr)); ++ memcpy(tmp->nets, map->nets, sizeof(tmp->nets)); +} + -+static void __exit ip_set_ipporthash_fini(void) ++HASH_RETRY2(ipportnethash, struct ipportip) ++ ++static inline int ++ipportnethash_del(struct ip_set *set, ++ ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t ip1, uint8_t cidr) +{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_ipporthash); ++ struct ip_set_ipportnethash *map = set->data; ++ ip_set_ip_t id; ++ struct ipportip *elem; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (!ip) ++ return -ERANGE; ++ if (cidr <= 0 || cidr >= 32) ++ return -EINVAL; ++ ++ id = ipportnethash_id_cidr(set, ip, port, ip1, cidr); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, struct ipportip *, id); ++ elem->ip = elem->ip1 = 0; ++ map->elements--; ++ if (!map->nets[cidr-1]--) ++ del_cidr_size(map->cidr, cidr); ++ ++ return 0; +} + -+module_init(ip_set_ipporthash_init); -+module_exit(ip_set_ipporthash_fini); ++UADT(ipportnethash, del, req->port, req->ip1, req->cidr) ++KADT(ipportnethash, del, ipaddr, port, ip1, cidr) ++ ++static inline int ++__ipportnethash_create(const struct ip_set_req_ipportnethash_create *req, ++ struct ip_set_ipportnethash *map) ++{ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big, %d elements (max %d)", ++ req->to - req->from + 1, MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ memset(map->cidr, 0, sizeof(map->cidr)); ++ memset(map->nets, 0, sizeof(map->nets)); ++ return 0; ++} ++ ++HASH_CREATE(ipportnethash, struct ipportip) ++HASH_DESTROY(ipportnethash) ++HASH_FLUSH_CIDR(ipportnethash, struct ipportip); ++ ++static inline void ++__ipportnethash_list_header(const struct ip_set_ipportnethash *map, ++ struct ip_set_req_ipportnethash_create *header) ++{ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++} ++ ++HASH_LIST_HEADER(ipportnethash) ++ ++HASH_LIST_MEMBERS_SIZE(ipportnethash, struct ipportip) ++HASH_LIST_MEMBERS_MEMCPY(ipportnethash, struct ipportip, ++ (elem->ip || elem->ip1)) ++ ++IP_SET_RTYPE(ipportnethash, IPSET_TYPE_IP | IPSET_TYPE_PORT ++ | IPSET_TYPE_IP1 | IPSET_DATA_TRIPLE) ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipportnethash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++REGISTER_MODULE(ipportnethash) diff --git a/net/ipv4/netfilter/ip_set_iptree.c b/net/ipv4/netfilter/ip_set_iptree.c new file mode 100644 -index 0000000..0180534 +index 0000000..77eb180 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_iptree.c -@@ -0,0 +1,612 @@ -+/* Copyright (C) 2005 Jozsef Kadlecsik +@@ -0,0 +1,464 @@ ++/* Copyright (C) 2005-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the iptree type */ + -+#include +#include ++#include +#include ++#include +#include +#include +#include -+#include -+#include +#include +#include +#include +#include ++#include + -+/* Backward compatibility */ -+#ifndef __nocast -+#define __nocast -+#endif -+ ++#include ++#include +#include + +static int limit = MAX_RANGE; + +/* Garbage collection interval in seconds: */ +#define IPTREE_GC_TIME 5*60 -+/* Sleep so many milliseconds before trying again -+ * to delete the gc timer at destroying/flushing a set */ ++/* Sleep so many milliseconds before trying again ++ * to delete the gc timer at destroying/flushing a set */ +#define IPTREE_DESTROY_SLEEP 100 + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+static struct kmem_cache *branch_cachep; -+static struct kmem_cache *leaf_cachep; -+#else -+static kmem_cache_t *branch_cachep; -+static kmem_cache_t *leaf_cachep; -+#endif ++static __KMEM_CACHE_T__ *branch_cachep; ++static __KMEM_CACHE_T__ *leaf_cachep; ++ + +#if defined(__LITTLE_ENDIAN) +#define ABCD(a,b,c,d,addrp) do { \ @@ -4783,9 +4851,9 @@ index 0000000..0180534 +} while (0) + +static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++iptree_test(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; @@ -4794,8 +4862,7 @@ index 0000000..0180534 + if (!ip) + return -ERANGE; + -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); + TESTIP_WALK(map, a, btree); + TESTIP_WALK(btree, b, ctree); @@ -4806,53 +4873,10 @@ index 0000000..0180534 + || time_after(dtree->expires[d], jiffies)); +} + -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; ++#define KADT_CONDITION + -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ return __testip(set, req->ip, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ int res; -+ -+ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", -+ flags[index] & IPSET_SRC ? "SRC" : "DST", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ NIPQUAD(ip_hdr(skb)->saddr), -+ NIPQUAD(ip_hdr(skb)->daddr)); -+#else -+ NIPQUAD(skb->nh.iph->saddr), -+ NIPQUAD(skb->nh.iph->daddr)); -+#endif -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+ return (res < 0 ? 0 : res); -+} ++UADT(iptree, test) ++KADT(iptree, test, ipaddr) + +#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ + if ((map)->tree[elem]) { \ @@ -4870,10 +4894,9 @@ index 0000000..0180534 +} while (0) + +static inline int -+__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, -+ ip_set_ip_t *hash_ip) ++iptree_add(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; @@ -4885,8 +4908,7 @@ index 0000000..0180534 + * but it's probably overkill */ + return -ERANGE; + -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + DP("%u %u %u %u timeout %u", a, b, c, d, timeout); + ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); + ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); @@ -4894,6 +4916,8 @@ index 0000000..0180534 + if (dtree->expires[d] + && (!map->timeout || time_after(dtree->expires[d], jiffies))) + ret = -EEXIST; ++ if (map->timeout && timeout == 0) ++ timeout = map->timeout; + dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; + /* Lottery: I won! */ + if (dtree->expires[d] == 0) @@ -4904,47 +4928,8 @@ index 0000000..0180534 + return ret; +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); -+ return __addip(set, req->ip, -+ req->timeout ? req->timeout : map->timeout, -+ hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ -+ return __addip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ map->timeout, -+ hash_ip); -+} ++UADT(iptree, add, req->timeout) ++KADT(iptree, add, ipaddr, 0) + +#define DELIP_WALK(map, elem, branch) do { \ + if ((map)->tree[elem]) { \ @@ -4953,10 +4938,10 @@ index 0000000..0180534 + return -EEXIST; \ +} while (0) + -+static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++static inline int ++iptree_del(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; @@ -4965,8 +4950,7 @@ index 0000000..0180534 + if (!ip) + return -ERANGE; + -+ *hash_ip = ip; -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + DELIP_WALK(map, a, btree); + DELIP_WALK(btree, b, ctree); + DELIP_WALK(ctree, c, dtree); @@ -4979,40 +4963,8 @@ index 0000000..0180534 + return -EEXIST; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptree *req = -+ (struct ip_set_req_iptree *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptree)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_iptree), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT(iptree, del) ++KADT(iptree, del, ipaddr) + +#define LOOP_WALK_BEGIN(map, i, branch) \ + for (i = 0; i < 256; i++) { \ @@ -5022,10 +4974,11 @@ index 0000000..0180534 + +#define LOOP_WALK_END } + -+static void ip_tree_gc(unsigned long ul_set) ++static void ++ip_tree_gc(unsigned long ul_set) +{ -+ struct ip_set *set = (void *) ul_set; -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set *set = (struct ip_set *) ul_set; ++ struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; @@ -5092,9 +5045,10 @@ index 0000000..0180534 + add_timer(&map->gc); +} + -+static inline void init_gc_timer(struct ip_set *set) ++static inline void ++init_gc_timer(struct ip_set *set) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + + /* Even if there is no timeout for the entries, + * we still have to call gc because delete @@ -5107,22 +5061,22 @@ index 0000000..0180534 + add_timer(&map->gc); +} + -+static int create(struct ip_set *set, const void *data, size_t size) ++static int ++iptree_create(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_req_iptree_create *req = -+ (struct ip_set_req_iptree_create *) data; ++ const struct ip_set_req_iptree_create *req = data; + struct ip_set_iptree *map; + + if (size != sizeof(struct ip_set_req_iptree_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", ++ ip_set_printk("data length wrong (want %zu, have %lu)", + sizeof(struct ip_set_req_iptree_create), -+ size); ++ (unsigned long)size); + return -EINVAL; + } + + map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); + if (!map) { -+ DP("out of memory for %d bytes", ++ DP("out of memory for %zu bytes", + sizeof(struct ip_set_iptree)); + return -ENOMEM; + } @@ -5136,7 +5090,8 @@ index 0000000..0180534 + return 0; +} + -+static void __flush(struct ip_set_iptree *map) ++static inline void ++__flush(struct ip_set_iptree *map) +{ + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; @@ -5155,9 +5110,10 @@ index 0000000..0180534 + map->elements = 0; +} + -+static void destroy(struct ip_set *set) ++static void ++iptree_destroy(struct ip_set *set) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + + /* gc might be running */ + while (!del_timer(&map->gc)) @@ -5167,9 +5123,10 @@ index 0000000..0180534 + set->data = NULL; +} + -+static void flush(struct ip_set *set) ++static void ++iptree_flush(struct ip_set *set) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptree *map = set->data; + unsigned int timeout = map->timeout; + + /* gc might be running */ @@ -5182,18 +5139,19 @@ index 0000000..0180534 + init_gc_timer(set); +} + -+static void list_header(const struct ip_set *set, void *data) ++static void ++iptree_list_header(const struct ip_set *set, void *data) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; -+ struct ip_set_req_iptree_create *header = -+ (struct ip_set_req_iptree_create *) data; ++ const struct ip_set_iptree *map = set->data; ++ struct ip_set_req_iptree_create *header = data; + + header->timeout = map->timeout; +} + -+static int list_members_size(const struct ip_set *set) ++static int ++iptree_list_members_size(const struct ip_set *set, char dont_align) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ const struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; @@ -5213,30 +5171,32 @@ index 0000000..0180534 + LOOP_WALK_END; + + DP("members %u", count); -+ return (count * sizeof(struct ip_set_req_iptree)); ++ return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align)); +} + -+static void list_members(const struct ip_set *set, void *data) ++static void ++iptree_list_members(const struct ip_set *set, void *data, char dont_align) +{ -+ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ const struct ip_set_iptree *map = set->data; + struct ip_set_iptreeb *btree; + struct ip_set_iptreec *ctree; + struct ip_set_iptreed *dtree; + unsigned int a,b,c,d; -+ size_t offset = 0; ++ size_t offset = 0, datasize; + struct ip_set_req_iptree *entry; + ++ datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptree), dont_align); + LOOP_WALK_BEGIN(map, a, btree); + LOOP_WALK_BEGIN(btree, b, ctree); + LOOP_WALK_BEGIN(ctree, c, dtree); + for (d = 0; d < 256; d++) { + if (dtree->expires[d] + && (!map->timeout || time_after(dtree->expires[d], jiffies))) { -+ entry = (struct ip_set_req_iptree *)(data + offset); ++ entry = data + offset; + entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); -+ entry->timeout = !map->timeout ? 0 ++ entry->timeout = !map->timeout ? 0 + : (dtree->expires[d] - jiffies)/HZ; -+ offset += sizeof(struct ip_set_req_iptree); ++ offset += datasize; + } + } + LOOP_WALK_END; @@ -5244,26 +5204,7 @@ index 0000000..0180534 + LOOP_WALK_END; +} + -+static struct ip_set_type ip_set_iptree = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_iptree), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iptree_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_TYPE(iptree, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); @@ -5275,29 +5216,15 @@ index 0000000..0180534 +{ + int ret; + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ branch_cachep = kmem_cache_create("ip_set_iptreeb", -+ sizeof(struct ip_set_iptreeb), -+ 0, 0, NULL); -+#else -+ branch_cachep = kmem_cache_create("ip_set_iptreeb", -+ sizeof(struct ip_set_iptreeb), -+ 0, 0, NULL, NULL); -+#endif ++ branch_cachep = KMEM_CACHE_CREATE("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb)); + if (!branch_cachep) { + printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); + ret = -ENOMEM; + goto out; + } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ leaf_cachep = kmem_cache_create("ip_set_iptreed", -+ sizeof(struct ip_set_iptreed), -+ 0, 0, NULL); -+#else -+ leaf_cachep = kmem_cache_create("ip_set_iptreed", -+ sizeof(struct ip_set_iptreed), -+ 0, 0, NULL, NULL); -+#endif ++ leaf_cachep = KMEM_CACHE_CREATE("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed)); + if (!leaf_cachep) { + printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); + ret = -ENOMEM; @@ -5326,10 +5253,10 @@ index 0000000..0180534 +module_exit(ip_set_iptree_fini); diff --git a/net/ipv4/netfilter/ip_set_iptreemap.c b/net/ipv4/netfilter/ip_set_iptreemap.c new file mode 100644 -index 0000000..3825055 +index 0000000..76d0142 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_iptreemap.c -@@ -0,0 +1,829 @@ +@@ -0,0 +1,699 @@ +/* Copyright (C) 2007 Sven Wegener + * + * This program is free software; you can redistribute it and/or modify it @@ -5338,38 +5265,34 @@ index 0000000..3825055 + */ + +/* This modules implements the iptreemap ipset type. It uses bitmaps to -+ * represent every single IPv4 address as a single bit. The bitmaps are managed -+ * in a tree structure, where the first three octets of an addresses are used -+ * as an index to find the bitmap and the last octet is used as the bit number. ++ * represent every single IPv4 address as a bit. The bitmaps are managed in a ++ * tree structure, where the first three octets of an address are used as an ++ * index to find the bitmap and the last octet is used as the bit number. + */ + -+#include ++#include +#include +#include ++#include +#include +#include +#include -+#include -+#include +#include +#include +#include +#include ++#include + ++#include ++#include +#include + +#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60) +#define IPTREEMAP_DESTROY_SLEEP (100) + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+static struct kmem_cache *cachep_b; -+static struct kmem_cache *cachep_c; -+static struct kmem_cache *cachep_d; -+#else -+static kmem_cache_t *cachep_b; -+static kmem_cache_t *cachep_c; -+static kmem_cache_t *cachep_d; -+#endif ++static __KMEM_CACHE_T__ *cachep_b; ++static __KMEM_CACHE_T__ *cachep_c; ++static __KMEM_CACHE_T__ *cachep_d; + +static struct ip_set_iptreemap_d *fullbitmap_d; +static struct ip_set_iptreemap_c *fullbitmap_c; @@ -5438,13 +5361,13 @@ index 0000000..3825055 + } \ + } + -+#define DELIP_WALK(map, elem, branch, cachep, full, flags) \ ++#define DELIP_WALK(map, elem, branch, cachep, full) \ + do { \ + branch = (map)->tree[elem]; \ + if (!branch) { \ + return -EEXIST; \ + } else if (branch == full) { \ -+ branch = kmem_cache_alloc(cachep, flags); \ ++ branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ + if (!branch) \ + return -ENOMEM; \ + memcpy(branch, full, sizeof(*full)); \ @@ -5452,7 +5375,7 @@ index 0000000..3825055 + } \ + } while (0) + -+#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \ ++#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \ + for (a = a1; a <= a2; a++) { \ + branch = (map)->tree[a]; \ + if (branch) { \ @@ -5462,7 +5385,7 @@ index 0000000..3825055 + (map)->tree[a] = NULL; \ + continue; \ + } else if (branch == full) { \ -+ branch = kmem_cache_alloc(cachep, flags); \ ++ branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ + if (!branch) \ + return -ENOMEM; \ + memcpy(branch, full, sizeof(*branch)); \ @@ -5518,9 +5441,6 @@ index 0000000..3825055 +#define LOOP_WALK_END_COUNT() \ + } + -+#define MIN(a, b) (a < b ? a : b) -+#define MAX(a, b) (a > b ? a : b) -+ +#define GETVALUE1(a, a1, b1, r) \ + (a == a1 ? b1 : r) + @@ -5590,17 +5510,15 @@ index 0000000..3825055 +} + +static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++iptreemap_test(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; + unsigned char a, b, c, d; + -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + + TESTIP_WALK(map, a, btree, fullbitmap_b); + TESTIP_WALK(btree, b, ctree, fullbitmap_c); @@ -5609,40 +5527,13 @@ index 0000000..3825055 + return !!test_bit(d, (void *) dtree->bitmap); +} + -+static int -+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __testip(set, req->start, hash_ip); -+} -+ -+static int -+testip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ int res; -+ -+ res = __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); ++#define KADT_CONDITION + -+ return (res < 0 ? 0 : res); -+} ++UADT(iptreemap, test) ++KADT(iptreemap, test, ipaddr) + +static inline int -+__addip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++__addip_single(struct ip_set *set, ip_set_ip_t ip) +{ + struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; + struct ip_set_iptreemap_b *btree; @@ -5650,26 +5541,24 @@ index 0000000..3825055 + struct ip_set_iptreemap_d *dtree; + unsigned char a, b, c, d; + -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + + ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b); + ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c); + ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d); + -+ if (test_and_set_bit(d, (void *) dtree->bitmap)) ++ if (__test_and_set_bit(d, (void *) dtree->bitmap)) + return -EEXIST; + -+ set_bit(b, (void *) btree->dirty); ++ __set_bit(b, (void *) btree->dirty); + + return 0; +} + +static inline int -+__addip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip) ++iptreemap_add(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; @@ -5678,9 +5567,7 @@ index 0000000..3825055 + unsigned char a2, b2, c2, d2; + + if (start == end) -+ return __addip_single(set, start, hash_ip); -+ -+ *hash_ip = start; ++ return __addip_single(set, start); + + ABCD(a1, b1, c1, d1, &start); + ABCD(a2, b2, c2, d2, &end); @@ -5690,8 +5577,8 @@ index 0000000..3825055 + ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { + ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { + for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) -+ set_bit(d, (void *) dtree->bitmap); -+ set_bit(b, (void *) btree->dirty); ++ __set_bit(d, (void *) dtree->bitmap); ++ __set_bit(b, (void *) btree->dirty); + } ADDIP_RANGE_LOOP_END(); + } ADDIP_RANGE_LOOP_END(); + } ADDIP_RANGE_LOOP_END(); @@ -5699,64 +5586,36 @@ index 0000000..3825055 + return 0; +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __addip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ -+ return __addip_single(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++UADT0(iptreemap, add, min(req->ip, req->end), max(req->ip, req->end)) ++KADT(iptreemap, add, ipaddr, ip) + +static inline int -+__delip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++__delip_single(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; + unsigned char a,b,c,d; + -+ *hash_ip = ip; -+ -+ ABCD(a, b, c, d, hash_ip); ++ ABCD(a, b, c, d, &ip); + -+ DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags); -+ DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags); -+ DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags); ++ DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b); ++ DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c); ++ DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d); + -+ if (!test_and_clear_bit(d, (void *) dtree->bitmap)) ++ if (!__test_and_clear_bit(d, (void *) dtree->bitmap)) + return -EEXIST; + -+ set_bit(b, (void *) btree->dirty); ++ __set_bit(b, (void *) btree->dirty); + + return 0; +} + +static inline int -+__delip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++iptreemap_del(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; @@ -5765,20 +5624,18 @@ index 0000000..3825055 + unsigned char a2, b2, c2, d2; + + if (start == end) -+ return __delip_single(set, start, hash_ip, flags); -+ -+ *hash_ip = start; ++ return __delip_single(set, start); + + ABCD(a1, b1, c1, d1, &start); + ABCD(a2, b2, c2, d2, &end); + + /* This is sooo ugly... */ -+ DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) { -+ DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) { -+ DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) { ++ DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) { ++ DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { ++ DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { + for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) -+ clear_bit(d, (void *) dtree->bitmap); -+ set_bit(b, (void *) btree->dirty); ++ __clear_bit(d, (void *) dtree->bitmap); ++ __set_bit(b, (void *) btree->dirty); + } DELIP_RANGE_LOOP_END(); + } DELIP_RANGE_LOOP_END(); + } DELIP_RANGE_LOOP_END(); @@ -5786,34 +5643,8 @@ index 0000000..3825055 + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; -+ -+ if (size != sizeof(struct ip_set_req_iptreemap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); -+ return -EINVAL; -+ } -+ -+ return __delip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip, GFP_KERNEL); -+} -+ -+static int -+delip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) -+{ -+ return __delip_single(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip, -+ GFP_ATOMIC); -+} ++UADT0(iptreemap, del, min(req->ip, req->end), max(req->ip, req->end)) ++KADT(iptreemap, del, ipaddr, ip) + +/* Check the status of the bitmap + * -1 == all bits cleared @@ -5837,7 +5668,7 @@ index 0000000..3825055 +gc(unsigned long addr) +{ + struct ip_set *set = (struct ip_set *) addr; -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; @@ -5848,7 +5679,7 @@ index 0000000..3825055 + + LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) { + LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) { -+ if (!test_and_clear_bit(b, (void *) btree->dirty)) ++ if (!__test_and_clear_bit(b, (void *) btree->dirty)) + continue; + LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) { + switch (bitmap_status(dtree)) { @@ -5876,7 +5707,7 @@ index 0000000..3825055 +static inline void +init_gc_timer(struct ip_set *set) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + + init_timer(&map->gc); + map->gc.data = (unsigned long) set; @@ -5885,16 +5716,12 @@ index 0000000..3825055 + add_timer(&map->gc); +} + -+static int create(struct ip_set *set, const void *data, size_t size) ++static int ++iptreemap_create(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_req_iptreemap_create *req = (struct ip_set_req_iptreemap_create *) data; ++ const struct ip_set_req_iptreemap_create *req = data; + struct ip_set_iptreemap *map; + -+ if (size != sizeof(struct ip_set_req_iptreemap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap_create), size); -+ return -EINVAL; -+ } -+ + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; @@ -5907,7 +5734,8 @@ index 0000000..3825055 + return 0; +} + -+static inline void __flush(struct ip_set_iptreemap *map) ++static inline void ++__flush(struct ip_set_iptreemap *map) +{ + struct ip_set_iptreemap_b *btree; + unsigned int a; @@ -5918,9 +5746,10 @@ index 0000000..3825055 + LOOP_WALK_END(); +} + -+static void destroy(struct ip_set *set) ++static void ++iptreemap_destroy(struct ip_set *set) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + + while (!del_timer(&map->gc)) + msleep(IPTREEMAP_DESTROY_SLEEP); @@ -5931,9 +5760,11 @@ index 0000000..3825055 + set->data = NULL; +} + -+static void flush(struct ip_set *set) ++static void ++iptreemap_flush(struct ip_set *set) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; ++ unsigned int gc_interval = map->gc_interval; + + while (!del_timer(&map->gc)) + msleep(IPTREEMAP_DESTROY_SLEEP); @@ -5941,21 +5772,24 @@ index 0000000..3825055 + __flush(map); + + memset(map, 0, sizeof(*map)); ++ map->gc_interval = gc_interval; + + init_gc_timer(set); +} + -+static void list_header(const struct ip_set *set, void *data) ++static void ++iptreemap_list_header(const struct ip_set *set, void *data) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; -+ struct ip_set_req_iptreemap_create *header = (struct ip_set_req_iptreemap_create *) data; ++ struct ip_set_iptreemap *map = set->data; ++ struct ip_set_req_iptreemap_create *header = data; + + header->gc_interval = map->gc_interval; +} + -+static int list_members_size(const struct ip_set *set) ++static int ++iptreemap_list_members_size(const struct ip_set *set, char dont_align) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; @@ -5979,29 +5813,30 @@ index 0000000..3825055 + if (inrange) + count++; + -+ return (count * sizeof(struct ip_set_req_iptreemap)); ++ return (count * IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align)); +} + -+static inline size_t add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) ++static inline void ++add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) +{ -+ struct ip_set_req_iptreemap *entry = (struct ip_set_req_iptreemap *) (data + offset); ++ struct ip_set_req_iptreemap *entry = data + offset; + -+ entry->start = start; ++ entry->ip = start; + entry->end = end; -+ -+ return sizeof(*entry); +} + -+static void list_members(const struct ip_set *set, void *data) ++static void ++iptreemap_list_members(const struct ip_set *set, void *data, char dont_align) +{ -+ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap *map = set->data; + struct ip_set_iptreemap_b *btree; + struct ip_set_iptreemap_c *ctree; + struct ip_set_iptreemap_d *dtree; + unsigned int a, b, c, d, inrange = 0; -+ size_t offset = 0; ++ size_t offset = 0, datasize; + ip_set_ip_t start = 0, end = 0, ip; + ++ datasize = IPSET_VALIGN(sizeof(struct ip_set_req_iptreemap), dont_align); + LOOP_WALK_BEGIN(map, a, btree) { + LOOP_WALK_BEGIN(btree, b, ctree) { + LOOP_WALK_BEGIN(ctree, c, dtree) { @@ -6012,12 +5847,14 @@ index 0000000..3825055 + inrange = 1; + start = ip; + } else if (end < ip - 1) { -+ offset += add_member(data, offset, start, end); ++ add_member(data, offset, start, end); ++ offset += datasize; + start = ip; + } + end = ip; + } else if (inrange) { -+ offset += add_member(data, offset, start, end); ++ add_member(data, offset, start, end); ++ offset += datasize; + inrange = 0; + } + } @@ -6029,26 +5866,7 @@ index 0000000..3825055 + add_member(data, offset, start, end); +} + -+static struct ip_set_type ip_set_iptreemap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = create, -+ .destroy = destroy, -+ .flush = flush, -+ .reqsize = sizeof(struct ip_set_req_iptreemap), -+ .addip = addip, -+ .addip_kernel = addip_kernel, -+ .delip = delip, -+ .delip_kernel = delip_kernel, -+ .testip = testip, -+ .testip_kernel = testip_kernel, -+ .header_size = sizeof(struct ip_set_req_iptreemap_create), -+ .list_header = list_header, -+ .list_members_size = list_members_size, -+ .list_members = list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_TYPE(iptreemap, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sven Wegener "); @@ -6059,43 +5877,22 @@ index 0000000..3825055 + int ret = -ENOMEM; + int a; + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_b = kmem_cache_create("ip_set_iptreemap_b", -+ sizeof(struct ip_set_iptreemap_b), -+ 0, 0, NULL); -+#else -+ cachep_b = kmem_cache_create("ip_set_iptreemap_b", -+ sizeof(struct ip_set_iptreemap_b), -+ 0, 0, NULL, NULL); -+#endif ++ cachep_b = KMEM_CACHE_CREATE("ip_set_iptreemap_b", ++ sizeof(struct ip_set_iptreemap_b)); + if (!cachep_b) { + ip_set_printk("Unable to create ip_set_iptreemap_b slab cache"); + goto out; + } + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_c = kmem_cache_create("ip_set_iptreemap_c", -+ sizeof(struct ip_set_iptreemap_c), -+ 0, 0, NULL); -+#else -+ cachep_c = kmem_cache_create("ip_set_iptreemap_c", -+ sizeof(struct ip_set_iptreemap_c), -+ 0, 0, NULL, NULL); -+#endif ++ cachep_c = KMEM_CACHE_CREATE("ip_set_iptreemap_c", ++ sizeof(struct ip_set_iptreemap_c)); + if (!cachep_c) { + ip_set_printk("Unable to create ip_set_iptreemap_c slab cache"); + goto outb; + } + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ cachep_d = kmem_cache_create("ip_set_iptreemap_d", -+ sizeof(struct ip_set_iptreemap_d), -+ 0, 0, NULL); -+#else -+ cachep_d = kmem_cache_create("ip_set_iptreemap_d", -+ sizeof(struct ip_set_iptreemap_d), -+ 0, 0, NULL, NULL); -+#endif ++ cachep_d = KMEM_CACHE_CREATE("ip_set_iptreemap_d", ++ sizeof(struct ip_set_iptreemap_d)); + if (!cachep_d) { + ip_set_printk("Unable to create ip_set_iptreemap_d slab cache"); + goto outc; @@ -6161,18 +5958,18 @@ index 0000000..3825055 +module_exit(ip_set_iptreemap_fini); diff --git a/net/ipv4/netfilter/ip_set_macipmap.c b/net/ipv4/netfilter/ip_set_macipmap.c new file mode 100644 -index 0000000..8ca2159 +index 0000000..89e907b --- /dev/null +++ b/net/ipv4/netfilter/ip_set_macipmap.c -@@ -0,0 +1,375 @@ +@@ -0,0 +1,179 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson -+ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the macipmap type */ @@ -6180,41 +5977,26 @@ index 0000000..8ca2159 +#include +#include +#include -+#include -+#include -+#include +#include +#include +#include +#include +#include -+#include + -+#include +#include + +static int -+testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++macipmap_utest(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = (struct ip_set_macip *) map->members; -+ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } ++ const struct ip_set_macipmap *map = set->data; ++ const struct ip_set_macip *table = map->members; ++ const struct ip_set_req_macipmap *req = data; + + if (req->ip < map->first_ip || req->ip > map->last_ip) + return -ERANGE; + -+ *hash_ip = req->ip; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); -+ if (test_bit(IPSET_MACIP_ISSET, -+ (void *) &table[req->ip - map->first_ip].flags)) { ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(req->ip)); ++ if (table[req->ip - map->first_ip].match) { + return (memcmp(req->ethernet, + &table[req->ip - map->first_ip].ethernet, + ETH_ALEN) == 0); @@ -6224,44 +6006,25 @@ index 0000000..8ca2159 +} + +static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; ++macipmap_ktest(struct ip_set *set, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ const struct ip_set_macipmap *map = set->data; ++ const struct ip_set_macip *table = map->members; + ip_set_ip_t ip; + -+ ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif ++ ip = ipaddr(skb, flags); + + if (ip < map->first_ip || ip > map->last_ip) + return 0; + -+ *hash_ip = ip; -+ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", -+ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); -+ if (test_bit(IPSET_MACIP_ISSET, -+ (void *) &table[ip - map->first_ip].flags)) { ++ DP("set: %s, ip:%u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (table[ip - map->first_ip].match) { + /* Is mac pointer valid? + * If so, compare... */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + return (skb_mac_header(skb) >= skb->head + && (skb_mac_header(skb) + ETH_HLEN) <= skb->data -+#else -+ return (skb->mac.raw >= skb->head -+ && (skb->mac.raw + ETH_HLEN) <= skb->data -+#endif + && (memcmp(eth_hdr(skb)->h_source, + &table[ip - map->first_ip].ethernet, + ETH_ALEN) == 0)); @@ -6272,347 +6035,177 @@ index 0000000..8ca2159 + +/* returns 0 on success */ +static inline int -+__addip(struct ip_set *set, -+ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) ++macipmap_add(struct ip_set *set, ++ ip_set_ip_t ip, const unsigned char *ethernet) +{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; ++ struct ip_set_macipmap *map = set->data; ++ struct ip_set_macip *table = map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; -+ if (test_and_set_bit(IPSET_MACIP_ISSET, -+ (void *) &table[ip - map->first_ip].flags)) ++ if (table[ip - map->first_ip].match) + return -EEXIST; + -+ *hash_ip = ip; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ DP("set: %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); + memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); ++ table[ip - map->first_ip].match = IPSET_MACIP_ISSET; + return 0; +} + -+static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_macipmap *req = -+ (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } -+ return __addip(set, req->ip, req->ethernet, hash_ip); -+} -+ -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ ip_set_ip_t ip; -+ -+ ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (!(skb_mac_header(skb) >= skb->head -+ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data)) -+#else -+ if (!(skb->mac.raw >= skb->head -+ && (skb->mac.raw + ETH_HLEN) <= skb->data)) -+#endif ++#define KADT_CONDITION \ ++ if (!(skb_mac_header(skb) >= skb->head \ ++ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data))\ + return -EINVAL; + -+ return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); -+} ++UADT(macipmap, add, req->ethernet) ++KADT(macipmap, add, ipaddr, eth_hdr(skb)->h_source) + +static inline int -+__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++macipmap_del(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_macip *table = -+ (struct ip_set_macip *) map->members; ++ struct ip_set_macipmap *map = set->data; ++ struct ip_set_macip *table = map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; -+ if (!test_and_clear_bit(IPSET_MACIP_ISSET, -+ (void *)&table[ip - map->first_ip].flags)) ++ if (!table[ip - map->first_ip].match) + return -EEXIST; + -+ *hash_ip = ip; -+ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ table[ip - map->first_ip].match = 0; ++ DP("set: %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_macipmap *req = -+ (struct ip_set_req_macipmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_macipmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap), -+ size); -+ return -EINVAL; -+ } -+ return __delip(set, req->ip, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __delip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} ++#undef KADT_CONDITION ++#define KADT_CONDITION + -+static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) -+{ -+ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); -+} ++UADT(macipmap, del) ++KADT(macipmap, del, ipaddr) + -+static int create(struct ip_set *set, const void *data, size_t size) ++static inline int ++__macipmap_create(const struct ip_set_req_macipmap_create *req, ++ struct ip_set_macipmap *map) +{ -+ int newbytes; -+ struct ip_set_req_macipmap_create *req = -+ (struct ip_set_req_macipmap_create *) data; -+ struct ip_set_macipmap *map; -+ -+ if (size != sizeof(struct ip_set_req_macipmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_macipmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u.%u.%u.%u to %u.%u.%u.%u", -+ HIPQUAD(req->from), HIPQUAD(req->to)); -+ -+ if (req->from > req->to) { -+ DP("bad ip range"); -+ return -ENOEXEC; -+ } -+ + if (req->to - req->from > MAX_RANGE) { -+ ip_set_printk("range too big (max %d addresses)", -+ MAX_RANGE+1); ++ ip_set_printk("range too big, %d elements (max %d)", ++ req->to - req->from + 1, MAX_RANGE+1); + return -ENOEXEC; + } -+ -+ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_macipmap)); -+ return -ENOMEM; -+ } + map->flags = req->flags; -+ map->first_ip = req->from; -+ map->last_ip = req->to; -+ newbytes = members_size(map->first_ip, map->last_ip); -+ map->members = ip_set_malloc(newbytes); -+ DP("members: %u %p", newbytes, map->members); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); -+ return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ -+ set->data = map; -+ return 0; -+} -+ -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ -+ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); -+ kfree(map); -+ -+ set->data = NULL; ++ return (req->to - req->from + 1) * sizeof(struct ip_set_macip); +} + -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); -+} ++BITMAP_CREATE(macipmap) ++BITMAP_DESTROY(macipmap) ++BITMAP_FLUSH(macipmap) + -+static void list_header(const struct ip_set *set, void *data) ++static inline void ++__macipmap_list_header(const struct ip_set_macipmap *map, ++ struct ip_set_req_macipmap_create *header) +{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ struct ip_set_req_macipmap_create *header = -+ (struct ip_set_req_macipmap_create *) data; -+ -+ DP("list_header %x %x %u", map->first_ip, map->last_ip, -+ map->flags); -+ -+ header->from = map->first_ip; -+ header->to = map->last_ip; + header->flags = map->flags; +} + -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; ++BITMAP_LIST_HEADER(macipmap) ++BITMAP_LIST_MEMBERS_SIZE(macipmap, struct ip_set_req_macipmap, ++ (map->last_ip - map->first_ip + 1), ++ ((const struct ip_set_macip *)map->members)[i].match) + -+ DP("%u", members_size(map->first_ip, map->last_ip)); -+ return members_size(map->first_ip, map->last_ip); -+} + -+static void list_members(const struct ip_set *set, void *data) ++static void ++macipmap_list_members(const struct ip_set *set, void *data, char dont_align) +{ -+ struct ip_set_macipmap *map = -+ (struct ip_set_macipmap *) set->data; -+ -+ int bytes = members_size(map->first_ip, map->last_ip); -+ -+ DP("members: %u %p", bytes, map->members); -+ memcpy(data, map->members, bytes); ++ const struct ip_set_macipmap *map = set->data; ++ const struct ip_set_macip *table = map->members; ++ uint32_t i, n = 0; ++ struct ip_set_req_macipmap *d; ++ ++ if (dont_align) { ++ memcpy(data, map->members, map->size); ++ return; ++ } ++ ++ for (i = 0; i < map->last_ip - map->first_ip + 1; i++) ++ if (table[i].match) { ++ d = data + n * IPSET_ALIGN(sizeof(struct ip_set_req_macipmap)); ++ d->ip = map->first_ip + i; ++ memcpy(d->ethernet, &table[i].ethernet, ETH_ALEN); ++ n++; ++ } +} + -+static struct ip_set_type ip_set_macipmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_macipmap), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_macipmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_TYPE(macipmap, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("macipmap type of IP sets"); + -+static int __init ip_set_macipmap_init(void) -+{ -+ init_max_malloc_size(); -+ return ip_set_register_set_type(&ip_set_macipmap); -+} -+ -+static void __exit ip_set_macipmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_macipmap); -+} -+ -+module_init(ip_set_macipmap_init); -+module_exit(ip_set_macipmap_fini); ++REGISTER_MODULE(macipmap) diff --git a/net/ipv4/netfilter/ip_set_nethash.c b/net/ipv4/netfilter/ip_set_nethash.c new file mode 100644 -index 0000000..d94cc32 +index 0000000..e3b09e0 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_nethash.c -@@ -0,0 +1,497 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik +@@ -0,0 +1,218 @@ ++/* Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing a cidr nethash set */ + +#include ++#include +#include +#include -+#include -+#include -+#include -+#include ++#include +#include +#include +#include +#include -+#include +#include + +#include + -+#include +#include + +static int limit = MAX_RANGE; + +static inline __u32 -+jhash_ip(const struct ip_set_nethash *map, uint16_t i, ip_set_ip_t ip) -+{ -+ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); -+} -+ -+static inline __u32 -+hash_id_cidr(struct ip_set_nethash *map, -+ ip_set_ip_t ip, -+ unsigned char cidr, -+ ip_set_ip_t *hash_ip) ++nethash_id_cidr(const struct ip_set_nethash *map, ++ ip_set_ip_t ip, ++ uint8_t cidr) +{ + __u32 id; + u_int16_t i; + ip_set_ip_t *elem; + -+ *hash_ip = pack(ip, cidr); ++ ip = pack_ip_cidr(ip, cidr); ++ if (!ip) ++ return MAX_RANGE; + + for (i = 0; i < map->probes; i++) { -+ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ id = jhash_ip(map, i, ip) % map->hashsize; + DP("hash key: %u", id); + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); -+ if (*elem == *hash_ip) ++ if (*elem == ip) + return id; ++ /* No shortcut - there can be deleted entries. */ + } + return UINT_MAX; +} + +static inline __u32 -+hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++nethash_id(struct ip_set *set, ip_set_ip_t ip) +{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ const struct ip_set_nethash *map = set->data; + __u32 id = UINT_MAX; + int i; + + for (i = 0; i < 30 && map->cidr[i]; i++) { -+ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); ++ id = nethash_id_cidr(map, ip, map->cidr[i]); + if (id != UINT_MAX) + break; + } @@ -6620,409 +6213,150 @@ index 0000000..d94cc32 +} + +static inline int -+__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ -+ return (ip && hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); -+} -+ -+static inline int -+__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) -+{ -+ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); -+} -+ -+static int -+testip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); -+ return -EINVAL; -+ } -+ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) -+ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); -+} -+ -+static int -+testip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ return __testip(set, -+ ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr), -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr), -+#endif -+ hash_ip); -+} -+ -+static inline int -+__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) -+{ -+ __u32 probe; -+ u_int16_t i; -+ ip_set_ip_t *elem; -+ -+ for (i = 0; i < map->probes; i++) { -+ probe = jhash_ip(map, i, ip) % map->hashsize; -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); -+ if (*elem == ip) -+ return -EEXIST; -+ if (!*elem) { -+ *elem = ip; -+ map->elements++; -+ return 0; -+ } -+ } -+ /* Trigger rehashing */ -+ return -EAGAIN; -+} -+ -+static inline int -+__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) ++nethash_test_cidr(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +{ -+ if (!ip || map->elements >= limit) -+ return -ERANGE; -+ -+ *hash_ip = pack(ip, cidr); -+ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); -+ -+ return __addip_base(map, *hash_ip); -+} ++ const struct ip_set_nethash *map = set->data; + -+static void -+update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) -+{ -+ unsigned char next; -+ int i; -+ -+ for (i = 0; i < 30 && map->cidr[i]; i++) { -+ if (map->cidr[i] == cidr) { -+ return; -+ } else if (map->cidr[i] < cidr) { -+ next = map->cidr[i]; -+ map->cidr[i] = cidr; -+ cidr = next; -+ } -+ } -+ if (i < 30) -+ map->cidr[i] = cidr; ++ return (nethash_id_cidr(map, ip, cidr) != UINT_MAX); ++} ++ ++static inline int ++nethash_test(struct ip_set *set, ip_set_ip_t ip) ++{ ++ return (nethash_id(set, ip) != UINT_MAX); +} + +static int -+addip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) ++nethash_utest(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ int ret; ++ const struct ip_set_req_nethash *req = data; + -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); ++ if (req->cidr <= 0 || req->cidr > 32) + return -EINVAL; -+ } -+ ret = __addip((struct ip_set_nethash *) set->data, -+ req->ip, req->cidr, hash_ip); -+ -+ if (ret == 0) -+ update_cidr_sizes((struct ip_set_nethash *) set->data, -+ req->cidr); -+ -+ return ret; ++ return (req->cidr == 32 ? nethash_test(set, req->ip) ++ : nethash_test_cidr(set, req->ip, req->cidr)); +} + -+static int -+addip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ int ret = -ERANGE; -+ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif ++#define KADT_CONDITION ++ ++KADT(nethash, test, ipaddr) ++ ++static inline int ++__nethash_add(struct ip_set_nethash *map, ip_set_ip_t *ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem, *slot = NULL; + -+ if (map->cidr[0]) -+ ret = __addip(map, ip, map->cidr[0], hash_ip); -+ -+ return ret; ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, *ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == *ip) ++ return -EEXIST; ++ if (!(slot || *elem)) ++ slot = elem; ++ /* There can be deleted entries, must check all slots */ ++ } ++ if (slot) { ++ *slot = *ip; ++ map->elements++; ++ return 0; ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; +} + -+static int retry(struct ip_set *set) ++static inline int ++nethash_add(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ ip_set_ip_t *elem; -+ void *members; -+ u_int32_t i, hashsize = map->hashsize; -+ int res; -+ struct ip_set_nethash *tmp; ++ struct ip_set_nethash *map = set->data; ++ int ret; + -+ if (map->resize == 0) -+ return -ERANGE; ++ if (map->elements >= limit || map->nets[cidr-1] == UINT16_MAX) ++ return -ERANGE; ++ if (cidr <= 0 || cidr >= 32) ++ return -EINVAL; + -+ again: -+ res = 0; -+ -+ /* Calculate new parameters */ -+ hashsize += (hashsize * map->resize)/100; -+ if (hashsize == map->hashsize) -+ hashsize++; -+ -+ ip_set_printk("rehashing of set %s triggered: " -+ "hashsize grows from %u to %u", -+ set->name, map->hashsize, hashsize); -+ -+ tmp = kmalloc(sizeof(struct ip_set_nethash) -+ + map->probes * sizeof(uint32_t), GFP_ATOMIC); -+ if (!tmp) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_nethash) -+ + map->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); -+ if (!tmp->members) { -+ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); -+ kfree(tmp); -+ return -ENOMEM; -+ } -+ tmp->hashsize = hashsize; -+ tmp->elements = 0; -+ tmp->probes = map->probes; -+ tmp->resize = map->resize; -+ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); -+ memcpy(tmp->cidr, map->cidr, 30 * sizeof(unsigned char)); ++ ip = pack_ip_cidr(ip, cidr); ++ if (!ip) ++ return -ERANGE; + -+ write_lock_bh(&set->lock); -+ map = (struct ip_set_nethash *) set->data; /* Play safe */ -+ for (i = 0; i < map->hashsize && res == 0; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ if (*elem) -+ res = __addip_base(tmp, *elem); -+ } -+ if (res) { -+ /* Failure, try again */ -+ write_unlock_bh(&set->lock); -+ harray_free(tmp->members); -+ kfree(tmp); -+ goto again; ++ ret = __nethash_add(map, &ip); ++ if (ret == 0) { ++ if (!map->nets[cidr-1]++) ++ add_cidr_size(map->cidr, cidr); + } + -+ /* Success at resizing! */ -+ members = map->members; -+ -+ map->hashsize = tmp->hashsize; -+ map->members = tmp->members; -+ write_unlock_bh(&set->lock); ++ return ret; ++} + -+ harray_free(members); -+ kfree(tmp); ++#undef KADT_CONDITION ++#define KADT_CONDITION \ ++ struct ip_set_nethash *map = set->data; \ ++ uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31; + -+ return 0; ++UADT(nethash, add, req->cidr) ++KADT(nethash, add, ipaddr, cidr) ++ ++static inline void ++__nethash_retry(struct ip_set_nethash *tmp, struct ip_set_nethash *map) ++{ ++ memcpy(tmp->cidr, map->cidr, sizeof(tmp->cidr)); ++ memcpy(tmp->nets, map->nets, sizeof(tmp->nets)); +} + ++HASH_RETRY(nethash, ip_set_ip_t) ++ +static inline int -+__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, -+ ip_set_ip_t *hash_ip) ++nethash_del(struct ip_set *set, ip_set_ip_t ip, uint8_t cidr) +{ ++ struct ip_set_nethash *map = set->data; + ip_set_ip_t id, *elem; + -+ if (!ip) -+ return -ERANGE; ++ if (cidr <= 0 || cidr >= 32) ++ return -EINVAL; + -+ id = hash_id_cidr(map, ip, cidr, hash_ip); ++ id = nethash_id_cidr(map, ip, cidr); + if (id == UINT_MAX) + return -EEXIST; + + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); + *elem = 0; + map->elements--; ++ if (!map->nets[cidr-1]--) ++ del_cidr_size(map->cidr, cidr); + return 0; +} + -+static int -+delip(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_ip) -+{ -+ struct ip_set_req_nethash *req = -+ (struct ip_set_req_nethash *) data; -+ -+ if (size != sizeof(struct ip_set_req_nethash)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash), -+ size); -+ return -EINVAL; -+ } -+ /* TODO: no garbage collection in map->cidr */ -+ return __delip((struct ip_set_nethash *) set->data, -+ req->ip, req->cidr, hash_ip); -+} -+ -+static int -+delip_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_ip, -+ const u_int32_t *flags, -+ unsigned char index) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ int ret = -ERANGE; -+ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ ? ip_hdr(skb)->saddr -+ : ip_hdr(skb)->daddr); -+#else -+ ? skb->nh.iph->saddr -+ : skb->nh.iph->daddr); -+#endif -+ -+ if (map->cidr[0]) -+ ret = __delip(map, ip, map->cidr[0], hash_ip); -+ -+ return ret; -+} ++UADT(nethash, del, req->cidr) ++KADT(nethash, del, ipaddr, cidr) + -+static int create(struct ip_set *set, const void *data, size_t size) ++static inline int ++__nethash_create(const struct ip_set_req_nethash_create *req, ++ struct ip_set_nethash *map) +{ -+ struct ip_set_req_nethash_create *req = -+ (struct ip_set_req_nethash_create *) data; -+ struct ip_set_nethash *map; -+ uint16_t i; -+ -+ if (size != sizeof(struct ip_set_req_nethash_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_nethash_create), -+ size); -+ return -EINVAL; -+ } -+ -+ if (req->hashsize < 1) { -+ ip_set_printk("hashsize too small"); -+ return -ENOEXEC; -+ } -+ if (req->probes < 1) { -+ ip_set_printk("probes too small"); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_nethash) -+ + req->probes * sizeof(uint32_t), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_nethash) -+ + req->probes * sizeof(uint32_t)); -+ return -ENOMEM; -+ } -+ for (i = 0; i < req->probes; i++) -+ get_random_bytes(((uint32_t *) map->initval)+i, 4); -+ map->elements = 0; -+ map->hashsize = req->hashsize; -+ map->probes = req->probes; -+ map->resize = req->resize; -+ memset(map->cidr, 0, 30 * sizeof(unsigned char)); -+ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); -+ kfree(map); -+ return -ENOMEM; -+ } ++ memset(map->cidr, 0, sizeof(map->cidr)); ++ memset(map->nets, 0, sizeof(map->nets)); + -+ set->data = map; + return 0; +} + -+static void destroy(struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ -+ harray_free(map->members); -+ kfree(map); -+ -+ set->data = NULL; -+} -+ -+static void flush(struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); -+ memset(map->cidr, 0, 30 * sizeof(unsigned char)); -+ map->elements = 0; -+} -+ -+static void list_header(const struct ip_set *set, void *data) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ struct ip_set_req_nethash_create *header = -+ (struct ip_set_req_nethash_create *) data; -+ -+ header->hashsize = map->hashsize; -+ header->probes = map->probes; -+ header->resize = map->resize; -+} ++HASH_CREATE(nethash, ip_set_ip_t) ++HASH_DESTROY(nethash) + -+static int list_members_size(const struct ip_set *set) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++HASH_FLUSH_CIDR(nethash, ip_set_ip_t) + -+ return (map->hashsize * sizeof(ip_set_ip_t)); ++static inline void ++__nethash_list_header(const struct ip_set_nethash *map, ++ struct ip_set_req_nethash_create *header) ++{ +} + -+static void list_members(const struct ip_set *set, void *data) -+{ -+ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; -+ ip_set_ip_t i, *elem; -+ -+ for (i = 0; i < map->hashsize; i++) { -+ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); -+ ((ip_set_ip_t *)data)[i] = *elem; -+ } -+} ++HASH_LIST_HEADER(nethash) ++HASH_LIST_MEMBERS_SIZE(nethash, ip_set_ip_t) ++HASH_LIST_MEMBERS(nethash, ip_set_ip_t) + -+static struct ip_set_type ip_set_nethash = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_nethash), -+ .addip = &addip, -+ .addip_kernel = &addip_kernel, -+ .retry = &retry, -+ .delip = &delip, -+ .delip_kernel = &delip_kernel, -+ .testip = &testip, -+ .testip_kernel = &testip_kernel, -+ .header_size = sizeof(struct ip_set_req_nethash_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_RTYPE(nethash, IPSET_TYPE_IP | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); @@ -7030,30 +6364,18 @@ index 0000000..d94cc32 +module_param(limit, int, 0600); +MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); + -+static int __init ip_set_nethash_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_nethash); -+} -+ -+static void __exit ip_set_nethash_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_nethash); -+} -+ -+module_init(ip_set_nethash_init); -+module_exit(ip_set_nethash_fini); ++REGISTER_MODULE(nethash) diff --git a/net/ipv4/netfilter/ip_set_portmap.c b/net/ipv4/netfilter/ip_set_portmap.c new file mode 100644 -index 0000000..9dba9cf +index 0000000..8bb6e76 --- /dev/null +++ b/net/ipv4/netfilter/ip_set_portmap.c -@@ -0,0 +1,346 @@ -+/* Copyright (C) 2003-2004 Jozsef Kadlecsik +@@ -0,0 +1,130 @@ ++/* Copyright (C) 2003-2008 Jozsef Kadlecsik + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module implementing a port set type as a bitmap */ @@ -7063,9 +6385,6 @@ index 0000000..9dba9cf +#include +#include +#include -+#include -+#include -+#include +#include +#include +#include @@ -7074,333 +6393,450 @@ index 0000000..9dba9cf +#include + +#include ++#include + -+/* We must handle non-linear skbs */ -+static inline ip_set_ip_t -+get_port(const struct sk_buff *skb, u_int32_t flags) ++static inline int ++portmap_test(const struct ip_set *set, ip_set_ip_t port) +{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ struct iphdr *iph = ip_hdr(skb); -+#else -+ struct iphdr *iph = skb->nh.iph; -+#endif -+ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; -+ switch (iph->protocol) { -+ case IPPROTO_TCP: { -+ struct tcphdr tcph; ++ const struct ip_set_portmap *map = set->data; ++ ++ if (port < map->first_ip || port > map->last_ip) ++ return -ERANGE; + -+ /* See comments at tcp_match in ip_tables.c */ -+ if (offset) -+ return INVALID_PORT; ++ DP("set: %s, port: %u", set->name, port); ++ return !!test_bit(port - map->first_ip, map->members); ++} + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ tcph.source : tcph.dest); -+ } -+ case IPPROTO_UDP: { -+ struct udphdr udph; ++#define KADT_CONDITION \ ++ if (ip == INVALID_PORT) \ ++ return 0; + -+ if (offset) -+ return INVALID_PORT; ++UADT(portmap, test) ++KADT(portmap, test, get_port) + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -+ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) -+#else -+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) -+#endif -+ /* No choice either */ -+ return INVALID_PORT; -+ -+ return ntohs(flags & IPSET_SRC ? -+ udph.source : udph.dest); -+ } -+ default: -+ return INVALID_PORT; -+ } ++static inline int ++portmap_add(struct ip_set *set, ip_set_ip_t port) ++{ ++ struct ip_set_portmap *map = set->data; ++ ++ if (port < map->first_ip || port > map->last_ip) ++ return -ERANGE; ++ if (test_and_set_bit(port - map->first_ip, map->members)) ++ return -EEXIST; ++ ++ DP("set: %s, port %u", set->name, port); ++ return 0; +} + ++UADT(portmap, add) ++KADT(portmap, add, get_port) ++ +static inline int -+__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++portmap_del(struct ip_set *set, ip_set_ip_t port) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_portmap *map = set->data; + -+ if (port < map->first_port || port > map->last_port) ++ if (port < map->first_ip || port > map->last_ip) + return -ERANGE; -+ -+ *hash_port = port; -+ DP("set: %s, port:%u, %u", set->name, port, *hash_port); -+ return !!test_bit(port - map->first_port, map->members); ++ if (!test_and_clear_bit(port - map->first_ip, map->members)) ++ return -EEXIST; ++ ++ DP("set: %s, port %u", set->name, port); ++ return 0; +} + -+static int -+testport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) ++UADT(portmap, del) ++KADT(portmap, del, get_port) ++ ++static inline int ++__portmap_create(const struct ip_set_req_portmap_create *req, ++ struct ip_set_portmap *map) +{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big, %d elements (max %d)", ++ req->to - req->from + 1, MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ return bitmap_bytes(req->from, req->to); ++} + -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); -+ return -EINVAL; ++BITMAP_CREATE(portmap) ++BITMAP_DESTROY(portmap) ++BITMAP_FLUSH(portmap) ++ ++static inline void ++__portmap_list_header(const struct ip_set_portmap *map, ++ struct ip_set_req_portmap_create *header) ++{ ++} ++ ++BITMAP_LIST_HEADER(portmap) ++BITMAP_LIST_MEMBERS_SIZE(portmap, ip_set_ip_t, (map->last_ip - map->first_ip + 1), ++ test_bit(i, map->members)) ++ ++static void ++portmap_list_members(const struct ip_set *set, void *data, char dont_align) ++{ ++ const struct ip_set_portmap *map = set->data; ++ uint32_t i, n = 0; ++ ip_set_ip_t *d; ++ ++ if (dont_align) { ++ memcpy(data, map->members, map->size); ++ return; + } -+ return __testport(set, req->port, hash_port); ++ ++ for (i = 0; i < map->last_ip - map->first_ip + 1; i++) ++ if (test_bit(i, map->members)) { ++ d = data + n * IPSET_ALIGN(sizeof(ip_set_ip_t)); ++ *d = map->first_ip + i; ++ n++; ++ } ++} ++ ++IP_SET_TYPE(portmap, IPSET_TYPE_PORT | IPSET_DATA_SINGLE) ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("portmap type of IP sets"); ++ ++REGISTER_MODULE(portmap) +diff --git a/net/ipv4/netfilter/ip_set_setlist.c b/net/ipv4/netfilter/ip_set_setlist.c +new file mode 100644 +index 0000000..3cfdae8 +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_setlist.c +@@ -0,0 +1,324 @@ ++/* Copyright (C) 2008 Jozsef Kadlecsik ++ * ++ * 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 setlist type */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * before ==> index, ref ++ * after ==> ref, index ++ */ ++ ++static inline int ++next_index_eq(const struct ip_set_setlist *map, int i, ip_set_id_t index) ++{ ++ return i < map->size && map->index[i] == index; +} + +static int -+testport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) ++setlist_utest(struct ip_set *set, const void *data, u_int32_t size) +{ -+ int res; -+ ip_set_ip_t port = get_port(skb, flags[index]); ++ const struct ip_set_setlist *map = set->data; ++ const struct ip_set_req_setlist *req = data; ++ ip_set_id_t index, ref = IP_SET_INVALID_ID; ++ int i, res = 0; ++ struct ip_set *s; ++ ++ if (req->before && req->ref[0] == '\0') ++ return 0; + -+ DP("flag %s port %u", flags[index] & IPSET_SRC ? "SRC" : "DST", port); -+ if (port == INVALID_PORT) -+ return 0; ++ index = __ip_set_get_byname(req->name, &s); ++ if (index == IP_SET_INVALID_ID) ++ return 0; ++ if (req->ref[0] != '\0') { ++ ref = __ip_set_get_byname(req->ref, &s); ++ if (ref == IP_SET_INVALID_ID) ++ goto finish; ++ } ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID; i++) { ++ if (req->before && map->index[i] == index) { ++ res = next_index_eq(map, i + 1, ref); ++ break; ++ } else if (!req->before) { ++ if ((ref == IP_SET_INVALID_ID ++ && map->index[i] == index) ++ || (map->index[i] == ref ++ && next_index_eq(map, i + 1, index))) { ++ res = 1; ++ break; ++ } ++ } ++ } ++ if (ref != IP_SET_INVALID_ID) ++ __ip_set_put_byindex(ref); ++finish: ++ __ip_set_put_byindex(index); ++ return res; ++} + -+ res = __testport(set, port, hash_port); ++static int ++setlist_ktest(struct ip_set *set, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set_setlist *map = set->data; ++ int i, res = 0; + -+ return (res < 0 ? 0 : res); ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID ++ && res == 0; i++) ++ res = ip_set_testip_kernel(map->index[i], skb, flags); ++ return res; +} + +static inline int -+__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++insert_setlist(struct ip_set_setlist *map, int i, ip_set_id_t index) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ip_set_id_t tmp; ++ int j; + -+ if (port < map->first_port || port > map->last_port) ++ DP("i: %u, last %u\n", i, map->index[map->size - 1]); ++ if (i >= map->size || map->index[map->size - 1] != IP_SET_INVALID_ID) + return -ERANGE; -+ if (test_and_set_bit(port - map->first_port, map->members)) -+ return -EEXIST; -+ -+ *hash_port = port; -+ DP("port %u", port); ++ ++ for (j = i; j < map->size ++ && index != IP_SET_INVALID_ID; j++) { ++ tmp = map->index[j]; ++ map->index[j] = index; ++ index = tmp; ++ } + return 0; +} + +static int -+addport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) ++setlist_uadd(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); ++ struct ip_set_setlist *map = set->data; ++ const struct ip_set_req_setlist *req = data; ++ ip_set_id_t index, ref = IP_SET_INVALID_ID; ++ int i, res = -ERANGE; ++ struct ip_set *s; ++ ++ if (req->before && req->ref[0] == '\0') + return -EINVAL; ++ ++ index = __ip_set_get_byname(req->name, &s); ++ if (index == IP_SET_INVALID_ID) ++ return -EEXIST; ++ /* "Loop detection" */ ++ if (strcmp(s->type->typename, "setlist") == 0) ++ goto finish; ++ ++ if (req->ref[0] != '\0') { ++ ref = __ip_set_get_byname(req->ref, &s); ++ if (ref == IP_SET_INVALID_ID) { ++ res = -EEXIST; ++ goto finish; ++ } + } -+ return __addport(set, req->port, hash_port); ++ for (i = 0; i < map->size; i++) { ++ if (map->index[i] != ref) ++ continue; ++ if (req->before) ++ res = insert_setlist(map, i, index); ++ else ++ res = insert_setlist(map, ++ ref == IP_SET_INVALID_ID ? i : i + 1, ++ index); ++ break; ++ } ++ if (ref != IP_SET_INVALID_ID) ++ __ip_set_put_byindex(ref); ++ /* In case of success, we keep the reference to the set */ ++finish: ++ if (res != 0) ++ __ip_set_put_byindex(index); ++ return res; +} + +static int -+addport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) ++setlist_kadd(struct ip_set *set, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) +{ -+ ip_set_ip_t port = get_port(skb, flags[index]); ++ struct ip_set_setlist *map = set->data; ++ int i, res = -EINVAL; + -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __addport(set, port, hash_port); ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID ++ && res != 0; i++) ++ res = ip_set_addip_kernel(map->index[i], skb, flags); ++ return res; +} + +static inline int -+__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++unshift_setlist(struct ip_set_setlist *map, int i) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ if (port < map->first_port || port > map->last_port) -+ return -ERANGE; -+ if (!test_and_clear_bit(port - map->first_port, map->members)) -+ return -EEXIST; -+ -+ *hash_port = port; -+ DP("port %u", port); ++ int j; ++ ++ for (j = i; j < map->size - 1; j++) ++ map->index[j] = map->index[j+1]; ++ map->index[map->size-1] = IP_SET_INVALID_ID; + return 0; +} + +static int -+delport(struct ip_set *set, const void *data, size_t size, -+ ip_set_ip_t *hash_port) ++setlist_udel(struct ip_set *set, const void *data, u_int32_t size) +{ -+ struct ip_set_req_portmap *req = -+ (struct ip_set_req_portmap *) data; -+ -+ if (size != sizeof(struct ip_set_req_portmap)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap), -+ size); ++ struct ip_set_setlist *map = set->data; ++ const struct ip_set_req_setlist *req = data; ++ ip_set_id_t index, ref = IP_SET_INVALID_ID; ++ int i, res = -EEXIST; ++ struct ip_set *s; ++ ++ if (req->before && req->ref[0] == '\0') + return -EINVAL; ++ ++ index = __ip_set_get_byname(req->name, &s); ++ if (index == IP_SET_INVALID_ID) ++ return -EEXIST; ++ if (req->ref[0] != '\0') { ++ ref = __ip_set_get_byname(req->ref, &s); ++ if (ref == IP_SET_INVALID_ID) ++ goto finish; ++ } ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID; i++) { ++ if (req->before) { ++ if (map->index[i] == index ++ && next_index_eq(map, i + 1, ref)) { ++ res = unshift_setlist(map, i); ++ break; ++ } ++ } else if (ref == IP_SET_INVALID_ID) { ++ if (map->index[i] == index) { ++ res = unshift_setlist(map, i); ++ break; ++ } ++ } else if (map->index[i] == ref ++ && next_index_eq(map, i + 1, index)) { ++ res = unshift_setlist(map, i + 1); ++ break; ++ } + } -+ return __delport(set, req->port, hash_port); ++ if (ref != IP_SET_INVALID_ID) ++ __ip_set_put_byindex(ref); ++finish: ++ __ip_set_put_byindex(index); ++ /* In case of success, release the reference to the set */ ++ if (res == 0) ++ __ip_set_put_byindex(index); ++ return res; +} + +static int -+delport_kernel(struct ip_set *set, -+ const struct sk_buff *skb, -+ ip_set_ip_t *hash_port, -+ const u_int32_t *flags, -+ unsigned char index) ++setlist_kdel(struct ip_set *set, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) +{ -+ ip_set_ip_t port = get_port(skb, flags[index]); ++ struct ip_set_setlist *map = set->data; ++ int i, res = -EINVAL; + -+ if (port == INVALID_PORT) -+ return -EINVAL; -+ -+ return __delport(set, port, hash_port); ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID ++ && res != 0; i++) ++ res = ip_set_delip_kernel(map->index[i], skb, flags); ++ return res; +} + -+static int create(struct ip_set *set, const void *data, size_t size) ++static int ++setlist_create(struct ip_set *set, const void *data, u_int32_t size) +{ -+ int newbytes; -+ struct ip_set_req_portmap_create *req = -+ (struct ip_set_req_portmap_create *) data; -+ struct ip_set_portmap *map; -+ -+ if (size != sizeof(struct ip_set_req_portmap_create)) { -+ ip_set_printk("data length wrong (want %zu, have %zu)", -+ sizeof(struct ip_set_req_portmap_create), -+ size); -+ return -EINVAL; -+ } -+ -+ DP("from %u to %u", req->from, req->to); -+ -+ if (req->from > req->to) { -+ DP("bad port range"); -+ return -ENOEXEC; -+ } -+ -+ if (req->to - req->from > MAX_RANGE) { -+ ip_set_printk("range too big (max %d ports)", -+ MAX_RANGE+1); -+ return -ENOEXEC; -+ } -+ -+ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); -+ if (!map) { -+ DP("out of memory for %d bytes", -+ sizeof(struct ip_set_portmap)); -+ return -ENOMEM; -+ } -+ map->first_port = req->from; -+ map->last_port = req->to; -+ newbytes = bitmap_bytes(req->from, req->to); -+ map->members = kmalloc(newbytes, GFP_KERNEL); -+ if (!map->members) { -+ DP("out of memory for %d bytes", newbytes); -+ kfree(map); ++ struct ip_set_setlist *map; ++ const struct ip_set_req_setlist_create *req = data; ++ int i; ++ ++ map = kmalloc(sizeof(struct ip_set_setlist) + ++ req->size * sizeof(ip_set_id_t), GFP_KERNEL); ++ if (!map) + return -ENOMEM; -+ } -+ memset(map->members, 0, newbytes); -+ ++ map->size = req->size; ++ for (i = 0; i < map->size; i++) ++ map->index[i] = IP_SET_INVALID_ID; ++ + set->data = map; + return 0; -+} ++} + -+static void destroy(struct ip_set *set) ++static void ++setlist_destroy(struct ip_set *set) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_setlist *map = set->data; ++ int i; ++ ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID; i++) ++ __ip_set_put_byindex(map->index[i]); + -+ kfree(map->members); + kfree(map); -+ + set->data = NULL; +} + -+static void flush(struct ip_set *set) ++static void ++setlist_flush(struct ip_set *set) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); ++ struct ip_set_setlist *map = set->data; ++ int i; ++ ++ for (i = 0; i < map->size ++ && map->index[i] != IP_SET_INVALID_ID; i++) { ++ __ip_set_put_byindex(map->index[i]); ++ map->index[i] = IP_SET_INVALID_ID; ++ } +} + -+static void list_header(const struct ip_set *set, void *data) ++static void ++setlist_list_header(const struct ip_set *set, void *data) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ struct ip_set_req_portmap_create *header = -+ (struct ip_set_req_portmap_create *) data; -+ -+ DP("list_header %u %u", map->first_port, map->last_port); -+ -+ header->from = map->first_port; -+ header->to = map->last_port; ++ const struct ip_set_setlist *map = set->data; ++ struct ip_set_req_setlist_create *header = data; ++ ++ header->size = map->size; +} + -+static int list_members_size(const struct ip_set *set) ++static int ++setlist_list_members_size(const struct ip_set *set, char dont_align) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ -+ return bitmap_bytes(map->first_port, map->last_port); ++ const struct ip_set_setlist *map = set->data; ++ ++ return map->size * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); +} + -+static void list_members(const struct ip_set *set, void *data) ++static void ++setlist_list_members(const struct ip_set *set, void *data, char dont_align) +{ -+ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; -+ int bytes = bitmap_bytes(map->first_port, map->last_port); -+ -+ memcpy(data, map->members, bytes); ++ struct ip_set_setlist *map = set->data; ++ ip_set_id_t *d; ++ int i; ++ ++ for (i = 0; i < map->size; i++) { ++ d = data + i * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); ++ *d = ip_set_id(map->index[i]); ++ } +} + -+static struct ip_set_type ip_set_portmap = { -+ .typename = SETTYPE_NAME, -+ .features = IPSET_TYPE_PORT | IPSET_DATA_SINGLE, -+ .protocol_version = IP_SET_PROTOCOL_VERSION, -+ .create = &create, -+ .destroy = &destroy, -+ .flush = &flush, -+ .reqsize = sizeof(struct ip_set_req_portmap), -+ .addip = &addport, -+ .addip_kernel = &addport_kernel, -+ .delip = &delport, -+ .delip_kernel = &delport_kernel, -+ .testip = &testport, -+ .testip_kernel = &testport_kernel, -+ .header_size = sizeof(struct ip_set_req_portmap_create), -+ .list_header = &list_header, -+ .list_members_size = &list_members_size, -+ .list_members = &list_members, -+ .me = THIS_MODULE, -+}; ++IP_SET_TYPE(setlist, IPSET_TYPE_SETNAME | IPSET_DATA_SINGLE) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); -+MODULE_DESCRIPTION("portmap type of IP sets"); -+ -+static int __init ip_set_portmap_init(void) -+{ -+ return ip_set_register_set_type(&ip_set_portmap); -+} -+ -+static void __exit ip_set_portmap_fini(void) -+{ -+ /* FIXME: possible race with ip_set_create() */ -+ ip_set_unregister_set_type(&ip_set_portmap); -+} ++MODULE_DESCRIPTION("setlist type of IP sets"); + -+module_init(ip_set_portmap_init); -+module_exit(ip_set_portmap_fini); ++REGISTER_MODULE(setlist) diff --git a/net/ipv4/netfilter/ipt_SET.c b/net/ipv4/netfilter/ipt_SET.c new file mode 100644 -index 0000000..2052c0c +index 0000000..952b5d9 --- /dev/null +++ b/net/ipv4/netfilter/ipt_SET.c -@@ -0,0 +1,162 @@ +@@ -0,0 +1,256 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson @@ -7408,73 +6844,158 @@ index 0000000..2052c0c + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* ipt_SET.c - netfilter target to manipulate IP sets */ + -+#include -+#include -+#include +#include -+#include -+#include -+#include -+#include ++#include ++#include +#include -+#include -+#include ++ +#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +#include ++#define xt_register_target ipt_register_target ++#define xt_unregister_target ipt_unregister_target ++#define xt_target ipt_target ++#define XT_CONTINUE IPT_CONTINUE ++#else ++#include ++#endif +#include + -+static unsigned int target(struct sk_buff *skb, ++static unsigned int ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++target(struct sk_buff **pskb, ++ unsigned int hooknum, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targinfo, ++ void *userinfo) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userinfo) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo, ++ void *userinfo) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++target(struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const struct xt_target *target, + const void *targinfo) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++target(struct sk_buff *skb, ++ const struct xt_target_param *par) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++target(struct sk_buff *skb, ++ const struct xt_action_param *par) ++#endif +{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + const struct ipt_set_info_target *info = targinfo; ++#else ++ const struct ipt_set_info_target *info = par->targinfo; ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++ struct sk_buff *skb = *pskb; ++#endif ++ + + if (info->add_set.index != IP_SET_INVALID_ID) -+ ip_set_addip_kernel(info->add_set.index, skb, ++ ip_set_addip_kernel(info->add_set.index, ++ skb, + info->add_set.flags); + if (info->del_set.index != IP_SET_INVALID_ID) -+ ip_set_delip_kernel(info->del_set.index, skb, ++ ip_set_delip_kernel(info->del_set.index, ++ skb, + info->del_set.flags); + -+ return IPT_CONTINUE; ++ return XT_CONTINUE; +} + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+static bool -+#else -+static int ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++#define CHECK_OK 1 ++#define CHECK_FAIL 0 ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++#define CHECK_OK 0 ++#define CHECK_FAIL -EINVAL +#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) ++static int +checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *e, -+#else + const struct ipt_entry *e, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static int ++checkentry(const char *tablename, ++ const void *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++static int ++checkentry(const char *tablename, ++ const void *e, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++static int ++checkentry(const char *tablename, ++ const void *e, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static bool ++checkentry(const char *tablename, ++ const void *e, + const struct xt_target *target, -+#endif + void *targinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ unsigned int targinfosize, -+#endif + unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++static bool ++checkentry(const struct xt_tgchk_param *par) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++static int ++checkentry(const struct xt_tgchk_param *par) ++#endif +{ -+ struct ipt_set_info_target *info = -+ (struct ipt_set_info_target *) targinfo; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ const struct ipt_set_info_target *info = targinfo; ++#else ++ const struct ipt_set_info_target *info = par->targinfo; ++#endif + ip_set_id_t index; + -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + if (targinfosize != IPT_ALIGN(sizeof(*info))) { + DP("bad target info size %u", targinfosize); -+ return 0; ++ return CHECK_FAIL; + } +#endif + @@ -7483,7 +7004,7 @@ index 0000000..2052c0c + if (index == IP_SET_INVALID_ID) { + ip_set_printk("cannot find add_set index %u as target", + info->add_set.index); -+ return 0; /* error */ ++ return CHECK_FAIL; /* error */ + } + } + @@ -7492,83 +7013,92 @@ index 0000000..2052c0c + if (index == IP_SET_INVALID_ID) { + ip_set_printk("cannot find del_set index %u as target", + info->del_set.index); -+ return 0; /* error */ ++ return CHECK_FAIL; /* error */ + } + } + if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 + || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { + ip_set_printk("That's nasty!"); -+ return 0; /* error */ ++ return CHECK_FAIL; /* error */ + } + -+ return 1; ++ return CHECK_OK; +} + -+static void destroy( -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_target *target, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ void *targetinfo, unsigned int targetsize) -+#else ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static void destroy(void *targetinfo, ++ unsigned int targetsize) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++static void destroy(const struct xt_target *target, ++ void *targetinfo, ++ unsigned int targetsize) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static void destroy(const struct xt_target *target, + void *targetinfo) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ ++static void destroy(const struct xt_tgdtor_param *par) +#endif +{ -+ struct ipt_set_info_target *info = targetinfo; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ const struct ipt_set_info_target *info = targetinfo; ++#else ++ const struct ipt_set_info_target *info = par->targinfo; ++#endif + -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { + ip_set_printk("invalid targetsize %d", targetsize); + return; + } +#endif + if (info->add_set.index != IP_SET_INVALID_ID) -+ ip_set_put(info->add_set.index); ++ ip_set_put_byindex(info->add_set.index); + if (info->del_set.index != IP_SET_INVALID_ID) -+ ip_set_put(info->del_set.index); ++ ip_set_put_byindex(info->del_set.index); +} + -+static struct ipt_target SET_target = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static struct xt_target SET_target = { ++ .name = "SET", ++ .target = target, ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE ++}; ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */ ++static struct xt_target SET_target = { + .name = "SET", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + .family = AF_INET, -+#endif + .target = target, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + .targetsize = sizeof(struct ipt_set_info_target), -+#endif + .checkentry = checkentry, + .destroy = destroy, + .me = THIS_MODULE +}; ++#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("iptables IP set target module"); + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+#define ipt_register_target xt_register_target -+#define ipt_unregister_target xt_unregister_target -+#endif -+ +static int __init ipt_SET_init(void) +{ -+ return ipt_register_target(&SET_target); ++ return xt_register_target(&SET_target); +} + +static void __exit ipt_SET_fini(void) +{ -+ ipt_unregister_target(&SET_target); ++ xt_unregister_target(&SET_target); +} + +module_init(ipt_SET_init); +module_exit(ipt_SET_fini); diff --git a/net/ipv4/netfilter/ipt_set.c b/net/ipv4/netfilter/ipt_set.c new file mode 100644 -index 0000000..f192013 +index 0000000..3c661be --- /dev/null +++ b/net/ipv4/netfilter/ipt_set.c -@@ -0,0 +1,160 @@ +@@ -0,0 +1,253 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson @@ -7576,7 +7106,7 @@ index 0000000..f192013 + * + * 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. ++ * published by the Free Software Foundation. + */ + +/* Kernel module to match an IP set. */ @@ -7586,7 +7116,14 @@ index 0000000..f192013 +#include +#include + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +#include ++#define xt_register_match ipt_register_match ++#define xt_unregister_match ipt_unregister_match ++#define xt_match ipt_match ++#else ++#include ++#endif +#include +#include + @@ -7600,61 +7137,137 @@ index 0000000..f192013 + return inv; +} + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+static bool -+#else ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ const void *hdr, ++ u_int16_t datalen, ++ int *hotdrop) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ int *hotdrop) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ int *hotdrop) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) +static int -+#endif +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + const struct xt_match *match, -+#endif + const void *matchinfo, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+ int offset, unsigned int protoff, bool *hotdrop) -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ int offset, unsigned int protoff, int *hotdrop) -+#else -+ int offset, int *hotdrop) ++ int offset, ++ unsigned int protoff, ++ int *hotdrop) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static bool ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ bool *hotdrop) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++static bool ++match(const struct sk_buff *skb, ++ const struct xt_match_param *par) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++static bool ++match(const struct sk_buff *skb, ++ struct xt_action_param *par) +#endif +{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + const struct ipt_set_info_match *info = matchinfo; ++#else ++ const struct ipt_set_info_match *info = par->matchinfo; ++#endif + + return match_set(&info->match_set, + skb, + info->match_set.flags[0] & IPSET_MATCH_INV); +} + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) -+bool -+#else -+static int ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++#define CHECK_OK 1 ++#define CHECK_FAIL 0 ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++#define CHECK_OK 0 ++#define CHECK_FAIL -EINVAL +#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) ++static int +checkentry(const char *tablename, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ const void *inf, -+#else + const struct ipt_ip *ip, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static int ++checkentry(const char *tablename, ++ const void *inf, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++static int ++checkentry(const char *tablename, ++ const void *inf, + const struct xt_match *match, -+#endif + void *matchinfo, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + unsigned int matchsize, -+#endif + unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++static int ++checkentry(const char *tablename, ++ const void *inf, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static bool ++checkentry(const char *tablename, ++ const void *inf, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++static bool ++checkentry(const struct xt_mtchk_param *par) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ ++static int ++checkentry(const struct xt_mtchk_param *par) ++#endif +{ -+ struct ipt_set_info_match *info = -+ (struct ipt_set_info_match *) matchinfo; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ struct ipt_set_info_match *info = matchinfo; ++#else ++ struct ipt_set_info_match *info = par->matchinfo; ++#endif + ip_set_id_t index; + -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { + ip_set_printk("invalid matchsize %d", matchsize); -+ return 0; ++ return CHECK_FAIL; + } +#endif + @@ -7663,68 +7276,78 @@ index 0000000..f192013 + if (index == IP_SET_INVALID_ID) { + ip_set_printk("Cannot find set indentified by id %u to match", + info->match_set.index); -+ return 0; /* error */ ++ return CHECK_FAIL; /* error */ + } + if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { + ip_set_printk("That's nasty!"); -+ return 0; /* error */ ++ return CHECK_FAIL; /* error */ + } + -+ return 1; ++ return CHECK_OK; +} + -+static void destroy( -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) -+ const struct xt_match *match, -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ void *matchinfo, unsigned int matchsize) -+#else ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static void destroy(void *matchinfo, ++ unsigned int matchsize) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++static void destroy(const struct xt_match *match, ++ void *matchinfo, ++ unsigned int matchsize) ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static void destroy(const struct xt_match *match, + void *matchinfo) ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ ++static void destroy(const struct xt_mtdtor_param *par) +#endif +{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + struct ipt_set_info_match *info = matchinfo; ++#else ++ struct ipt_set_info_match *info = par->matchinfo; ++#endif + -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { + ip_set_printk("invalid matchsize %d", matchsize); + return; + } +#endif -+ ip_set_put(info->match_set.index); ++ ip_set_put_byindex(info->match_set.index); +} + -+static struct ipt_match set_match = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ++static struct xt_match set_match = { ++ .name = "set", ++ .match = &match, ++ .checkentry = &checkentry, ++ .destroy = &destroy, ++ .me = THIS_MODULE ++}; ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */ ++static struct xt_match set_match = { + .name = "set", -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) + .family = AF_INET, -+#endif + .match = &match, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) + .matchsize = sizeof(struct ipt_set_info_match), -+#endif + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; ++#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("iptables IP set match module"); + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) -+#define ipt_register_match xt_register_match -+#define ipt_unregister_match xt_unregister_match -+#endif -+ +static int __init ipt_ipset_init(void) +{ -+ return ipt_register_match(&set_match); ++ return xt_register_match(&set_match); +} + +static void __exit ipt_ipset_fini(void) +{ -+ ipt_unregister_match(&set_match); ++ xt_unregister_match(&set_match); +} + +module_init(ipt_ipset_init);