static DEFINE_RWLOCK(xfrm_policy_lock);
-static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX];
unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
EXPORT_SYMBOL(xfrm_policy_count);
static struct kmem_cache *xfrm_dst_cache __read_mostly;
-static struct work_struct xfrm_policy_gc_work;
static HLIST_HEAD(xfrm_policy_gc_list);
static DEFINE_SPINLOCK(xfrm_policy_gc_lock);
read_lock(&xp->lock);
- if (xp->dead)
+ if (xp->walk.dead)
goto out;
dir = xfrm_policy_id2dir(xp->index);
* SPD calls.
*/
-struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
+struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
{
struct xfrm_policy *policy;
policy = kzalloc(sizeof(struct xfrm_policy), gfp);
if (policy) {
- INIT_LIST_HEAD(&policy->bytype);
+ write_pnet(&policy->xp_net, net);
+ INIT_LIST_HEAD(&policy->walk.all);
INIT_HLIST_NODE(&policy->bydst);
INIT_HLIST_NODE(&policy->byidx);
rwlock_init(&policy->lock);
void xfrm_policy_destroy(struct xfrm_policy *policy)
{
- BUG_ON(!policy->dead);
+ BUG_ON(!policy->walk.dead);
BUG_ON(policy->bundles);
if (del_timer(&policy->timer))
BUG();
- write_lock_bh(&xfrm_policy_lock);
- list_del(&policy->bytype);
- write_unlock_bh(&xfrm_policy_lock);
-
security_xfrm_policy_free(policy->security);
kfree(policy);
}
hlist_for_each_entry_safe(policy, entry, tmp, &gc_list, bydst)
xfrm_policy_gc_kill(policy);
}
+static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task);
/* Rule must be locked. Release descentant resources, announce
* entry dead. The rule must be unlinked from lists to the moment.
int dead;
write_lock_bh(&policy->lock);
- dead = policy->dead;
- policy->dead = 1;
+ dead = policy->walk.dead;
+ policy->walk.dead = 1;
write_unlock_bh(&policy->lock);
if (unlikely(dead)) {
return;
}
- spin_lock(&xfrm_policy_gc_lock);
+ spin_lock_bh(&xfrm_policy_gc_lock);
hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
- spin_unlock(&xfrm_policy_gc_lock);
+ spin_unlock_bh(&xfrm_policy_gc_lock);
schedule_work(&xfrm_policy_gc_work);
}
static struct hlist_head xfrm_policy_inexact[XFRM_POLICY_MAX*2];
static struct xfrm_policy_hash xfrm_policy_bydst[XFRM_POLICY_MAX*2] __read_mostly;
-static struct hlist_head *xfrm_policy_byidx __read_mostly;
static unsigned int xfrm_idx_hmask __read_mostly;
static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
unsigned int hmask = xfrm_idx_hmask;
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
- struct hlist_head *oidx = xfrm_policy_byidx;
+ struct hlist_head *oidx = init_net.xfrm.policy_byidx;
struct hlist_head *nidx = xfrm_hash_alloc(nsize);
int i;
for (i = hmask; i >= 0; i--)
xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
- xfrm_policy_byidx = nidx;
+ init_net.xfrm.policy_byidx = nidx;
xfrm_idx_hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
/* Generate new index... KAME seems to generate them ordered by cost
* of an absolute inpredictability of ordering of rules. This will not pass. */
-static u32 xfrm_gen_index(u8 type, int dir)
+static u32 xfrm_gen_index(int dir)
{
static u32 idx_generator;
idx_generator += 8;
if (idx == 0)
idx = 8;
- list = xfrm_policy_byidx + idx_hash(idx);
+ list = init_net.xfrm.policy_byidx + idx_hash(idx);
found = 0;
hlist_for_each_entry(p, entry, list, byidx) {
if (p->index == idx) {
if (delpol) {
hlist_del(&delpol->bydst);
hlist_del(&delpol->byidx);
+ list_del(&delpol->walk.all);
xfrm_policy_count[dir]--;
}
- policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
- hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
+ policy->index = delpol ? delpol->index : xfrm_gen_index(dir);
+ hlist_add_head(&policy->byidx, init_net.xfrm.policy_byidx+idx_hash(policy->index));
policy->curlft.add_time = get_seconds();
policy->curlft.use_time = 0;
if (!mod_timer(&policy->timer, jiffies + HZ))
xfrm_pol_hold(policy);
- list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]);
+ list_add(&policy->walk.all, &init_net.xfrm.policy_all);
write_unlock_bh(&xfrm_policy_lock);
if (delpol)
}
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
+ list_del(&pol->walk.all);
xfrm_policy_count[dir]--;
}
ret = pol;
*err = 0;
write_lock_bh(&xfrm_policy_lock);
- chain = xfrm_policy_byidx + idx_hash(id);
+ chain = init_net.xfrm.policy_byidx + idx_hash(id);
ret = NULL;
hlist_for_each_entry(pol, entry, chain, byidx) {
if (pol->type == type && pol->index == id) {
}
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
+ list_del(&pol->walk.all);
xfrm_policy_count[dir]--;
}
ret = pol;
continue;
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
+ list_del(&pol->walk.all);
write_unlock_bh(&xfrm_policy_lock);
xfrm_audit_policy_delete(pol, 1,
int (*func)(struct xfrm_policy *, int, int, void*),
void *data)
{
- struct xfrm_policy *old, *pol, *last = NULL;
+ struct xfrm_policy *pol;
+ struct xfrm_policy_walk_entry *x;
int error = 0;
if (walk->type >= XFRM_POLICY_TYPE_MAX &&
walk->type != XFRM_POLICY_TYPE_ANY)
return -EINVAL;
- if (walk->policy == NULL && walk->count != 0)
+ if (list_empty(&walk->walk.all) && walk->seq != 0)
return 0;
- old = pol = walk->policy;
- walk->policy = NULL;
- read_lock_bh(&xfrm_policy_lock);
-
- for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) {
- if (walk->type != walk->cur_type &&
- walk->type != XFRM_POLICY_TYPE_ANY)
+ write_lock_bh(&xfrm_policy_lock);
+ if (list_empty(&walk->walk.all))
+ x = list_first_entry(&init_net.xfrm.policy_all, struct xfrm_policy_walk_entry, all);
+ else
+ x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all);
+ list_for_each_entry_from(x, &init_net.xfrm.policy_all, all) {
+ if (x->dead)
continue;
-
- if (pol == NULL) {
- pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type],
- struct xfrm_policy, bytype);
- }
- list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) {
- if (pol->dead)
- continue;
- if (last) {
- error = func(last, xfrm_policy_id2dir(last->index),
- walk->count, data);
- if (error) {
- xfrm_pol_hold(last);
- walk->policy = last;
- goto out;
- }
- }
- last = pol;
- walk->count++;
+ pol = container_of(x, struct xfrm_policy, walk);
+ if (walk->type != XFRM_POLICY_TYPE_ANY &&
+ walk->type != pol->type)
+ continue;
+ error = func(pol, xfrm_policy_id2dir(pol->index),
+ walk->seq, data);
+ if (error) {
+ list_move_tail(&walk->walk.all, &x->all);
+ goto out;
}
- pol = NULL;
+ walk->seq++;
}
- if (walk->count == 0) {
+ if (walk->seq == 0) {
error = -ENOENT;
goto out;
}
- if (last)
- error = func(last, xfrm_policy_id2dir(last->index), 0, data);
+ list_del_init(&walk->walk.all);
out:
- read_unlock_bh(&xfrm_policy_lock);
- if (old != NULL)
- xfrm_pol_put(old);
+ write_unlock_bh(&xfrm_policy_lock);
return error;
}
EXPORT_SYMBOL(xfrm_policy_walk);
+void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
+{
+ INIT_LIST_HEAD(&walk->walk.all);
+ walk->walk.dead = 1;
+ walk->type = type;
+ walk->seq = 0;
+}
+EXPORT_SYMBOL(xfrm_policy_walk_init);
+
+void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
+{
+ if (list_empty(&walk->walk.all))
+ return;
+
+ write_lock_bh(&xfrm_policy_lock);
+ list_del(&walk->walk.all);
+ write_unlock_bh(&xfrm_policy_lock);
+}
+EXPORT_SYMBOL(xfrm_policy_walk_done);
+
/*
* Find policy to apply to this flow.
*
struct hlist_head *chain = policy_hash_bysel(&pol->selector,
pol->family, dir);
- list_add_tail(&pol->bytype, &xfrm_policy_bytype[pol->type]);
+ list_add(&pol->walk.all, &init_net.xfrm.policy_all);
hlist_add_head(&pol->bydst, chain);
- hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
+ hlist_add_head(&pol->byidx, init_net.xfrm.policy_byidx+idx_hash(pol->index));
xfrm_policy_count[dir]++;
xfrm_pol_hold(pol);
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
+ list_del(&pol->walk.all);
xfrm_policy_count[dir]--;
return pol;
sk->sk_policy[dir] = pol;
if (pol) {
pol->curlft.add_time = get_seconds();
- pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir);
+ pol->index = xfrm_gen_index(XFRM_POLICY_MAX+dir);
__xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
}
if (old_pol)
static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir)
{
- struct xfrm_policy *newp = xfrm_policy_alloc(GFP_ATOMIC);
+ struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC);
if (newp) {
newp->selector = old->selector;
-EINVAL : -EAGAIN);
xfrm_state_put(x);
}
+ else if (error == -ESRCH)
+ error = -EAGAIN;
if (!tmpl->optional)
goto fail;
if (err == -EAGAIN && (flags & XFRM_LOOKUP_WAIT)) {
DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&km_waitq, &wait);
+ add_wait_queue(&init_net.xfrm.km_waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
- remove_wait_queue(&km_waitq, &wait);
+ remove_wait_queue(&init_net.xfrm.km_waitq, &wait);
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
for (pi = 0; pi < npols; pi++) {
read_lock_bh(&pols[pi]->lock);
- pol_dead |= pols[pi]->dead;
+ pol_dead |= pols[pi]->walk.dead;
read_unlock_bh(&pols[pi]->lock);
}
}
static struct notifier_block xfrm_dev_notifier = {
- xfrm_dev_event,
- NULL,
- 0
+ .notifier_call = xfrm_dev_event,
};
#ifdef CONFIG_XFRM_STATISTICS
}
#endif
-static void __init xfrm_policy_init(void)
+static int __net_init xfrm_policy_init(struct net *net)
{
unsigned int hmask, sz;
int dir;
- xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
+ if (net_eq(net, &init_net))
+ xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
sizeof(struct xfrm_dst),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
hmask = 8 - 1;
sz = (hmask+1) * sizeof(struct hlist_head);
- xfrm_policy_byidx = xfrm_hash_alloc(sz);
+ net->xfrm.policy_byidx = xfrm_hash_alloc(sz);
+ if (!net->xfrm.policy_byidx)
+ goto out_byidx;
xfrm_idx_hmask = hmask;
- if (!xfrm_policy_byidx)
- panic("XFRM: failed to allocate byidx hash\n");
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
struct xfrm_policy_hash *htab;
panic("XFRM: failed to allocate bydst hash\n");
}
- for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++)
- INIT_LIST_HEAD(&xfrm_policy_bytype[dir]);
+ INIT_LIST_HEAD(&net->xfrm.policy_all);
+ if (net_eq(net, &init_net))
+ register_netdevice_notifier(&xfrm_dev_notifier);
+ return 0;
+
+out_byidx:
+ return -ENOMEM;
+}
+
+static void xfrm_policy_fini(struct net *net)
+{
+ unsigned int sz;
+
+ WARN_ON(!list_empty(&net->xfrm.policy_all));
- INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
- register_netdevice_notifier(&xfrm_dev_notifier);
+ sz = (xfrm_idx_hmask + 1) * sizeof(struct hlist_head);
+ WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
+ xfrm_hash_free(net->xfrm.policy_byidx, sz);
}
+static int __net_init xfrm_net_init(struct net *net)
+{
+ int rv;
+
+ rv = xfrm_state_init(net);
+ if (rv < 0)
+ goto out_state;
+ rv = xfrm_policy_init(net);
+ if (rv < 0)
+ goto out_policy;
+ return 0;
+
+out_policy:
+ xfrm_state_fini(net);
+out_state:
+ return rv;
+}
+
+static void __net_exit xfrm_net_exit(struct net *net)
+{
+ xfrm_policy_fini(net);
+ xfrm_state_fini(net);
+}
+
+static struct pernet_operations __net_initdata xfrm_net_ops = {
+ .init = xfrm_net_init,
+ .exit = xfrm_net_exit,
+};
+
void __init xfrm_init(void)
{
+ register_pernet_subsys(&xfrm_net_ops);
#ifdef CONFIG_XFRM_STATISTICS
xfrm_statistics_init();
#endif
- xfrm_state_init();
- xfrm_policy_init();
xfrm_input_init();
#ifdef CONFIG_XFRM_STATISTICS
xfrm_proc_init();
switch(sel->family) {
case AF_INET:
- audit_log_format(audit_buf, " src=" NIPQUAD_FMT,
- NIPQUAD(sel->saddr.a4));
+ audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4);
if (sel->prefixlen_s != 32)
audit_log_format(audit_buf, " src_prefixlen=%d",
sel->prefixlen_s);
- audit_log_format(audit_buf, " dst=" NIPQUAD_FMT,
- NIPQUAD(sel->daddr.a4));
+ audit_log_format(audit_buf, " dst=%pI4", &sel->daddr.a4);
if (sel->prefixlen_d != 32)
audit_log_format(audit_buf, " dst_prefixlen=%d",
sel->prefixlen_d);
break;
case AF_INET6:
- audit_log_format(audit_buf, " src=" NIP6_FMT,
- NIP6(*(struct in6_addr *)sel->saddr.a6));
+ audit_log_format(audit_buf, " src=%pI6", sel->saddr.a6);
if (sel->prefixlen_s != 128)
audit_log_format(audit_buf, " src_prefixlen=%d",
sel->prefixlen_s);
- audit_log_format(audit_buf, " dst=" NIP6_FMT,
- NIP6(*(struct in6_addr *)sel->daddr.a6));
+ audit_log_format(audit_buf, " dst=%pI6", sel->daddr.a6);
if (sel->prefixlen_d != 128)
audit_log_format(audit_buf, " dst_prefixlen=%d",
sel->prefixlen_d);
int i, j, n = 0;
write_lock_bh(&pol->lock);
- if (unlikely(pol->dead)) {
+ if (unlikely(pol->walk.dead)) {
/* target policy has been deleted */
write_unlock_bh(&pol->lock);
return -ENOENT;
}
int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
- struct xfrm_migrate *m, int num_migrate)
+ struct xfrm_migrate *m, int num_migrate,
+ struct xfrm_kmaddress *k)
{
int i, err, nx_cur = 0, nx_new = 0;
struct xfrm_policy *pol = NULL;
}
/* Stage 5 - announce */
- km_migrate(sel, dir, type, m, num_migrate);
+ km_migrate(sel, dir, type, m, num_migrate, k);
xfrm_pol_put(pol);