]> Git Repo - linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec...
authorDavid S. Miller <[email protected]>
Sun, 28 Sep 2014 21:19:15 +0000 (17:19 -0400)
committerDavid S. Miller <[email protected]>
Sun, 28 Sep 2014 21:19:15 +0000 (17:19 -0400)
Steffen Klassert says:

====================
pull request (net-next): ipsec-next 2014-09-25

1) Remove useless hash_resize_mutex in xfrm_hash_resize().
   This mutex is used only there, but xfrm_hash_resize()
   can't be called concurrently at all. From Ying Xue.

2) Extend policy hashing to prefixed policies based on
   prefix lenght thresholds. From Christophe Gouault.

3) Make the policy hash table thresholds configurable
   via netlink. From Christophe Gouault.

4) Remove the maximum authentication length for AH.
   This was needed to limit stack usage. We switched
   already to allocate space, so no need to keep the
   limit. From Herbert Xu.
====================

Signed-off-by: David S. Miller <[email protected]>
1  2 
net/xfrm/xfrm_policy.c

diff --combined net/xfrm/xfrm_policy.c
index fdde51f4271adf22b26e36c46f76c2b93c4ce56e,55bcb8604bc6de77033280082176ed68f9dbc666..f623dca6ce30f49c65938364e5f1785ee0be416c
  #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))
  #define XFRM_MAX_QUEUE_LEN    100
  
 +struct xfrm_flo {
 +      struct dst_entry *dst_orig;
 +      u8 flags;
 +};
 +
  static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
  static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]
                                                __read_mostly;
@@@ -349,12 -344,39 +349,39 @@@ static inline unsigned int idx_hash(str
        return __idx_hash(index, net->xfrm.policy_idx_hmask);
  }
  
+ /* calculate policy hash thresholds */
+ static void __get_hash_thresh(struct net *net,
+                             unsigned short family, int dir,
+                             u8 *dbits, u8 *sbits)
+ {
+       switch (family) {
+       case AF_INET:
+               *dbits = net->xfrm.policy_bydst[dir].dbits4;
+               *sbits = net->xfrm.policy_bydst[dir].sbits4;
+               break;
+       case AF_INET6:
+               *dbits = net->xfrm.policy_bydst[dir].dbits6;
+               *sbits = net->xfrm.policy_bydst[dir].sbits6;
+               break;
+       default:
+               *dbits = 0;
+               *sbits = 0;
+       }
+ }
  static struct hlist_head *policy_hash_bysel(struct net *net,
                                            const struct xfrm_selector *sel,
                                            unsigned short family, int dir)
  {
        unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
-       unsigned int hash = __sel_hash(sel, family, hmask);
+       unsigned int hash;
+       u8 dbits;
+       u8 sbits;
+       __get_hash_thresh(net, family, dir, &dbits, &sbits);
+       hash = __sel_hash(sel, family, hmask, dbits, sbits);
  
        return (hash == hmask + 1 ?
                &net->xfrm.policy_inexact[dir] :
@@@ -367,25 -389,35 +394,35 @@@ static struct hlist_head *policy_hash_d
                                             unsigned short family, int dir)
  {
        unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
-       unsigned int hash = __addr_hash(daddr, saddr, family, hmask);
+       unsigned int hash;
+       u8 dbits;
+       u8 sbits;
+       __get_hash_thresh(net, family, dir, &dbits, &sbits);
+       hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits);
  
        return net->xfrm.policy_bydst[dir].table + hash;
  }
  
- static void xfrm_dst_hash_transfer(struct hlist_head *list,
+ static void xfrm_dst_hash_transfer(struct net *net,
+                                  struct hlist_head *list,
                                   struct hlist_head *ndsttable,
-                                  unsigned int nhashmask)
+                                  unsigned int nhashmask,
+                                  int dir)
  {
        struct hlist_node *tmp, *entry0 = NULL;
        struct xfrm_policy *pol;
        unsigned int h0 = 0;
+       u8 dbits;
+       u8 sbits;
  
  redo:
        hlist_for_each_entry_safe(pol, tmp, list, bydst) {
                unsigned int h;
  
+               __get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
                h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
-                               pol->family, nhashmask);
+                               pol->family, nhashmask, dbits, sbits);
                if (!entry0) {
                        hlist_del(&pol->bydst);
                        hlist_add_head(&pol->bydst, ndsttable+h);
@@@ -439,7 -471,7 +476,7 @@@ static void xfrm_bydst_resize(struct ne
        write_lock_bh(&net->xfrm.xfrm_policy_lock);
  
        for (i = hmask; i >= 0; i--)
-               xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
+               xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir);
  
        net->xfrm.policy_bydst[dir].table = ndst;
        net->xfrm.policy_bydst[dir].hmask = nhashmask;
@@@ -534,6 -566,86 +571,86 @@@ static void xfrm_hash_resize(struct wor
        mutex_unlock(&hash_resize_mutex);
  }
  
+ static void xfrm_hash_rebuild(struct work_struct *work)
+ {
+       struct net *net = container_of(work, struct net,
+                                      xfrm.policy_hthresh.work);
+       unsigned int hmask;
+       struct xfrm_policy *pol;
+       struct xfrm_policy *policy;
+       struct hlist_head *chain;
+       struct hlist_head *odst;
+       struct hlist_node *newpos;
+       int i;
+       int dir;
+       unsigned seq;
+       u8 lbits4, rbits4, lbits6, rbits6;
+       mutex_lock(&hash_resize_mutex);
+       /* read selector prefixlen thresholds */
+       do {
+               seq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
+               lbits4 = net->xfrm.policy_hthresh.lbits4;
+               rbits4 = net->xfrm.policy_hthresh.rbits4;
+               lbits6 = net->xfrm.policy_hthresh.lbits6;
+               rbits6 = net->xfrm.policy_hthresh.rbits6;
+       } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
+       write_lock_bh(&net->xfrm.xfrm_policy_lock);
+       /* reset the bydst and inexact table in all directions */
+       for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+               INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
+               hmask = net->xfrm.policy_bydst[dir].hmask;
+               odst = net->xfrm.policy_bydst[dir].table;
+               for (i = hmask; i >= 0; i--)
+                       INIT_HLIST_HEAD(odst + i);
+               if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) {
+                       /* dir out => dst = remote, src = local */
+                       net->xfrm.policy_bydst[dir].dbits4 = rbits4;
+                       net->xfrm.policy_bydst[dir].sbits4 = lbits4;
+                       net->xfrm.policy_bydst[dir].dbits6 = rbits6;
+                       net->xfrm.policy_bydst[dir].sbits6 = lbits6;
+               } else {
+                       /* dir in/fwd => dst = local, src = remote */
+                       net->xfrm.policy_bydst[dir].dbits4 = lbits4;
+                       net->xfrm.policy_bydst[dir].sbits4 = rbits4;
+                       net->xfrm.policy_bydst[dir].dbits6 = lbits6;
+                       net->xfrm.policy_bydst[dir].sbits6 = rbits6;
+               }
+       }
+       /* re-insert all policies by order of creation */
+       list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
+               newpos = NULL;
+               chain = policy_hash_bysel(net, &policy->selector,
+                                         policy->family,
+                                         xfrm_policy_id2dir(policy->index));
+               hlist_for_each_entry(pol, chain, bydst) {
+                       if (policy->priority >= pol->priority)
+                               newpos = &pol->bydst;
+                       else
+                               break;
+               }
+               if (newpos)
+                       hlist_add_behind(&policy->bydst, newpos);
+               else
+                       hlist_add_head(&policy->bydst, chain);
+       }
+       write_unlock_bh(&net->xfrm.xfrm_policy_lock);
+       mutex_unlock(&hash_resize_mutex);
+ }
+ void xfrm_policy_hash_rebuild(struct net *net)
+ {
+       schedule_work(&net->xfrm.policy_hthresh.work);
+ }
+ EXPORT_SYMBOL(xfrm_policy_hash_rebuild);
  /* 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(struct net *net, int dir, u32 index)
@@@ -1882,14 -1994,13 +1999,14 @@@ static int xdst_queue_output(struct soc
  }
  
  static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
 -                                               struct dst_entry *dst,
 +                                               struct xfrm_flo *xflo,
                                                 const struct flowi *fl,
                                                 int num_xfrms,
                                                 u16 family)
  {
        int err;
        struct net_device *dev;
 +      struct dst_entry *dst;
        struct dst_entry *dst1;
        struct xfrm_dst *xdst;
  
        if (IS_ERR(xdst))
                return xdst;
  
 -      if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0)
 +      if (!(xflo->flags & XFRM_LOOKUP_QUEUE) ||
 +          net->xfrm.sysctl_larval_drop ||
 +          num_xfrms <= 0)
                return xdst;
  
 +      dst = xflo->dst_orig;
        dst1 = &xdst->u.dst;
        dst_hold(dst);
        xdst->route = dst;
@@@ -1944,7 -2052,7 +2061,7 @@@ static struct flow_cache_object 
  xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
                   struct flow_cache_object *oldflo, void *ctx)
  {
 -      struct dst_entry *dst_orig = (struct dst_entry *)ctx;
 +      struct xfrm_flo *xflo = (struct xfrm_flo *)ctx;
        struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
        struct xfrm_dst *xdst, *new_xdst;
        int num_pols = 0, num_xfrms = 0, i, err, pol_dead;
                        goto make_dummy_bundle;
        }
  
 -      new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, dst_orig);
 +      new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family,
 +                                                xflo->dst_orig);
        if (IS_ERR(new_xdst)) {
                err = PTR_ERR(new_xdst);
                if (err != -EAGAIN)
@@@ -2020,7 -2127,7 +2137,7 @@@ make_dummy_bundle
        /* We found policies, but there's no bundles to instantiate:
         * either because the policy blocks, has no transformations or
         * we could not build template (no xfrm_states).*/
 -      xdst = xfrm_create_dummy_bundle(net, dst_orig, fl, num_xfrms, family);
 +      xdst = xfrm_create_dummy_bundle(net, xflo, fl, num_xfrms, family);
        if (IS_ERR(xdst)) {
                xfrm_pols_put(pols, num_pols);
                return ERR_CAST(xdst);
@@@ -2114,18 -2221,13 +2231,18 @@@ struct dst_entry *xfrm_lookup(struct ne
        }
  
        if (xdst == NULL) {
 +              struct xfrm_flo xflo;
 +
 +              xflo.dst_orig = dst_orig;
 +              xflo.flags = flags;
 +
                /* To accelerate a bit...  */
                if ((dst_orig->flags & DST_NOXFRM) ||
                    !net->xfrm.policy_count[XFRM_POLICY_OUT])
                        goto nopol;
  
                flo = flow_cache_lookup(net, fl, family, dir,
 -                                      xfrm_bundle_lookup, dst_orig);
 +                                      xfrm_bundle_lookup, &xflo);
                if (flo == NULL)
                        goto nopol;
                if (IS_ERR(flo)) {
                        xfrm_pols_put(pols, drop_pols);
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
  
 -                      return make_blackhole(net, family, dst_orig);
 +                      return ERR_PTR(-EREMOTE);
                }
  
                err = -EAGAIN;
@@@ -2210,23 -2312,6 +2327,23 @@@ dropdst
  }
  EXPORT_SYMBOL(xfrm_lookup);
  
 +/* Callers of xfrm_lookup_route() must ensure a call to dst_output().
 + * Otherwise we may send out blackholed packets.
 + */
 +struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
 +                                  const struct flowi *fl,
 +                                  struct sock *sk, int flags)
 +{
 +      struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk,
 +                                          flags | XFRM_LOOKUP_QUEUE);
 +
 +      if (IS_ERR(dst) && PTR_ERR(dst) == -EREMOTE)
 +              return make_blackhole(net, dst_orig->ops->family, dst_orig);
 +
 +      return dst;
 +}
 +EXPORT_SYMBOL(xfrm_lookup_route);
 +
  static inline int
  xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl)
  {
@@@ -2492,7 -2577,7 +2609,7 @@@ int __xfrm_route_forward(struct sk_buf
  
        skb_dst_force(skb);
  
 -      dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0);
 +      dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE);
        if (IS_ERR(dst)) {
                res = 0;
                dst = NULL;
@@@ -2862,10 -2947,21 +2979,21 @@@ static int __net_init xfrm_policy_init(
                if (!htab->table)
                        goto out_bydst;
                htab->hmask = hmask;
+               htab->dbits4 = 32;
+               htab->sbits4 = 32;
+               htab->dbits6 = 128;
+               htab->sbits6 = 128;
        }
+       net->xfrm.policy_hthresh.lbits4 = 32;
+       net->xfrm.policy_hthresh.rbits4 = 32;
+       net->xfrm.policy_hthresh.lbits6 = 128;
+       net->xfrm.policy_hthresh.rbits6 = 128;
+       seqlock_init(&net->xfrm.policy_hthresh.lock);
  
        INIT_LIST_HEAD(&net->xfrm.policy_all);
        INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
+       INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild);
        if (net_eq(net, &init_net))
                register_netdevice_notifier(&xfrm_dev_notifier);
        return 0;
This page took 0.081434 seconds and 4 git commands to generate.