X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fxfrm%2Fxfrm_policy.c;h=bae94a8031a290d5138cb99c60f007026c11d548;hb=255333c1db3ec63921de29b134418a4e56e5921e;hp=47219f98053f86217027245dc81b42fbdf4d57e5;hpb=6208e77e7fa9e69f399fddc55b1cf9527fbde599;p=linux-2.6 diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 47219f9805..bae94a8031 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -46,6 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex); 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); @@ -208,6 +209,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp) policy = kzalloc(sizeof(struct xfrm_policy), gfp); if (policy) { + INIT_LIST_HEAD(&policy->bytype); INIT_HLIST_NODE(&policy->bydst); INIT_HLIST_NODE(&policy->byidx); rwlock_init(&policy->lock); @@ -230,6 +232,10 @@ void xfrm_policy_destroy(struct xfrm_policy *policy) 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); kfree(policy); } @@ -331,15 +337,31 @@ static void xfrm_dst_hash_transfer(struct hlist_head *list, struct hlist_head *ndsttable, unsigned int nhashmask) { - struct hlist_node *entry, *tmp; + struct hlist_node *entry, *tmp, *entry0 = NULL; struct xfrm_policy *pol; + unsigned int h0 = 0; +redo: hlist_for_each_entry_safe(pol, entry, tmp, list, bydst) { unsigned int h; h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, pol->family, nhashmask); - hlist_add_head(&pol->bydst, ndsttable+h); + if (!entry0) { + hlist_del(entry); + hlist_add_head(&pol->bydst, ndsttable+h); + h0 = h; + } else { + if (h != h0) + continue; + hlist_del(entry); + hlist_add_after(entry0, &pol->bydst); + } + entry0 = entry; + } + if (!hlist_empty(list)) { + entry0 = NULL; + goto redo; } } @@ -568,6 +590,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) 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]); write_unlock_bh(&xfrm_policy_lock); if (delpol) @@ -806,57 +829,60 @@ out: } EXPORT_SYMBOL(xfrm_policy_flush); -int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*), +int xfrm_policy_walk(struct xfrm_policy_walk *walk, + int (*func)(struct xfrm_policy *, int, int, void*), void *data) { - struct xfrm_policy *pol, *last = NULL; - struct hlist_node *entry; - int dir, last_dir = 0, count, error; + struct xfrm_policy *old, *pol, *last = NULL; + 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) + return 0; + + old = pol = walk->policy; + walk->policy = NULL; read_lock_bh(&xfrm_policy_lock); - count = 0; - for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) { - struct hlist_head *table = xfrm_policy_bydst[dir].table; - int i; + for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) { + if (walk->type != walk->cur_type && + walk->type != XFRM_POLICY_TYPE_ANY) + continue; - hlist_for_each_entry(pol, entry, - &xfrm_policy_inexact[dir], bydst) { - if (pol->type != type) + 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, last_dir % XFRM_POLICY_MAX, - count, data); - if (error) + error = func(last, xfrm_policy_id2dir(last->index), + walk->count, data); + if (error) { + xfrm_pol_hold(last); + walk->policy = last; goto out; - } - last = pol; - last_dir = dir; - count++; - } - for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) { - hlist_for_each_entry(pol, entry, table + i, bydst) { - if (pol->type != type) - continue; - if (last) { - error = func(last, last_dir % XFRM_POLICY_MAX, - count, data); - if (error) - goto out; } - last = pol; - last_dir = dir; - count++; } + last = pol; + walk->count++; } + pol = NULL; } - if (count == 0) { + if (walk->count == 0) { error = -ENOENT; goto out; } - error = func(last, last_dir % XFRM_POLICY_MAX, 0, data); + if (last) + error = func(last, xfrm_policy_id2dir(last->index), 0, data); out: read_unlock_bh(&xfrm_policy_lock); + if (old != NULL) + xfrm_pol_put(old); return error; } EXPORT_SYMBOL(xfrm_policy_walk); @@ -2349,6 +2375,9 @@ static void __init xfrm_policy_init(void) 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_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); register_netdevice_notifier(&xfrm_dev_notifier); }