#include <linux/spinlock.h>
#include <linux/rculist.h>
#include <net/netlink.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <linux/netfilter.h>
#include <linux/netfilter/x_tables.h>
static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
-static struct ip_set * __rcu *ip_set_list; /* all individual sets */
-static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */
+struct ip_set_net {
+ struct ip_set * __rcu *ip_set_list; /* all individual sets */
+ ip_set_id_t ip_set_max; /* max number of sets */
+ int is_deleted; /* deleted by ip_set_net_exit */
+};
+static int ip_set_net_id __read_mostly;
+
+static inline struct ip_set_net *ip_set_pernet(struct net *net)
+{
+ return net_generic(net, ip_set_net_id);
+}
#define IP_SET_INC 64
#define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0)
/* When the nfnl mutex is held: */
#define nfnl_dereference(p) \
rcu_dereference_protected(p, 1)
-#define nfnl_set(id) \
- nfnl_dereference(ip_set_list)[id]
+#define nfnl_set(inst, id) \
+ nfnl_dereference((inst)->ip_set_list)[id]
/*
* The set types are implemented in modules and registered set types
*/
static inline struct ip_set *
-ip_set_rcu_get(ip_set_id_t index)
+ip_set_rcu_get(struct net *net, ip_set_id_t index)
{
struct ip_set *set;
+ struct ip_set_net *inst = ip_set_pernet(net);
rcu_read_lock();
/* ip_set_list itself needs to be protected */
- set = rcu_dereference(ip_set_list)[index];
+ set = rcu_dereference(inst->ip_set_list)[index];
rcu_read_unlock();
return set;
ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_rcu_get(index);
+ struct ip_set *set = ip_set_rcu_get(
+ dev_net(par->in ? par->in : par->out), index);
int ret = 0;
BUG_ON(set == NULL);
ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_rcu_get(index);
+ struct ip_set *set = ip_set_rcu_get(
+ dev_net(par->in ? par->in : par->out), index);
int ret;
BUG_ON(set == NULL);
ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par, struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_rcu_get(index);
+ struct ip_set *set = ip_set_rcu_get(
+ dev_net(par->in ? par->in : par->out), index);
int ret = 0;
BUG_ON(set == NULL);
*
*/
ip_set_id_t
-ip_set_get_byname(const char *name, struct ip_set **set)
+ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
{
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
+ struct ip_set_net *inst = ip_set_pernet(net);
rcu_read_lock();
- for (i = 0; i < ip_set_max; i++) {
- s = rcu_dereference(ip_set_list)[i];
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = rcu_dereference(inst->ip_set_list)[i];
if (s != NULL && STREQ(s->name, name)) {
__ip_set_get(s);
index = i;
* to be valid, after calling this function.
*
*/
-void
-ip_set_put_byindex(ip_set_id_t index)
+
+static inline void
+__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
{
struct ip_set *set;
rcu_read_lock();
- set = rcu_dereference(ip_set_list)[index];
+ set = rcu_dereference(inst->ip_set_list)[index];
if (set != NULL)
__ip_set_put(set);
rcu_read_unlock();
}
+
+void
+ip_set_put_byindex(struct net *net, ip_set_id_t index)
+{
+ struct ip_set_net *inst = ip_set_pernet(net);
+
+ __ip_set_put_byindex(inst, index);
+}
EXPORT_SYMBOL_GPL(ip_set_put_byindex);
/*
*
*/
const char *
-ip_set_name_byindex(ip_set_id_t index)
+ip_set_name_byindex(struct net *net, ip_set_id_t index)
{
- const struct ip_set *set = ip_set_rcu_get(index);
+ const struct ip_set *set = ip_set_rcu_get(net, index);
BUG_ON(set == NULL);
BUG_ON(set->ref == 0);
* The nfnl mutex is used in the function.
*/
ip_set_id_t
-ip_set_nfnl_get(const char *name)
+ip_set_nfnl_get(struct net *net, const char *name)
{
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
+ struct ip_set_net *inst = ip_set_pernet(net);
nfnl_lock(NFNL_SUBSYS_IPSET);
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s != NULL && STREQ(s->name, name)) {
__ip_set_get(s);
index = i;
* The nfnl mutex is used in the function.
*/
ip_set_id_t
-ip_set_nfnl_get_byindex(ip_set_id_t index)
+ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
{
struct ip_set *set;
+ struct ip_set_net *inst = ip_set_pernet(net);
- if (index > ip_set_max)
+ if (index > inst->ip_set_max)
return IPSET_INVALID_ID;
nfnl_lock(NFNL_SUBSYS_IPSET);
- set = nfnl_set(index);
+ set = nfnl_set(inst, index);
if (set)
__ip_set_get(set);
else
* The nfnl mutex is used in the function.
*/
void
-ip_set_nfnl_put(ip_set_id_t index)
+ip_set_nfnl_put(struct net *net, ip_set_id_t index)
{
struct ip_set *set;
+ struct ip_set_net *inst = ip_set_pernet(net);
+
nfnl_lock(NFNL_SUBSYS_IPSET);
- set = nfnl_set(index);
- if (set != NULL)
- __ip_set_put(set);
+ if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
+ set = nfnl_set(inst, index);
+ if (set != NULL)
+ __ip_set_put(set);
+ }
nfnl_unlock(NFNL_SUBSYS_IPSET);
}
EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
};
static struct ip_set *
-find_set_and_id(const char *name, ip_set_id_t *id)
+find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id)
{
struct ip_set *set = NULL;
ip_set_id_t i;
*id = IPSET_INVALID_ID;
- for (i = 0; i < ip_set_max; i++) {
- set = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ set = nfnl_set(inst, i);
if (set != NULL && STREQ(set->name, name)) {
*id = i;
break;
}
static inline struct ip_set *
-find_set(const char *name)
+find_set(struct ip_set_net *inst, const char *name)
{
ip_set_id_t id;
- return find_set_and_id(name, &id);
+ return find_set_and_id(inst, name, &id);
}
static int
-find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set)
+find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
+ struct ip_set **set)
{
struct ip_set *s;
ip_set_id_t i;
*index = IPSET_INVALID_ID;
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s == NULL) {
if (*index == IPSET_INVALID_ID)
*index = i;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct net *net = sock_net(ctnl);
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *clash = NULL;
ip_set_id_t index = IPSET_INVALID_ID;
struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {};
goto put_out;
}
- ret = set->type->create(set, tb, flags);
+ ret = set->type->create(net, set, tb, flags);
if (ret != 0)
goto put_out;
* by the nfnl mutex. Find the first free index in ip_set_list
* and check clashing.
*/
- ret = find_free_id(set->name, &index, &clash);
+ ret = find_free_id(inst, set->name, &index, &clash);
if (ret == -EEXIST) {
/* If this is the same set and requested, ignore error */
if ((flags & IPSET_FLAG_EXIST) &&
goto cleanup;
} else if (ret == -IPSET_ERR_MAX_SETS) {
struct ip_set **list, **tmp;
- ip_set_id_t i = ip_set_max + IP_SET_INC;
+ ip_set_id_t i = inst->ip_set_max + IP_SET_INC;
- if (i < ip_set_max || i == IPSET_INVALID_ID)
+ if (i < inst->ip_set_max || i == IPSET_INVALID_ID)
/* Wraparound */
goto cleanup;
if (!list)
goto cleanup;
/* nfnl mutex is held, both lists are valid */
- tmp = nfnl_dereference(ip_set_list);
- memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max);
- rcu_assign_pointer(ip_set_list, list);
+ tmp = nfnl_dereference(inst->ip_set_list);
+ memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
+ rcu_assign_pointer(inst->ip_set_list, list);
/* Make sure all current packets have passed through */
synchronize_net();
/* Use new list */
- index = ip_set_max;
- ip_set_max = i;
+ index = inst->ip_set_max;
+ inst->ip_set_max = i;
kfree(tmp);
ret = 0;
} else if (ret)
* Finally! Add our shiny new set to the list, and be done.
*/
pr_debug("create: '%s' created with index %u!\n", set->name, index);
- nfnl_set(index) = set;
+ nfnl_set(inst, index) = set;
return ret;
};
static void
-ip_set_destroy_set(ip_set_id_t index)
+ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index)
{
- struct ip_set *set = nfnl_set(index);
+ struct ip_set *set = nfnl_set(inst, index);
pr_debug("set: %s\n", set->name);
- nfnl_set(index) = NULL;
+ nfnl_set(inst, index) = NULL;
/* Must call it without holding any lock */
set->variant->destroy(set);
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *s;
ip_set_id_t i;
int ret = 0;
*/
read_lock_bh(&ip_set_ref_lock);
if (!attr[IPSET_ATTR_SETNAME]) {
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s != NULL && s->ref) {
ret = -IPSET_ERR_BUSY;
goto out;
}
}
read_unlock_bh(&ip_set_ref_lock);
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s != NULL)
- ip_set_destroy_set(i);
+ ip_set_destroy_set(inst, i);
}
} else {
- s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i);
+ s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+ &i);
if (s == NULL) {
ret = -ENOENT;
goto out;
}
read_unlock_bh(&ip_set_ref_lock);
- ip_set_destroy_set(i);
+ ip_set_destroy_set(inst, i);
}
return 0;
out:
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *s;
ip_set_id_t i;
return -IPSET_ERR_PROTOCOL;
if (!attr[IPSET_ATTR_SETNAME]) {
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s != NULL)
ip_set_flush_set(s);
}
} else {
- s = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (s == NULL)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set, *s;
const char *name2;
ip_set_id_t i;
attr[IPSET_ATTR_SETNAME2] == NULL))
return -IPSET_ERR_PROTOCOL;
- set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -ENOENT;
}
name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
- for (i = 0; i < ip_set_max; i++) {
- s = nfnl_set(i);
+ for (i = 0; i < inst->ip_set_max; i++) {
+ s = nfnl_set(inst, i);
if (s != NULL && STREQ(s->name, name2)) {
ret = -IPSET_ERR_EXIST_SETNAME2;
goto out;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *from, *to;
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];
attr[IPSET_ATTR_SETNAME2] == NULL))
return -IPSET_ERR_PROTOCOL;
- from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id);
+ from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+ &from_id);
if (from == NULL)
return -ENOENT;
- to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id);
+ to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]),
+ &to_id);
if (to == NULL)
return -IPSET_ERR_EXIST_SETNAME2;
write_lock_bh(&ip_set_ref_lock);
swap(from->ref, to->ref);
- nfnl_set(from_id) = to;
- nfnl_set(to_id) = from;
+ nfnl_set(inst, from_id) = to;
+ nfnl_set(inst, to_id) = from;
write_unlock_bh(&ip_set_ref_lock);
return 0;
static int
ip_set_dump_done(struct netlink_callback *cb)
{
+ struct ip_set_net *inst = (struct ip_set_net *)cb->data;
if (cb->args[2]) {
- pr_debug("release set %s\n", nfnl_set(cb->args[1])->name);
- ip_set_put_byindex((ip_set_id_t) cb->args[1]);
+ pr_debug("release set %s\n", nfnl_set(inst, cb->args[1])->name);
+ __ip_set_put_byindex(inst, (ip_set_id_t) cb->args[1]);
}
return 0;
}
struct nlattr *attr = (void *)nlh + min_len;
u32 dump_type;
ip_set_id_t index;
+ struct ip_set_net *inst = (struct ip_set_net *)cb->data;
/* Second pass, so parser can't fail */
nla_parse(cda, IPSET_ATTR_CMD_MAX,
if (cda[IPSET_ATTR_SETNAME]) {
struct ip_set *set;
- set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]),
+ set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
&index);
if (set == NULL)
return -ENOENT;
unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0;
u32 dump_type, dump_flags;
int ret = 0;
+ struct ip_set_net *inst = (struct ip_set_net *)cb->data;
if (!cb->args[0]) {
ret = dump_init(cb);
}
}
- if (cb->args[1] >= ip_set_max)
+ if (cb->args[1] >= inst->ip_set_max)
goto out;
dump_type = DUMP_TYPE(cb->args[0]);
dump_flags = DUMP_FLAGS(cb->args[0]);
- max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+ max = dump_type == DUMP_ONE ? cb->args[1] + 1 : inst->ip_set_max;
dump_last:
pr_debug("args[0]: %u %u args[1]: %ld\n",
dump_type, dump_flags, cb->args[1]);
for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1];
- set = nfnl_set(index);
+ set = nfnl_set(inst, index);
if (set == NULL) {
if (dump_type == DUMP_ONE) {
ret = -ENOENT;
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[2]) {
- pr_debug("release set %s\n", nfnl_set(index)->name);
- ip_set_put_byindex(index);
+ pr_debug("release set %s\n", nfnl_set(inst, index)->name);
+ __ip_set_put_byindex(inst, index);
cb->args[2] = 0;
}
out:
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
struct netlink_dump_control c = {
.dump = ip_set_dump_start,
.done = ip_set_dump_done,
+ .data = (void *)inst
};
return netlink_dump_start(ctnl, skb, nlh, &c);
}
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
const struct nlattr *nla;
attr[IPSET_ATTR_LINENO] == NULL))))
return -IPSET_ERR_PROTOCOL;
- set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
const struct nlattr *nla;
attr[IPSET_ATTR_LINENO] == NULL))))
return -IPSET_ERR_PROTOCOL;
- set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
int ret = 0;
!flag_nested(attr[IPSET_ATTR_DATA])))
return -IPSET_ERR_PROTOCOL;
- set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
const struct ip_set *set;
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
attr[IPSET_ATTR_SETNAME] == NULL))
return -IPSET_ERR_PROTOCOL;
- set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
if (set == NULL)
return -ENOENT;
unsigned int *op;
void *data;
int copylen = *len, ret = 0;
+ struct net *net = sock_net(sk);
+ struct ip_set_net *inst = ip_set_pernet(net);
- if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (optval != SO_IP_SET)
return -EBADF;
}
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(NFNL_SUBSYS_IPSET);
- find_set_and_id(req_get->set.name, &id);
+ find_set_and_id(inst, req_get->set.name, &id);
req_get->set.index = id;
nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(NFNL_SUBSYS_IPSET);
- find_set_and_id(req_get->set.name, &id);
+ find_set_and_id(inst, req_get->set.name, &id);
req_get->set.index = id;
if (id != IPSET_INVALID_ID)
- req_get->family = nfnl_set(id)->family;
+ req_get->family = nfnl_set(inst, id)->family;
nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
struct ip_set *set;
if (*len != sizeof(struct ip_set_req_get_set) ||
- req_get->set.index >= ip_set_max) {
+ req_get->set.index >= inst->ip_set_max) {
ret = -EINVAL;
goto done;
}
nfnl_lock(NFNL_SUBSYS_IPSET);
- set = nfnl_set(req_get->set.index);
+ set = nfnl_set(inst, req_get->set.index);
strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN);
nfnl_unlock(NFNL_SUBSYS_IPSET);
.owner = THIS_MODULE,
};
-static int __init
-ip_set_init(void)
+static int __net_init
+ip_set_net_init(struct net *net)
{
+ struct ip_set_net *inst = ip_set_pernet(net);
+
struct ip_set **list;
- int ret;
- if (max_sets)
- ip_set_max = max_sets;
- if (ip_set_max >= IPSET_INVALID_ID)
- ip_set_max = IPSET_INVALID_ID - 1;
+ inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX;
+ if (inst->ip_set_max >= IPSET_INVALID_ID)
+ inst->ip_set_max = IPSET_INVALID_ID - 1;
- list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
+ list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL);
if (!list)
return -ENOMEM;
+ inst->is_deleted = 0;
+ rcu_assign_pointer(inst->ip_set_list, list);
+ pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+ return 0;
+}
+
+static void __net_exit
+ip_set_net_exit(struct net *net)
+{
+ struct ip_set_net *inst = ip_set_pernet(net);
+
+ struct ip_set *set = NULL;
+ ip_set_id_t i;
+
+ inst->is_deleted = 1; /* flag for ip_set_nfnl_put */
+
+ for (i = 0; i < inst->ip_set_max; i++) {
+ set = nfnl_set(inst, i);
+ if (set != NULL)
+ ip_set_destroy_set(inst, i);
+ }
+ kfree(rcu_dereference_protected(inst->ip_set_list, 1));
+}
+
+static struct pernet_operations ip_set_net_ops = {
+ .init = ip_set_net_init,
+ .exit = ip_set_net_exit,
+ .id = &ip_set_net_id,
+ .size = sizeof(struct ip_set_net)
+};
+
- rcu_assign_pointer(ip_set_list, list);
- ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
+static int __init
+ip_set_init(void)
+{
+ int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
if (ret != 0) {
pr_err("ip_set: cannot register with nfnetlink.\n");
- kfree(list);
return ret;
}
ret = nf_register_sockopt(&so_set);
if (ret != 0) {
pr_err("SO_SET registry failed: %d\n", ret);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
- kfree(list);
return ret;
}
-
- pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+ ret = register_pernet_subsys(&ip_set_net_ops);
+ if (ret) {
+ pr_err("ip_set: cannot register pernet_subsys.\n");
+ nf_unregister_sockopt(&so_set);
+ nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
+ return ret;
+ }
return 0;
}
static void __exit
ip_set_fini(void)
{
- struct ip_set **list = rcu_dereference_protected(ip_set_list, 1);
-
- /* There can't be any existing set */
+ unregister_pernet_subsys(&ip_set_net_ops);
nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
- kfree(list);
pr_debug("these are the famous last words\n");
}
struct list_set {
u32 size; /* size of set list array */
struct timer_list gc; /* garbage collection */
+ struct net *net; /* namespace */
struct set_elem members[0]; /* the set members */
};
if (e->id != IPSET_INVALID_ID) {
if (i == map->size - 1) {
/* Last element replaced: e.g. add new,before,last */
- ip_set_put_byindex(e->id);
+ ip_set_put_byindex(map->net, e->id);
ip_set_ext_destroy(set, e);
} else {
struct set_elem *x = list_set_elem(set, map,
/* Last element pushed off */
if (x->id != IPSET_INVALID_ID) {
- ip_set_put_byindex(x->id);
+ ip_set_put_byindex(map->net, x->id);
ip_set_ext_destroy(set, x);
}
memmove(list_set_elem(set, map, i + 1), e,
struct list_set *map = set->data;
struct set_elem *e = list_set_elem(set, map, i);
- ip_set_put_byindex(e->id);
+ ip_set_put_byindex(map->net, e->id);
ip_set_ext_destroy(set, e);
if (i < map->size - 1)
if (SET_WITH_COMMENT(set))
ip_set_init_comment(ext_comment(e, set), ext);
/* Set is already added to the list */
- ip_set_put_byindex(d->id);
+ ip_set_put_byindex(map->net, d->id);
return 0;
}
insert:
list_set_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
+ struct list_set *map = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
ret = ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
- e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+ e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
if (e.id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
/* "Loop detection" */
}
if (tb[IPSET_ATTR_NAMEREF]) {
- e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+ e.refid = ip_set_get_byname(map->net,
+ nla_data(tb[IPSET_ATTR_NAMEREF]),
&s);
if (e.refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;
finish:
if (e.refid != IPSET_INVALID_ID)
- ip_set_put_byindex(e.refid);
+ ip_set_put_byindex(map->net, e.refid);
if (adt != IPSET_ADD || ret)
- ip_set_put_byindex(e.id);
+ ip_set_put_byindex(map->net, e.id);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
for (i = 0; i < map->size; i++) {
e = list_set_elem(set, map, i);
if (e->id != IPSET_INVALID_ID) {
- ip_set_put_byindex(e->id);
+ ip_set_put_byindex(map->net, e->id);
ip_set_ext_destroy(set, e);
e->id = IPSET_INVALID_ID;
}
goto nla_put_failure;
}
if (nla_put_string(skb, IPSET_ATTR_NAME,
- ip_set_name_byindex(e->id)))
+ ip_set_name_byindex(map->net, e->id)))
goto nla_put_failure;
if (ip_set_put_extensions(skb, set, e, true))
goto nla_put_failure;
/* Create list:set type of sets */
static bool
-init_list_set(struct ip_set *set, u32 size)
+init_list_set(struct net *net, struct ip_set *set, u32 size)
{
struct list_set *map;
struct set_elem *e;
return false;
map->size = size;
+ map->net = net;
set->data = map;
for (i = 0; i < size; i++) {
}
static int
-list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+ u32 flags)
{
u32 size = IP_SET_LIST_DEFAULT_SIZE;
set->variant = &set_variant;
set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem));
- if (!init_list_set(set, size))
+ if (!init_list_set(net, set, size))
return -ENOMEM;
if (tb[IPSET_ATTR_TIMEOUT]) {
set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
struct xt_set_info_match_v0 *info = par->matchinfo;
ip_set_id_t index;
- index = ip_set_nfnl_get_byindex(info->match_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n",
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
- ip_set_nfnl_put(info->match_set.index);
+ ip_set_nfnl_put(par->net, info->match_set.index);
return -ERANGE;
}
{
struct xt_set_info_match_v0 *info = par->matchinfo;
- ip_set_nfnl_put(info->match_set.index);
+ ip_set_nfnl_put(par->net, info->match_set.index);
}
/* Revision 1 match */
struct xt_set_info_match_v1 *info = par->matchinfo;
ip_set_id_t index;
- index = ip_set_nfnl_get_byindex(info->match_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find set indentified by id %u to match\n",
if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
- ip_set_nfnl_put(info->match_set.index);
+ ip_set_nfnl_put(par->net, info->match_set.index);
return -ERANGE;
}
{
struct xt_set_info_match_v1 *info = par->matchinfo;
- ip_set_nfnl_put(info->match_set.index);
+ ip_set_nfnl_put(par->net, info->match_set.index);
}
/* Revision 3 match */
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
- index = ip_set_nfnl_get_byindex(info->add_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
}
if (info->del_set.index != IPSET_INVALID_ID) {
- index = ip_set_nfnl_get_byindex(info->del_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
return -ENOENT;
}
}
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->del_set.index);
+ ip_set_nfnl_put(par->net, info->del_set.index);
return -ERANGE;
}
const struct xt_set_info_target_v0 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->del_set.index);
+ ip_set_nfnl_put(par->net, info->del_set.index);
}
/* Revision 1 target */
ip_set_id_t index;
if (info->add_set.index != IPSET_INVALID_ID) {
- index = ip_set_nfnl_get_byindex(info->add_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find add_set index %u as target\n",
info->add_set.index);
}
if (info->del_set.index != IPSET_INVALID_ID) {
- index = ip_set_nfnl_get_byindex(info->del_set.index);
+ index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
return -ENOENT;
}
}
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->del_set.index);
+ ip_set_nfnl_put(par->net, info->del_set.index);
return -ERANGE;
}
const struct xt_set_info_target_v1 *info = par->targinfo;
if (info->add_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->add_set.index);
+ ip_set_nfnl_put(par->net, info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
- ip_set_nfnl_put(info->del_set.index);
+ ip_set_nfnl_put(par->net, info->del_set.index);
}
/* Revision 2 target */