/* Kernel module for IP set management */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#include <linux/config.h>
+#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kmod.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
-#define ASSERT_READ_LOCK(x) /* dont use that */
+#define ASSERT_READ_LOCK(x)
#define ASSERT_WRITE_LOCK(x)
-#include <linux/netfilter_ipv4/listhelp.h>
#include <linux/netfilter_ipv4/ip_set.h>
static struct list_head set_type_list; /* all registered sets */
* Binding routines
*/
-static inline int
-ip_hash_cmp(const struct ip_set_hash *set_hash,
- ip_set_id_t id, ip_set_ip_t ip)
+static inline struct ip_set_hash *
+__ip_set_find(u_int32_t key, ip_set_id_t id, ip_set_ip_t ip)
{
- return set_hash->id == id && set_hash->ip == 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_ASSERT(ip_set_list[id]);
DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip));
- set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
- struct ip_set_hash *, id, 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),
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 = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
- struct ip_set_hash *, id, 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 : "");
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 = LIST_FIND(&ip_set_hash[key], ip_hash_cmp,
- struct ip_set_hash *, id, ip);
+ set_hash = __ip_set_find(key, id, ip);
if (!set_hash) {
- set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL);
+ 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(&ip_set_hash[key], &set_hash->list);
+ list_add(&set_hash->list, &ip_set_hash[key]);
} else {
IP_SET_ASSERT(ip_set_list[set_hash->binding]);
DP("overwrite binding: %s",
}
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;
/* Register and deregister settype */
-static inline int
-set_type_equal(const struct ip_set_type *set_type, const char *str2)
-{
- return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1);
-}
-
static inline struct ip_set_type *
find_set_type(const char *name)
{
- return LIST_FIND(&set_type_list,
- set_type_equal,
- struct ip_set_type *,
- name);
+ 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))
+ return set_type;
+ return NULL;
}
int
ret = -EFAULT;
goto unlock;
}
- list_append(&set_type_list, set_type);
+ list_add(&set_type->list, &set_type_list);
DP("'%s' registered.", set_type->typename);
unlock:
write_unlock_bh(&ip_set_lock);
set_type->typename);
goto unlock;
}
- LIST_DELETE(&set_type_list, set_type);
+ list_del(&set_type->list);
module_put(THIS_MODULE);
DP("'%s' unregistered.", set_type->typename);
unlock:
size_t size)
{
struct ip_set *set;
- ip_set_id_t index, id;
+ ip_set_id_t index = 0, id;
int res = 0;
DP("setname: %s, typename: %s, id: %u", name, typename, restore);
set->type->list_header(set, data + *used);
*used += set_save->header_size;
- DP("set header filled: %s, used: %u %p %p", set->name, *used,
- data, data + *used);
+ DP("set header filled: %s, used: %u(%u) %p %p", set->name, *used,
+ set_save->header_size, data, data + *used);
/* Get and ensure set specific members size */
set_save->members_size = set->type->list_members_size(set);
if (*used + set_save->members_size > len)
set->type->list_members(set, data + *used);
*used += set_save->members_size;
read_unlock_bh(&set->lock);
- DP("set members filled: %s, used: %u %p %p", set->name, *used,
- data, data + *used);
+ DP("set members filled: %s, used: %u(%u) %p %p", set->name, *used,
+ set_save->members_size, data, data + *used);
return 0;
unlock_set:
/* 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);
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)
struct ip_set_req_create *req_create
= (struct ip_set_req_create *) data;
- if (len <= sizeof(struct ip_set_req_create)) {
- ip_set_printk("short CREATE data (want >%zu, got %u)",
+ if (len < sizeof(struct ip_set_req_create)) {
+ ip_set_printk("short CREATE data (want >=%zu, got %u)",
sizeof(struct ip_set_req_create), len);
res = -EINVAL;
goto done;
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,
- i, &req_setnames->size);
+ set->id, &req_setnames->size);
break;
}
case IP_SET_OP_SAVE_SIZE: {
+ set->type->header_size
+ set->type->list_members_size(set);
FOREACH_HASH_DO(__set_hash_bindings_size_save,
- i, &req_setnames->size);
+ set->id, &req_setnames->size);
break;
}
default: