summaryrefslogtreecommitdiffstats
path: root/kernel/ip_set_nethash.c
diff options
context:
space:
mode:
author/C=EU/ST=EU/CN=Jozsef Kadlecsik/emailAddress=kadlec@blackhole.kfki.hu </C=EU/ST=EU/CN=Jozsef Kadlecsik/emailAddress=kadlec@blackhole.kfki.hu>2008-10-20 10:00:26 +0000
committer/C=EU/ST=EU/CN=Jozsef Kadlecsik/emailAddress=kadlec@blackhole.kfki.hu </C=EU/ST=EU/CN=Jozsef Kadlecsik/emailAddress=kadlec@blackhole.kfki.hu>2008-10-20 10:00:26 +0000
commita96e4fca10506462df4ee4035f0f86f09bd9dc34 (patch)
tree103bed0a7ae3608675f371d2ac91f3fa7f3a58cc /kernel/ip_set_nethash.c
parentbc2ddd2d8da1252e78a1f25bd91c1e3cd8016ead (diff)
ipset 2.4 release
userspace changes: - Added KBUILD_OUTPUT support (Sven Wegener) - Fix memory leak in ipset_iptreemap (Sven Wegener) - Fix multiple compiler warnings (Sven Wegener) - ipportiphash, ipportnethash and setlist types added - binding marked as deprecated functionality - element separator token changed to ',' in anticipating IPv6 addresses, old separator tokens are still supported - unnecessary includes removed - ipset does not try to resolve IP addresses when listing the content of sets (default changed) - manpage updated - ChangeLog forked for kernel part kernel part changes: - ipportiphash, ipportnethash and setlist types added - set type modules reworked to avoid code duplication as much as possible, code unification macros - expand_macros Makefile target added to help debugging code unification macros - ip_set_addip_kernel and ip_set_delip_kernel changed from void to int, __ip_set_get_byname and __ip_set_put_byid added for the sake of setlist type - unnecessary includes removed - compatibility fix for kernels >= 2.6.27: semaphore.h was moved from asm/ to linux/ (James King) - ChangeLog forked for kernel part
Diffstat (limited to 'kernel/ip_set_nethash.c')
-rw-r--r--kernel/ip_set_nethash.c422
1 files changed, 84 insertions, 338 deletions
diff --git a/kernel/ip_set_nethash.c b/kernel/ip_set_nethash.c
index ecdf369..a04857c 100644
--- a/kernel/ip_set_nethash.c
+++ b/kernel/ip_set_nethash.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2008 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,41 +11,32 @@
#include <linux/moduleparam.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
-#include <linux/version.h>
#include <linux/jhash.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ip_set.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
-#include <linux/vmalloc.h>
#include <linux/random.h>
#include <net/ip.h>
-#include <linux/netfilter_ipv4/ip_set_malloc.h>
+#include <linux/netfilter_ipv4/ip_set.h>
+#include <linux/netfilter_ipv4/ip_set_hashes.h>
#include <linux/netfilter_ipv4/ip_set_nethash.h>
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 *hash_ip,
+ ip_set_ip_t ip,
+ uint8_t cidr)
{
__u32 id;
u_int16_t i;
ip_set_ip_t *elem;
- *hash_ip = pack(ip, cidr);
+ *hash_ip = pack_ip_cidr(ip, cidr);
for (i = 0; i < map->probes; i++) {
id = jhash_ip(map, i, *hash_ip) % map->hashsize;
@@ -58,14 +49,14 @@ hash_id_cidr(struct ip_set_nethash *map,
}
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 *hash_ip, ip_set_ip_t ip)
{
- struct ip_set_nethash *map = 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, hash_ip, ip, map->cidr[i]);
if (id != UINT_MAX)
break;
}
@@ -73,65 +64,50 @@ hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
}
static inline int
-__testip_cidr(struct ip_set *set, 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 *hash_ip,
+ ip_set_ip_t ip, uint8_t cidr)
{
- struct ip_set_nethash *map = set->data;
+ const struct ip_set_nethash *map = set->data;
- return (ip && hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX);
+ return (ip && nethash_id_cidr(map, hash_ip, ip, cidr) != UINT_MAX);
}
static inline int
-__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
+nethash_test(struct ip_set *set, ip_set_ip_t *hash_ip, ip_set_ip_t ip)
{
- return (ip && hash_id(set, ip, hash_ip) != UINT_MAX);
+ return (ip && nethash_id(set, hash_ip, ip) != UINT_MAX);
}
static int
-testip(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, size_t size,
+ ip_set_ip_t *hash_ip)
{
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;
- }
- return (req->cidr == 32 ? __testip(set, req->ip, hash_ip)
- : __testip_cidr(set, req->ip, req->cidr, hash_ip));
+ return (req->cidr == 32 ? nethash_test(set, hash_ip, req->ip)
+ : nethash_test_cidr(set, hash_ip, req->ip, req->cidr));
}
-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
- ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr),
- hash_ip);
-}
+#define KADT_CONDITION
+
+KADT(nethash, test, ipaddr)
static inline int
-__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip)
+__nethash_add(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;
+ probe = jhash_ip(map, i, *ip) % map->hashsize;
elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe);
- if (*elem == ip)
+ if (*elem == *ip)
return -EEXIST;
if (!*elem) {
- *elem = ip;
- map->elements++;
+ *elem = *ip;
return 0;
}
}
@@ -140,319 +116,102 @@ __addip_base(struct ip_set_nethash *map, ip_set_ip_t ip)
}
static inline int
-__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr,
- ip_set_ip_t *hash_ip)
+nethash_add(struct ip_set *set, ip_set_ip_t *hash_ip,
+ ip_set_ip_t ip, uint8_t cidr)
{
+ struct ip_set_nethash *map = set->data;
+ int ret;
+
if (!ip || map->elements >= limit)
return -ERANGE;
+ if (cidr <= 0 || cidr >= 32)
+ return -EINVAL;
+ if (map->nets[cidr-1] == UINT16_MAX)
+ return -ERANGE;
- *hash_ip = pack(ip, cidr);
+ *hash_ip = pack_ip_cidr(ip, cidr);
DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip));
- return __addip_base(map, *hash_ip);
-}
-
-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;
- }
+ ret = __nethash_add(map, hash_ip);
+ if (ret == 0) {
+ if (!map->nets[cidr-1]++)
+ add_cidr_size(map->cidr, cidr);
+ map->elements++;
}
- if (i < 30)
- map->cidr[i] = cidr;
-}
-
-static int
-addip(struct ip_set *set, const void *data, size_t size,
- ip_set_ip_t *hash_ip)
-{
- const struct ip_set_req_nethash *req = data;
- int ret;
-
- 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;
- }
- ret = __addip(set->data, req->ip, req->cidr, hash_ip);
-
- if (ret == 0)
- update_cidr_sizes(set->data, req->cidr);
return ret;
}
-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 = set->data;
- int ret = -ERANGE;
- ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC
- ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr);
-
- if (map->cidr[0])
- ret = __addip(map, ip, map->cidr[0], hash_ip);
-
- return ret;
-}
-
-static int retry(struct ip_set *set)
-{
- struct ip_set_nethash *map = set->data;
- ip_set_ip_t *elem;
- void *members;
- u_int32_t i, hashsize = map->hashsize;
- int res;
- struct ip_set_nethash *tmp;
-
- if (map->resize == 0)
- return -ERANGE;
-
- 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));
-
- write_lock_bh(&set->lock);
- map = 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;
- }
-
- /* Success at resizing! */
- members = map->members;
-
- map->hashsize = tmp->hashsize;
- map->members = tmp->members;
- write_unlock_bh(&set->lock);
+#undef KADT_CONDITION
+#define KADT_CONDITION \
+ struct ip_set_nethash *map = set->data; \
+ uint8_t cidr = map->cidr[0] ? map->cidr[0] : 31;
- harray_free(members);
- kfree(tmp);
+UADT(nethash, add, req->cidr)
+KADT(nethash, add, ipaddr, cidr)
- return 0;
+static inline void
+__nethash_retry(struct ip_set_nethash *tmp, struct ip_set_nethash *map)
+{
+ memcpy(tmp->cidr, map->cidr, 30 * sizeof(uint8_t));
+ memcpy(tmp->nets, map->nets, 30 * sizeof(uint16_t));
}
+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 *hash_ip,
+ 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, hash_ip, 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)
-{
- const struct ip_set_req_nethash *req = data;
+UADT(nethash, del, req->cidr)
+KADT(nethash, del, ipaddr, cidr)
- 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(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 = set->data;
- int ret = -ERANGE;
- ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC
- ? ip_hdr(skb)->saddr
- : ip_hdr(skb)->daddr);
-
- if (map->cidr[0])
- ret = __delip(map, ip, map->cidr[0], hash_ip);
-
- return ret;
-}
-
-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)
{
- const struct ip_set_req_nethash_create *req = 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, 30 * sizeof(uint8_t));
+ memset(map->nets, 0, 30 * sizeof(uint16_t));
- set->data = map;
return 0;
}
-static void destroy(struct ip_set *set)
-{
- struct ip_set_nethash *map = set->data;
+HASH_CREATE(nethash, ip_set_ip_t)
+HASH_DESTROY(nethash)
- harray_free(map->members);
- kfree(map);
+HASH_FLUSH_CIDR(nethash, ip_set_ip_t)
- set->data = NULL;
+static inline void
+__nethash_list_header(const struct ip_set_nethash *map,
+ struct ip_set_req_nethash_create *header)
+{
}
-static void flush(struct ip_set *set)
-{
- struct ip_set_nethash *map = set->data;
- harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t));
- memset(map->cidr, 0, 30 * sizeof(unsigned char));
- map->elements = 0;
-}
+HASH_LIST_HEADER(nethash)
+HASH_LIST_MEMBERS_SIZE(nethash, ip_set_ip_t)
+HASH_LIST_MEMBERS(nethash, ip_set_ip_t)
-static void list_header(const struct ip_set *set, void *data)
-{
- const struct ip_set_nethash *map = set->data;
- struct ip_set_req_nethash_create *header = data;
-
- header->hashsize = map->hashsize;
- header->probes = map->probes;
- header->resize = map->resize;
-}
-
-static int list_members_size(const struct ip_set *set)
-{
- struct ip_set_nethash *map = set->data;
-
- return (map->hashsize * sizeof(ip_set_ip_t));
-}
-
-static void list_members(const struct ip_set *set, void *data)
-{
- const struct ip_set_nethash *map = 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;
- }
-}
-
-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 <kadlec@blackhole.kfki.hu>");
@@ -460,17 +219,4 @@ MODULE_DESCRIPTION("nethash type of IP sets");
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)
-{
- init_max_page_size();
- 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)