#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/module.h>
-#include <linux/bootmem.h>
-#include <linux/vmalloc.h>
#include <linux/cache.h>
#include <net/xfrm.h>
#include <net/ip.h>
+#include "xfrm_hash.h"
+
DEFINE_MUTEX(xfrm_cfg_mutex);
EXPORT_SYMBOL(xfrm_cfg_mutex);
static unsigned int xfrm_idx_hmask __read_mostly;
static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
-static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
-{
- return (index ^ (index >> 8)) & hmask;
-}
-
static inline unsigned int idx_hash(u32 index)
{
return __idx_hash(index, xfrm_idx_hmask);
}
-static inline unsigned int __sel_hash(struct xfrm_selector *sel, unsigned short family, unsigned int hmask)
-{
- xfrm_address_t *daddr = &sel->daddr;
- xfrm_address_t *saddr = &sel->saddr;
- unsigned int h = 0;
-
- switch (family) {
- case AF_INET:
- if (sel->prefixlen_d != 32 ||
- sel->prefixlen_s != 32)
- return hmask + 1;
-
- h = ntohl(daddr->a4 ^ saddr->a4);
- break;
-
- case AF_INET6:
- if (sel->prefixlen_d != 128 ||
- sel->prefixlen_s != 128)
- return hmask + 1;
-
- h = ntohl(daddr->a6[2] ^ daddr->a6[3] ^
- saddr->a6[2] ^ saddr->a6[3]);
- break;
- };
- h ^= (h >> 16);
- return h & hmask;
-}
-
-static inline unsigned int __addr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, unsigned int hmask)
-{
- unsigned int h = 0;
-
- switch (family) {
- case AF_INET:
- h = ntohl(daddr->a4 ^ saddr->a4);
- break;
-
- case AF_INET6:
- h = ntohl(daddr->a6[2] ^ daddr->a6[3] ^
- saddr->a6[2] ^ saddr->a6[3]);
- break;
- };
- h ^= (h >> 16);
- return h & hmask;
-}
-
static struct hlist_head *policy_hash_bysel(struct xfrm_selector *sel, unsigned short family, int dir)
{
unsigned int hmask = xfrm_policy_bydst[dir].hmask;
return xfrm_policy_bydst[dir].table + hash;
}
-static struct hlist_head *xfrm_policy_hash_alloc(unsigned int sz)
-{
- struct hlist_head *n;
-
- if (sz <= PAGE_SIZE)
- n = kmalloc(sz, GFP_KERNEL);
- else if (hashdist)
- n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
- else
- n = (struct hlist_head *)
- __get_free_pages(GFP_KERNEL, get_order(sz));
-
- if (n)
- memset(n, 0, sz);
-
- return n;
-}
-
-static void xfrm_policy_hash_free(struct hlist_head *n, unsigned int sz)
-{
- if (sz <= PAGE_SIZE)
- kfree(n);
- else if (hashdist)
- vfree(n);
- else
- free_pages((unsigned long)n, get_order(sz));
-}
-
static void xfrm_dst_hash_transfer(struct hlist_head *list,
struct hlist_head *ndsttable,
unsigned int nhashmask)
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
struct hlist_head *odst = xfrm_policy_bydst[dir].table;
- struct hlist_head *ndst = xfrm_policy_hash_alloc(nsize);
+ struct hlist_head *ndst = xfrm_hash_alloc(nsize);
int i;
if (!ndst)
write_unlock_bh(&xfrm_policy_lock);
- xfrm_policy_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
+ xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
}
static void xfrm_byidx_resize(int total)
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 *nidx = xfrm_policy_hash_alloc(nsize);
+ struct hlist_head *nidx = xfrm_hash_alloc(nsize);
int i;
if (!nidx)
write_unlock_bh(&xfrm_policy_lock);
- xfrm_policy_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
+ xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
}
static inline int xfrm_bydst_should_resize(int dir, int *total)
xfrm_address_t *daddr, *saddr;
struct hlist_node *entry;
struct hlist_head *chain;
+ u32 priority = ~0U;
daddr = xfrm_flowi_daddr(fl, family);
saddr = xfrm_flowi_saddr(fl, family);
ret = NULL;
hlist_for_each_entry(pol, entry, chain, bydst) {
if (xfrm_policy_match(pol, fl, type, family, dir)) {
- xfrm_pol_hold(pol);
ret = pol;
+ priority = ret->priority;
break;
}
}
- if (!ret) {
- chain = &xfrm_policy_inexact[dir];
- hlist_for_each_entry(pol, entry, chain, bydst) {
- if (xfrm_policy_match(pol, fl, type, family, dir)) {
- xfrm_pol_hold(pol);
- ret = pol;
- break;
- }
+ chain = &xfrm_policy_inexact[dir];
+ hlist_for_each_entry(pol, entry, chain, bydst) {
+ if (xfrm_policy_match(pol, fl, type, family, dir) &&
+ pol->priority < priority) {
+ ret = pol;
+ break;
}
}
+ if (ret)
+ xfrm_pol_hold(ret);
read_unlock_bh(&xfrm_policy_lock);
return ret;
return 0;
}
+static int
+xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
+ unsigned short family)
+{
+ int err;
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ err = afinfo->get_saddr(local, remote);
+ xfrm_policy_put_afinfo(afinfo);
+ return err;
+}
+
/* Resolve list of templates for the flow, given policy. */
static int
int i, error;
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
+ xfrm_address_t tmp;
for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x;
if (tmpl->mode == XFRM_MODE_TUNNEL) {
remote = &tmpl->id.daddr;
local = &tmpl->saddr;
+ if (xfrm_addr_any(local, family)) {
+ error = xfrm_get_saddr(&tmp, remote, family);
+ if (error)
+ goto fail;
+ local = &tmp;
+ }
}
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
{
for (; k < sp->len; k++) {
if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) {
- if (idxp)
- *idxp = k;
+ *idxp = k;
return 1;
}
}
struct flowi fl;
u8 fl_dir = policy_to_flow_dir(dir);
int xerr_idx = -1;
- int *xerr_idxp = &xerr_idx;
if (xfrm_decode_session(skb, &fl, family) < 0)
return 0;
xfrm_policy_lookup);
if (!pol) {
- if (skb->sp && secpath_has_nontransport(skb->sp, 0, xerr_idxp)) {
+ if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
xfrm_secpath_reject(xerr_idx, skb, &fl);
return 0;
}
for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
k = xfrm_policy_ok(tpp[i], sp, k, family);
if (k < 0) {
- if (k < -1 && xerr_idxp)
- *xerr_idxp = -(2+k);
+ if (k < -1)
+ /* "-2 - errored_index" returned */
+ xerr_idx = -(2+k);
goto reject;
}
}
- if (secpath_has_nontransport(sp, k, xerr_idxp))
+ if (secpath_has_nontransport(sp, k, &xerr_idx))
goto reject;
xfrm_pols_put(pols, npols);
xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
sizeof(struct xfrm_dst),
- 0, SLAB_HWCACHE_ALIGN,
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL, NULL);
- if (!xfrm_dst_cache)
- panic("XFRM: failed to allocate xfrm_dst_cache\n");
hmask = 8 - 1;
sz = (hmask+1) * sizeof(struct hlist_head);
- xfrm_policy_byidx = xfrm_policy_hash_alloc(sz);
+ xfrm_policy_byidx = xfrm_hash_alloc(sz);
xfrm_idx_hmask = hmask;
if (!xfrm_policy_byidx)
panic("XFRM: failed to allocate byidx hash\n");
INIT_HLIST_HEAD(&xfrm_policy_inexact[dir]);
htab = &xfrm_policy_bydst[dir];
- htab->table = xfrm_policy_hash_alloc(sz);
+ htab->table = xfrm_hash_alloc(sz);
htab->hmask = hmask;
if (!htab->table)
panic("XFRM: failed to allocate bydst hash\n");