Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/net/xfrm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 83 kB image not shown  

Quelle  xfrm_state.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * xfrm_state.c
 *
 * Changes:
 * Mitsuru KANDA @USAGI
 *  Kazunori MIYAZAWA @USAGI
 *  Kunihiro Ishiguro <kunihiro@ipinfusion.com>
 *  IPv6 support
 *  YOSHIFUJI Hideaki @USAGI
 *  Split up af-specific functions
 * Derek Atkins <derek@ihtfp.com>
 * Add UDP Encapsulation
 *
 */


#include <linux/compat.h>
#include <linux/workqueue.h>
#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
#include <linux/module.h>
#include <linux/cache.h>
#include <linux/audit.h>
#include <linux/uaccess.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

#include <crypto/aead.h>

#include "xfrm_hash.h"

#define xfrm_state_deref_prot(table, net) \
 rcu_dereference_protected((table), lockdep_is_held(&(net)->xfrm.xfrm_state_lock))
#define xfrm_state_deref_check(table, net) \
 rcu_dereference_check((table), lockdep_is_held(&(net)->xfrm.xfrm_state_lock))

static void xfrm_state_gc_task(struct work_struct *work);

/* Each xfrm_state may be linked to two tables:

   1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
   2. Hash table by (daddr,family,reqid) to find what SAs exist for given
      destination/tunnel endpoint. (output)
 */


static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
static struct kmem_cache *xfrm_state_cache __ro_after_init;

static DECLARE_WORK(xfrm_state_gc_work, xfrm_state_gc_task);
static HLIST_HEAD(xfrm_state_gc_list);
static HLIST_HEAD(xfrm_state_dev_gc_list);

static inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x)
{
 return refcount_inc_not_zero(&x->refcnt);
}

static inline unsigned int xfrm_dst_hash(struct net *net,
      const xfrm_address_t *daddr,
      const xfrm_address_t *saddr,
      u32 reqid,
      unsigned short family)
{
 lockdep_assert_held(&net->xfrm.xfrm_state_lock);

 return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
}

static inline unsigned int xfrm_src_hash(struct net *net,
      const xfrm_address_t *daddr,
      const xfrm_address_t *saddr,
      unsigned short family)
{
 lockdep_assert_held(&net->xfrm.xfrm_state_lock);

 return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
}

static inline unsigned int
xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr,
       __be32 spi, u8 proto, unsigned short family)
{
 lockdep_assert_held(&net->xfrm.xfrm_state_lock);

 return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
}

static unsigned int xfrm_seq_hash(struct net *net, u32 seq)
{
 lockdep_assert_held(&net->xfrm.xfrm_state_lock);

 return __xfrm_seq_hash(seq, net->xfrm.state_hmask);
}

#define XFRM_STATE_INSERT(by, _n, _h, _type)                               \
 {                                                                  \
  struct xfrm_state *_x = NULL;                              \
            \
  if (_type != XFRM_DEV_OFFLOAD_PACKET) {                    \
   hlist_for_each_entry_rcu(_x, _h, by) {             \
    if (_x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \
     continue;                          \
    break;                                     \
   }                                                  \
  }                                                          \
            \
  if (!_x || _x->xso.type == XFRM_DEV_OFFLOAD_PACKET)        \
   /* SAD is empty or consist from HW SAs only */     \
   hlist_add_head_rcu(_n, _h);                        \
  else                                                       \
   hlist_add_before_rcu(_n, &_x->by);                 \
 }

static void xfrm_hash_transfer(struct hlist_head *list,
          struct hlist_head *ndsttable,
          struct hlist_head *nsrctable,
          struct hlist_head *nspitable,
          struct hlist_head *nseqtable,
          unsigned int nhashmask)
{
 struct hlist_node *tmp;
 struct xfrm_state *x;

 hlist_for_each_entry_safe(x, tmp, list, bydst) {
  unsigned int h;

  h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
        x->props.reqid, x->props.family,
        nhashmask);
  XFRM_STATE_INSERT(bydst, &x->bydst, ndsttable + h, x->xso.type);

  h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
        x->props.family,
        nhashmask);
  XFRM_STATE_INSERT(bysrc, &x->bysrc, nsrctable + h, x->xso.type);

  if (x->id.spi) {
   h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
         x->id.proto, x->props.family,
         nhashmask);
   XFRM_STATE_INSERT(byspi, &x->byspi, nspitable + h,
       x->xso.type);
  }

  if (x->km.seq) {
   h = __xfrm_seq_hash(x->km.seq, nhashmask);
   XFRM_STATE_INSERT(byseq, &x->byseq, nseqtable + h,
       x->xso.type);
  }
 }
}

static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
{
 return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
}

static void xfrm_hash_resize(struct work_struct *work)
{
 struct net *net = container_of(work, struct net, xfrm.state_hash_work);
 struct hlist_head *ndst, *nsrc, *nspi, *nseq, *odst, *osrc, *ospi, *oseq;
 unsigned long nsize, osize;
 unsigned int nhashmask, ohashmask;
 int i;

 nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
 ndst = xfrm_hash_alloc(nsize);
 if (!ndst)
  return;
 nsrc = xfrm_hash_alloc(nsize);
 if (!nsrc) {
  xfrm_hash_free(ndst, nsize);
  return;
 }
 nspi = xfrm_hash_alloc(nsize);
 if (!nspi) {
  xfrm_hash_free(ndst, nsize);
  xfrm_hash_free(nsrc, nsize);
  return;
 }
 nseq = xfrm_hash_alloc(nsize);
 if (!nseq) {
  xfrm_hash_free(ndst, nsize);
  xfrm_hash_free(nsrc, nsize);
  xfrm_hash_free(nspi, nsize);
  return;
 }

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 write_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);

 nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
 odst = xfrm_state_deref_prot(net->xfrm.state_bydst, net);
 for (i = net->xfrm.state_hmask; i >= 0; i--)
  xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq, nhashmask);

 osrc = xfrm_state_deref_prot(net->xfrm.state_bysrc, net);
 ospi = xfrm_state_deref_prot(net->xfrm.state_byspi, net);
 oseq = xfrm_state_deref_prot(net->xfrm.state_byseq, net);
 ohashmask = net->xfrm.state_hmask;

 rcu_assign_pointer(net->xfrm.state_bydst, ndst);
 rcu_assign_pointer(net->xfrm.state_bysrc, nsrc);
 rcu_assign_pointer(net->xfrm.state_byspi, nspi);
 rcu_assign_pointer(net->xfrm.state_byseq, nseq);
 net->xfrm.state_hmask = nhashmask;

 write_seqcount_end(&net->xfrm.xfrm_state_hash_generation);
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 osize = (ohashmask + 1) * sizeof(struct hlist_head);

 synchronize_rcu();

 xfrm_hash_free(odst, osize);
 xfrm_hash_free(osrc, osize);
 xfrm_hash_free(ospi, osize);
 xfrm_hash_free(oseq, osize);
}

static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];

static DEFINE_SPINLOCK(xfrm_state_gc_lock);
static DEFINE_SPINLOCK(xfrm_state_dev_gc_lock);

int __xfrm_state_delete(struct xfrm_state *x);

int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
static bool km_is_alive(const struct km_event *c);
void km_state_expired(struct xfrm_state *x, int hard, u32 portid);

int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
{
 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
 int err = 0;

 if (!afinfo)
  return -EAFNOSUPPORT;

#define X(afi, T, name) do {   \
  WARN_ON((afi)->type_ ## name); \
  (afi)->type_ ## name = (T); \
 } while (0)

 switch (type->proto) {
 case IPPROTO_COMP:
  X(afinfo, type, comp);
  break;
 case IPPROTO_AH:
  X(afinfo, type, ah);
  break;
 case IPPROTO_ESP:
  X(afinfo, type, esp);
  break;
 case IPPROTO_IPIP:
  X(afinfo, type, ipip);
  break;
 case IPPROTO_DSTOPTS:
  X(afinfo, type, dstopts);
  break;
 case IPPROTO_ROUTING:
  X(afinfo, type, routing);
  break;
 case IPPROTO_IPV6:
  X(afinfo, type, ipip6);
  break;
 default:
  WARN_ON(1);
  err = -EPROTONOSUPPORT;
  break;
 }
#undef X
 rcu_read_unlock();
 return err;
}
EXPORT_SYMBOL(xfrm_register_type);

void xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
{
 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);

 if (unlikely(afinfo == NULL))
  return;

#define X(afi, T, name) do {    \
  WARN_ON((afi)->type_ ## name != (T)); \
  (afi)->type_ ## name = NULL;  \
 } while (0)

 switch (type->proto) {
 case IPPROTO_COMP:
  X(afinfo, type, comp);
  break;
 case IPPROTO_AH:
  X(afinfo, type, ah);
  break;
 case IPPROTO_ESP:
  X(afinfo, type, esp);
  break;
 case IPPROTO_IPIP:
  X(afinfo, type, ipip);
  break;
 case IPPROTO_DSTOPTS:
  X(afinfo, type, dstopts);
  break;
 case IPPROTO_ROUTING:
  X(afinfo, type, routing);
  break;
 case IPPROTO_IPV6:
  X(afinfo, type, ipip6);
  break;
 default:
  WARN_ON(1);
  break;
 }
#undef X
 rcu_read_unlock();
}
EXPORT_SYMBOL(xfrm_unregister_type);

static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
{
 const struct xfrm_type *type = NULL;
 struct xfrm_state_afinfo *afinfo;
 int modload_attempted = 0;

retry:
 afinfo = xfrm_state_get_afinfo(family);
 if (unlikely(afinfo == NULL))
  return NULL;

 switch (proto) {
 case IPPROTO_COMP:
  type = afinfo->type_comp;
  break;
 case IPPROTO_AH:
  type = afinfo->type_ah;
  break;
 case IPPROTO_ESP:
  type = afinfo->type_esp;
  break;
 case IPPROTO_IPIP:
  type = afinfo->type_ipip;
  break;
 case IPPROTO_DSTOPTS:
  type = afinfo->type_dstopts;
  break;
 case IPPROTO_ROUTING:
  type = afinfo->type_routing;
  break;
 case IPPROTO_IPV6:
  type = afinfo->type_ipip6;
  break;
 default:
  break;
 }

 if (unlikely(type && !try_module_get(type->owner)))
  type = NULL;

 rcu_read_unlock();

 if (!type && !modload_attempted) {
  request_module("xfrm-type-%d-%d", family, proto);
  modload_attempted = 1;
  goto retry;
 }

 return type;
}

static void xfrm_put_type(const struct xfrm_type *type)
{
 module_put(type->owner);
}

int xfrm_register_type_offload(const struct xfrm_type_offload *type,
          unsigned short family)
{
 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
 int err = 0;

 if (unlikely(afinfo == NULL))
  return -EAFNOSUPPORT;

 switch (type->proto) {
 case IPPROTO_ESP:
  WARN_ON(afinfo->type_offload_esp);
  afinfo->type_offload_esp = type;
  break;
 default:
  WARN_ON(1);
  err = -EPROTONOSUPPORT;
  break;
 }

 rcu_read_unlock();
 return err;
}
EXPORT_SYMBOL(xfrm_register_type_offload);

void xfrm_unregister_type_offload(const struct xfrm_type_offload *type,
      unsigned short family)
{
 struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);

 if (unlikely(afinfo == NULL))
  return;

 switch (type->proto) {
 case IPPROTO_ESP:
  WARN_ON(afinfo->type_offload_esp != type);
  afinfo->type_offload_esp = NULL;
  break;
 default:
  WARN_ON(1);
  break;
 }
 rcu_read_unlock();
}
EXPORT_SYMBOL(xfrm_unregister_type_offload);

void xfrm_set_type_offload(struct xfrm_state *x, bool try_load)
{
 const struct xfrm_type_offload *type = NULL;
 struct xfrm_state_afinfo *afinfo;

retry:
 afinfo = xfrm_state_get_afinfo(x->props.family);
 if (unlikely(afinfo == NULL))
  goto out;

 switch (x->id.proto) {
 case IPPROTO_ESP:
  type = afinfo->type_offload_esp;
  break;
 default:
  break;
 }

 if ((type && !try_module_get(type->owner)))
  type = NULL;

 rcu_read_unlock();

 if (!type && try_load) {
  request_module("xfrm-offload-%d-%d", x->props.family,
          x->id.proto);
  try_load = false;
  goto retry;
 }

out:
 x->type_offload = type;
}
EXPORT_SYMBOL(xfrm_set_type_offload);

static const struct xfrm_mode xfrm4_mode_map[XFRM_MODE_MAX] = {
 [XFRM_MODE_BEET] = {
  .encap = XFRM_MODE_BEET,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET,
 },
 [XFRM_MODE_TRANSPORT] = {
  .encap = XFRM_MODE_TRANSPORT,
  .family = AF_INET,
 },
 [XFRM_MODE_TUNNEL] = {
  .encap = XFRM_MODE_TUNNEL,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET,
 },
 [XFRM_MODE_IPTFS] = {
  .encap = XFRM_MODE_IPTFS,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET,
 },
};

static const struct xfrm_mode xfrm6_mode_map[XFRM_MODE_MAX] = {
 [XFRM_MODE_BEET] = {
  .encap = XFRM_MODE_BEET,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET6,
 },
 [XFRM_MODE_ROUTEOPTIMIZATION] = {
  .encap = XFRM_MODE_ROUTEOPTIMIZATION,
  .family = AF_INET6,
 },
 [XFRM_MODE_TRANSPORT] = {
  .encap = XFRM_MODE_TRANSPORT,
  .family = AF_INET6,
 },
 [XFRM_MODE_TUNNEL] = {
  .encap = XFRM_MODE_TUNNEL,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET6,
 },
 [XFRM_MODE_IPTFS] = {
  .encap = XFRM_MODE_IPTFS,
  .flags = XFRM_MODE_FLAG_TUNNEL,
  .family = AF_INET6,
 },
};

static const struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
{
 const struct xfrm_mode *mode;

 if (unlikely(encap >= XFRM_MODE_MAX))
  return NULL;

 switch (family) {
 case AF_INET:
  mode = &xfrm4_mode_map[encap];
  if (mode->family == family)
   return mode;
  break;
 case AF_INET6:
  mode = &xfrm6_mode_map[encap];
  if (mode->family == family)
   return mode;
  break;
 default:
  break;
 }

 return NULL;
}

static const struct xfrm_mode_cbs  __rcu *xfrm_mode_cbs_map[XFRM_MODE_MAX];
static DEFINE_SPINLOCK(xfrm_mode_cbs_map_lock);

int xfrm_register_mode_cbs(u8 mode, const struct xfrm_mode_cbs *mode_cbs)
{
 if (mode >= XFRM_MODE_MAX)
  return -EINVAL;

 spin_lock_bh(&xfrm_mode_cbs_map_lock);
 rcu_assign_pointer(xfrm_mode_cbs_map[mode], mode_cbs);
 spin_unlock_bh(&xfrm_mode_cbs_map_lock);

 return 0;
}
EXPORT_SYMBOL(xfrm_register_mode_cbs);

void xfrm_unregister_mode_cbs(u8 mode)
{
 if (mode >= XFRM_MODE_MAX)
  return;

 spin_lock_bh(&xfrm_mode_cbs_map_lock);
 RCU_INIT_POINTER(xfrm_mode_cbs_map[mode], NULL);
 spin_unlock_bh(&xfrm_mode_cbs_map_lock);
 synchronize_rcu();
}
EXPORT_SYMBOL(xfrm_unregister_mode_cbs);

static const struct xfrm_mode_cbs *xfrm_get_mode_cbs(u8 mode)
{
 const struct xfrm_mode_cbs *cbs;
 bool try_load = true;

 if (mode >= XFRM_MODE_MAX)
  return NULL;

retry:
 rcu_read_lock();

 cbs = rcu_dereference(xfrm_mode_cbs_map[mode]);
 if (cbs && !try_module_get(cbs->owner))
  cbs = NULL;

 rcu_read_unlock();

 if (mode == XFRM_MODE_IPTFS && !cbs && try_load) {
  request_module("xfrm-iptfs");
  try_load = false;
  goto retry;
 }

 return cbs;
}

void xfrm_state_free(struct xfrm_state *x)
{
 kmem_cache_free(xfrm_state_cache, x);
}
EXPORT_SYMBOL(xfrm_state_free);

static void xfrm_state_gc_destroy(struct xfrm_state *x)
{
 if (x->mode_cbs && x->mode_cbs->destroy_state)
  x->mode_cbs->destroy_state(x);
 hrtimer_cancel(&x->mtimer);
 timer_delete_sync(&x->rtimer);
 kfree_sensitive(x->aead);
 kfree_sensitive(x->aalg);
 kfree_sensitive(x->ealg);
 kfree(x->calg);
 kfree(x->encap);
 kfree(x->coaddr);
 kfree(x->replay_esn);
 kfree(x->preplay_esn);
 xfrm_unset_type_offload(x);
 if (x->type) {
  x->type->destructor(x);
  xfrm_put_type(x->type);
 }
 if (x->xfrag.page)
  put_page(x->xfrag.page);
 xfrm_dev_state_free(x);
 security_xfrm_state_free(x);
 xfrm_state_free(x);
}

static void xfrm_state_gc_task(struct work_struct *work)
{
 struct xfrm_state *x;
 struct hlist_node *tmp;
 struct hlist_head gc_list;

 spin_lock_bh(&xfrm_state_gc_lock);
 hlist_move_list(&xfrm_state_gc_list, &gc_list);
 spin_unlock_bh(&xfrm_state_gc_lock);

 synchronize_rcu();

 hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
  xfrm_state_gc_destroy(x);
}

static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
{
 struct xfrm_state *x = container_of(me, struct xfrm_state, mtimer);
 enum hrtimer_restart ret = HRTIMER_NORESTART;
 time64_t now = ktime_get_real_seconds();
 time64_t next = TIME64_MAX;
 int warn = 0;
 int err = 0;

 spin_lock(&x->lock);
 xfrm_dev_state_update_stats(x);

 if (x->km.state == XFRM_STATE_DEAD)
  goto out;
 if (x->km.state == XFRM_STATE_EXPIRED)
  goto expired;
 if (x->lft.hard_add_expires_seconds) {
  time64_t tmo = x->lft.hard_add_expires_seconds +
   x->curlft.add_time - now;
  if (tmo <= 0) {
   if (x->xflags & XFRM_SOFT_EXPIRE) {
    /* enter hard expire without soft expire first?!
 * setting a new date could trigger this.
 * workaround: fix x->curflt.add_time by below:
 */

    x->curlft.add_time = now - x->saved_tmo - 1;
    tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
   } else
    goto expired;
  }
  if (tmo < next)
   next = tmo;
 }
 if (x->lft.hard_use_expires_seconds) {
  time64_t tmo = x->lft.hard_use_expires_seconds +
   (READ_ONCE(x->curlft.use_time) ? : now) - now;
  if (tmo <= 0)
   goto expired;
  if (tmo < next)
   next = tmo;
 }
 if (x->km.dying)
  goto resched;
 if (x->lft.soft_add_expires_seconds) {
  time64_t tmo = x->lft.soft_add_expires_seconds +
   x->curlft.add_time - now;
  if (tmo <= 0) {
   warn = 1;
   x->xflags &= ~XFRM_SOFT_EXPIRE;
  } else if (tmo < next) {
   next = tmo;
   x->xflags |= XFRM_SOFT_EXPIRE;
   x->saved_tmo = tmo;
  }
 }
 if (x->lft.soft_use_expires_seconds) {
  time64_t tmo = x->lft.soft_use_expires_seconds +
   (READ_ONCE(x->curlft.use_time) ? : now) - now;
  if (tmo <= 0)
   warn = 1;
  else if (tmo < next)
   next = tmo;
 }

 x->km.dying = warn;
 if (warn)
  km_state_expired(x, 0, 0);
resched:
 if (next != TIME64_MAX) {
  hrtimer_forward_now(&x->mtimer, ktime_set(next, 0));
  ret = HRTIMER_RESTART;
 }

 goto out;

expired:
 if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0)
  x->km.state = XFRM_STATE_EXPIRED;

 err = __xfrm_state_delete(x);
 if (!err)
  km_state_expired(x, 1, 0);

 xfrm_audit_state_delete(x, err ? 0 : 1, true);

out:
 spin_unlock(&x->lock);
 return ret;
}

static void xfrm_replay_timer_handler(struct timer_list *t);

struct xfrm_state *xfrm_state_alloc(struct net *net)
{
 struct xfrm_state *x;

 x = kmem_cache_zalloc(xfrm_state_cache, GFP_ATOMIC);

 if (x) {
  write_pnet(&x->xs_net, net);
  refcount_set(&x->refcnt, 1);
  atomic_set(&x->tunnel_users, 0);
  INIT_LIST_HEAD(&x->km.all);
  INIT_HLIST_NODE(&x->state_cache);
  INIT_HLIST_NODE(&x->bydst);
  INIT_HLIST_NODE(&x->bysrc);
  INIT_HLIST_NODE(&x->byspi);
  INIT_HLIST_NODE(&x->byseq);
  hrtimer_setup(&x->mtimer, xfrm_timer_handler, CLOCK_BOOTTIME,
         HRTIMER_MODE_ABS_SOFT);
  timer_setup(&x->rtimer, xfrm_replay_timer_handler, 0);
  x->curlft.add_time = ktime_get_real_seconds();
  x->lft.soft_byte_limit = XFRM_INF;
  x->lft.soft_packet_limit = XFRM_INF;
  x->lft.hard_byte_limit = XFRM_INF;
  x->lft.hard_packet_limit = XFRM_INF;
  x->replay_maxage = 0;
  x->replay_maxdiff = 0;
  x->pcpu_num = UINT_MAX;
  spin_lock_init(&x->lock);
  x->mode_data = NULL;
 }
 return x;
}
EXPORT_SYMBOL(xfrm_state_alloc);

#ifdef CONFIG_XFRM_OFFLOAD
void xfrm_dev_state_delete(struct xfrm_state *x)
{
 struct xfrm_dev_offload *xso = &x->xso;
 struct net_device *dev = READ_ONCE(xso->dev);

 if (dev) {
  dev->xfrmdev_ops->xdo_dev_state_delete(dev, x);
  spin_lock_bh(&xfrm_state_dev_gc_lock);
  hlist_add_head(&x->dev_gclist, &xfrm_state_dev_gc_list);
  spin_unlock_bh(&xfrm_state_dev_gc_lock);
 }
}
EXPORT_SYMBOL_GPL(xfrm_dev_state_delete);

void xfrm_dev_state_free(struct xfrm_state *x)
{
 struct xfrm_dev_offload *xso = &x->xso;
 struct net_device *dev = READ_ONCE(xso->dev);

 if (dev && dev->xfrmdev_ops) {
  spin_lock_bh(&xfrm_state_dev_gc_lock);
  if (!hlist_unhashed(&x->dev_gclist))
   hlist_del(&x->dev_gclist);
  spin_unlock_bh(&xfrm_state_dev_gc_lock);

  if (dev->xfrmdev_ops->xdo_dev_state_free)
   dev->xfrmdev_ops->xdo_dev_state_free(dev, x);
  WRITE_ONCE(xso->dev, NULL);
  xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
  netdev_put(dev, &xso->dev_tracker);
 }
}
#endif

void __xfrm_state_destroy(struct xfrm_state *x)
{
 WARN_ON(x->km.state != XFRM_STATE_DEAD);

 spin_lock_bh(&xfrm_state_gc_lock);
 hlist_add_head(&x->gclist, &xfrm_state_gc_list);
 spin_unlock_bh(&xfrm_state_gc_lock);
 schedule_work(&xfrm_state_gc_work);
}
EXPORT_SYMBOL(__xfrm_state_destroy);

static void xfrm_state_delete_tunnel(struct xfrm_state *x);
int __xfrm_state_delete(struct xfrm_state *x)
{
 struct net *net = xs_net(x);
 int err = -ESRCH;

 if (x->km.state != XFRM_STATE_DEAD) {
  x->km.state = XFRM_STATE_DEAD;

  spin_lock(&net->xfrm.xfrm_state_lock);
  list_del(&x->km.all);
  hlist_del_rcu(&x->bydst);
  hlist_del_rcu(&x->bysrc);
  if (x->km.seq)
   hlist_del_rcu(&x->byseq);
  if (!hlist_unhashed(&x->state_cache))
   hlist_del_rcu(&x->state_cache);
  if (!hlist_unhashed(&x->state_cache_input))
   hlist_del_rcu(&x->state_cache_input);

  if (x->id.spi)
   hlist_del_rcu(&x->byspi);
  net->xfrm.state_num--;
  xfrm_nat_keepalive_state_updated(x);
  spin_unlock(&net->xfrm.xfrm_state_lock);

  xfrm_dev_state_delete(x);

  xfrm_state_delete_tunnel(x);

  /* All xfrm_state objects are created by xfrm_state_alloc.
 * The xfrm_state_alloc call gives a reference, and that
 * is what we are dropping here.
 */

  xfrm_state_put(x);
  err = 0;
 }

 return err;
}
EXPORT_SYMBOL(__xfrm_state_delete);

int xfrm_state_delete(struct xfrm_state *x)
{
 int err;

 spin_lock_bh(&x->lock);
 err = __xfrm_state_delete(x);
 spin_unlock_bh(&x->lock);

 return err;
}
EXPORT_SYMBOL(xfrm_state_delete);

#ifdef CONFIG_SECURITY_NETWORK_XFRM
static inline int
xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
{
 int i, err = 0;

 for (i = 0; i <= net->xfrm.state_hmask; i++) {
  struct xfrm_state *x;

  hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
   if (xfrm_id_proto_match(x->id.proto, proto) &&
      (err = security_xfrm_state_delete(x)) != 0) {
    xfrm_audit_state_delete(x, 0, task_valid);
    return err;
   }
  }
 }

 return err;
}

static inline int
xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid)
{
 int i, err = 0;

 for (i = 0; i <= net->xfrm.state_hmask; i++) {
  struct xfrm_state *x;
  struct xfrm_dev_offload *xso;

  hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
   xso = &x->xso;

   if (xso->dev == dev &&
      (err = security_xfrm_state_delete(x)) != 0) {
    xfrm_audit_state_delete(x, 0, task_valid);
    return err;
   }
  }
 }

 return err;
}
#else
static inline int
xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
{
 return 0;
}

static inline int
xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid)
{
 return 0;
}
#endif

int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
{
 int i, err = 0, cnt = 0;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 err = xfrm_state_flush_secctx_check(net, proto, task_valid);
 if (err)
  goto out;

 err = -ESRCH;
 for (i = 0; i <= net->xfrm.state_hmask; i++) {
  struct xfrm_state *x;
restart:
  hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
   if (!xfrm_state_kern(x) &&
       xfrm_id_proto_match(x->id.proto, proto)) {
    xfrm_state_hold(x);
    spin_unlock_bh(&net->xfrm.xfrm_state_lock);

    err = xfrm_state_delete(x);
    xfrm_audit_state_delete(x, err ? 0 : 1,
       task_valid);
    xfrm_state_put(x);
    if (!err)
     cnt++;

    spin_lock_bh(&net->xfrm.xfrm_state_lock);
    goto restart;
   }
  }
 }
out:
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 if (cnt)
  err = 0;

 return err;
}
EXPORT_SYMBOL(xfrm_state_flush);

int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid)
{
 struct xfrm_state *x;
 struct hlist_node *tmp;
 struct xfrm_dev_offload *xso;
 int i, err = 0, cnt = 0;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 err = xfrm_dev_state_flush_secctx_check(net, dev, task_valid);
 if (err)
  goto out;

 err = -ESRCH;
 for (i = 0; i <= net->xfrm.state_hmask; i++) {
restart:
  hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
   xso = &x->xso;

   if (!xfrm_state_kern(x) && xso->dev == dev) {
    xfrm_state_hold(x);
    spin_unlock_bh(&net->xfrm.xfrm_state_lock);

    err = xfrm_state_delete(x);
    xfrm_dev_state_free(x);

    xfrm_audit_state_delete(x, err ? 0 : 1,
       task_valid);
    xfrm_state_put(x);
    if (!err)
     cnt++;

    spin_lock_bh(&net->xfrm.xfrm_state_lock);
    goto restart;
   }
  }
 }
 if (cnt)
  err = 0;

out:
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 spin_lock_bh(&xfrm_state_dev_gc_lock);
restart_gc:
 hlist_for_each_entry_safe(x, tmp, &xfrm_state_dev_gc_list, dev_gclist) {
  xso = &x->xso;

  if (xso->dev == dev) {
   spin_unlock_bh(&xfrm_state_dev_gc_lock);
   xfrm_dev_state_free(x);
   spin_lock_bh(&xfrm_state_dev_gc_lock);
   goto restart_gc;
  }

 }
 spin_unlock_bh(&xfrm_state_dev_gc_lock);

 xfrm_flush_gc();

 return err;
}
EXPORT_SYMBOL(xfrm_dev_state_flush);

void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
{
 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 si->sadcnt = net->xfrm.state_num;
 si->sadhcnt = net->xfrm.state_hmask + 1;
 si->sadhmcnt = xfrm_state_hashmax;
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
}
EXPORT_SYMBOL(xfrm_sad_getinfo);

static void
__xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl)
{
 const struct flowi4 *fl4 = &fl->u.ip4;

 sel->daddr.a4 = fl4->daddr;
 sel->saddr.a4 = fl4->saddr;
 sel->dport = xfrm_flowi_dport(fl, &fl4->uli);
 sel->dport_mask = htons(0xffff);
 sel->sport = xfrm_flowi_sport(fl, &fl4->uli);
 sel->sport_mask = htons(0xffff);
 sel->family = AF_INET;
 sel->prefixlen_d = 32;
 sel->prefixlen_s = 32;
 sel->proto = fl4->flowi4_proto;
 sel->ifindex = fl4->flowi4_oif;
}

static void
__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl)
{
 const struct flowi6 *fl6 = &fl->u.ip6;

 /* Initialize temporary selector matching only to current session. */
 *(struct in6_addr *)&sel->daddr = fl6->daddr;
 *(struct in6_addr *)&sel->saddr = fl6->saddr;
 sel->dport = xfrm_flowi_dport(fl, &fl6->uli);
 sel->dport_mask = htons(0xffff);
 sel->sport = xfrm_flowi_sport(fl, &fl6->uli);
 sel->sport_mask = htons(0xffff);
 sel->family = AF_INET6;
 sel->prefixlen_d = 128;
 sel->prefixlen_s = 128;
 sel->proto = fl6->flowi6_proto;
 sel->ifindex = fl6->flowi6_oif;
}

static void
xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl,
      const struct xfrm_tmpl *tmpl,
      const xfrm_address_t *daddr, const xfrm_address_t *saddr,
      unsigned short family)
{
 switch (family) {
 case AF_INET:
  __xfrm4_init_tempsel(&x->sel, fl);
  break;
 case AF_INET6:
  __xfrm6_init_tempsel(&x->sel, fl);
  break;
 }

 x->id = tmpl->id;

 switch (tmpl->encap_family) {
 case AF_INET:
  if (x->id.daddr.a4 == 0)
   x->id.daddr.a4 = daddr->a4;
  x->props.saddr = tmpl->saddr;
  if (x->props.saddr.a4 == 0)
   x->props.saddr.a4 = saddr->a4;
  break;
 case AF_INET6:
  if (ipv6_addr_any((struct in6_addr *)&x->id.daddr))
   memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
  memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
  if (ipv6_addr_any((struct in6_addr *)&x->props.saddr))
   memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
  break;
 }

 x->props.mode = tmpl->mode;
 x->props.reqid = tmpl->reqid;
 x->props.family = tmpl->encap_family;
}

struct xfrm_hash_state_ptrs {
 const struct hlist_head *bydst;
 const struct hlist_head *bysrc;
 const struct hlist_head *byspi;
 unsigned int hmask;
};

static void xfrm_hash_ptrs_get(const struct net *net, struct xfrm_hash_state_ptrs *ptrs)
{
 unsigned int sequence;

 do {
  sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);

  ptrs->bydst = xfrm_state_deref_check(net->xfrm.state_bydst, net);
  ptrs->bysrc = xfrm_state_deref_check(net->xfrm.state_bysrc, net);
  ptrs->byspi = xfrm_state_deref_check(net->xfrm.state_byspi, net);
  ptrs->hmask = net->xfrm.state_hmask;
 } while (read_seqcount_retry(&net->xfrm.xfrm_state_hash_generation, sequence));
}

static struct xfrm_state *__xfrm_state_lookup_all(const struct xfrm_hash_state_ptrs *state_ptrs,
        u32 mark,
        const xfrm_address_t *daddr,
        __be32 spi, u8 proto,
        unsigned short family,
        struct xfrm_dev_offload *xdo)
{
 unsigned int h = __xfrm_spi_hash(daddr, spi, proto, family, state_ptrs->hmask);
 struct xfrm_state *x;

 hlist_for_each_entry_rcu(x, state_ptrs->byspi + h, byspi) {
#ifdef CONFIG_XFRM_OFFLOAD
  if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) {
   if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
    /* HW states are in the head of list, there is
 * no need to iterate further.
 */

    break;

   /* Packet offload: both policy and SA should
 * have same device.
 */

   if (xdo->dev != x->xso.dev)
    continue;
  } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
   /* Skip HW policy for SW lookups */
   continue;
#endif
  if (x->props.family != family ||
      x->id.spi       != spi ||
      x->id.proto     != proto ||
      !xfrm_addr_equal(&x->id.daddr, daddr, family))
   continue;

  if ((mark & x->mark.m) != x->mark.v)
   continue;
  if (!xfrm_state_hold_rcu(x))
   continue;
  return x;
 }

 return NULL;
}

static struct xfrm_state *__xfrm_state_lookup(const struct xfrm_hash_state_ptrs *state_ptrs,
           u32 mark,
           const xfrm_address_t *daddr,
           __be32 spi, u8 proto,
           unsigned short family)
{
 unsigned int h = __xfrm_spi_hash(daddr, spi, proto, family, state_ptrs->hmask);
 struct xfrm_state *x;

 hlist_for_each_entry_rcu(x, state_ptrs->byspi + h, byspi) {
  if (x->props.family != family ||
      x->id.spi       != spi ||
      x->id.proto     != proto ||
      !xfrm_addr_equal(&x->id.daddr, daddr, family))
   continue;

  if ((mark & x->mark.m) != x->mark.v)
   continue;
  if (!xfrm_state_hold_rcu(x))
   continue;
  return x;
 }

 return NULL;
}

struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
        const xfrm_address_t *daddr,
        __be32 spi, u8 proto,
        unsigned short family)
{
 struct xfrm_hash_state_ptrs state_ptrs;
 struct hlist_head *state_cache_input;
 struct xfrm_state *x = NULL;

 state_cache_input = raw_cpu_ptr(net->xfrm.state_cache_input);

 rcu_read_lock();
 hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) {
  if (x->props.family != family ||
      x->id.spi       != spi ||
      x->id.proto     != proto ||
      !xfrm_addr_equal(&x->id.daddr, daddr, family))
   continue;

  if ((mark & x->mark.m) != x->mark.v)
   continue;
  if (!xfrm_state_hold_rcu(x))
   continue;
  goto out;
 }

 xfrm_hash_ptrs_get(net, &state_ptrs);

 x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);

 if (x && x->km.state == XFRM_STATE_VALID) {
  spin_lock_bh(&net->xfrm.xfrm_state_lock);
  if (hlist_unhashed(&x->state_cache_input)) {
   hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
  } else {
   hlist_del_rcu(&x->state_cache_input);
   hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
  }
  spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 }

out:
 rcu_read_unlock();
 return x;
}
EXPORT_SYMBOL(xfrm_input_state_lookup);

static struct xfrm_state *__xfrm_state_lookup_byaddr(const struct xfrm_hash_state_ptrs *state_ptrs,
           u32 mark,
           const xfrm_address_t *daddr,
           const xfrm_address_t *saddr,
           u8 proto, unsigned short family)
{
 unsigned int h = __xfrm_src_hash(daddr, saddr, family, state_ptrs->hmask);
 struct xfrm_state *x;

 hlist_for_each_entry_rcu(x, state_ptrs->bysrc + h, bysrc) {
  if (x->props.family != family ||
      x->id.proto     != proto ||
      !xfrm_addr_equal(&x->id.daddr, daddr, family) ||
      !xfrm_addr_equal(&x->props.saddr, saddr, family))
   continue;

  if ((mark & x->mark.m) != x->mark.v)
   continue;
  if (!xfrm_state_hold_rcu(x))
   continue;
  return x;
 }

 return NULL;
}

static inline struct xfrm_state *
__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
{
 struct xfrm_hash_state_ptrs state_ptrs;
 struct net *net = xs_net(x);
 u32 mark = x->mark.v & x->mark.m;

 xfrm_hash_ptrs_get(net, &state_ptrs);

 if (use_spi)
  return __xfrm_state_lookup(&state_ptrs, mark, &x->id.daddr,
        x->id.spi, x->id.proto, family);
 else
  return __xfrm_state_lookup_byaddr(&state_ptrs, mark,
        &x->id.daddr,
        &x->props.saddr,
        x->id.proto, family);
}

static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
{
 if (have_hash_collision &&
     (net->xfrm.state_hmask + 1) < xfrm_state_hashmax &&
     net->xfrm.state_num > net->xfrm.state_hmask)
  schedule_work(&net->xfrm.state_hash_work);
}

static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
          const struct flowi *fl, unsigned short family,
          struct xfrm_state **best, int *acq_in_progress,
          int *error, unsigned int pcpu_id)
{
 /* Resolution logic:
 * 1. There is a valid state with matching selector. Done.
 * 2. Valid state with inappropriate selector. Skip.
 *
 * Entering area of "sysdeps".
 *
 * 3. If state is not valid, selector is temporary, it selects
 *    only session which triggered previous resolution. Key
 *    manager will do something to install a state with proper
 *    selector.
 */

 if (x->km.state == XFRM_STATE_VALID) {
  if ((x->sel.family &&
       (x->sel.family != family ||
        !xfrm_selector_match(&x->sel, fl, family))) ||
      !security_xfrm_state_pol_flow_match(x, pol,
       &fl->u.__fl_common))
   return;

  if (x->pcpu_num != UINT_MAX && x->pcpu_num != pcpu_id)
   return;

  if (!*best ||
      ((*best)->pcpu_num == UINT_MAX && x->pcpu_num == pcpu_id) ||
      (*best)->km.dying > x->km.dying ||
      ((*best)->km.dying == x->km.dying &&
       (*best)->curlft.add_time < x->curlft.add_time))
   *best = x;
 } else if (x->km.state == XFRM_STATE_ACQ) {
  if (!*best || x->pcpu_num == pcpu_id)
   *acq_in_progress = 1;
 } else if (x->km.state == XFRM_STATE_ERROR ||
     x->km.state == XFRM_STATE_EXPIRED) {
  if ((!x->sel.family ||
       (x->sel.family == family &&
        xfrm_selector_match(&x->sel, fl, family))) &&
      security_xfrm_state_pol_flow_match(x, pol,
             &fl->u.__fl_common))
   *error = -ESRCH;
 }
}

struct xfrm_state *
xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
  const struct flowi *fl, struct xfrm_tmpl *tmpl,
  struct xfrm_policy *pol, int *err,
  unsigned short family, u32 if_id)
{
 static xfrm_address_t saddr_wildcard = { };
 struct xfrm_hash_state_ptrs state_ptrs;
 struct net *net = xp_net(pol);
 unsigned int h, h_wildcard;
 struct xfrm_state *x, *x0, *to_put;
 int acquire_in_progress = 0;
 int error = 0;
 struct xfrm_state *best = NULL;
 u32 mark = pol->mark.v & pol->mark.m;
 unsigned short encap_family = tmpl->encap_family;
 unsigned int sequence;
 struct km_event c;
 unsigned int pcpu_id;
 bool cached = false;

 /* We need the cpu id just as a lookup key,
 * we don't require it to be stable.
 */

 pcpu_id = raw_smp_processor_id();

 to_put = NULL;

 sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);

 rcu_read_lock();
 xfrm_hash_ptrs_get(net, &state_ptrs);

 hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
  if (x->props.family == encap_family &&
      x->props.reqid == tmpl->reqid &&
      (mark & x->mark.m) == x->mark.v &&
      x->if_id == if_id &&
      !(x->props.flags & XFRM_STATE_WILDRECV) &&
      xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
      tmpl->mode == x->props.mode &&
      tmpl->id.proto == x->id.proto &&
      (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
   xfrm_state_look_at(pol, x, fl, encap_family,
        &best, &acquire_in_progress, &error, pcpu_id);
 }

 if (best)
  goto cached;

 hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
  if (x->props.family == encap_family &&
      x->props.reqid == tmpl->reqid &&
      (mark & x->mark.m) == x->mark.v &&
      x->if_id == if_id &&
      !(x->props.flags & XFRM_STATE_WILDRECV) &&
      xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
      tmpl->mode == x->props.mode &&
      tmpl->id.proto == x->id.proto &&
      (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
   xfrm_state_look_at(pol, x, fl, family,
        &best, &acquire_in_progress, &error, pcpu_id);
 }

cached:
 cached = true;
 if (best)
  goto found;
 else if (error)
  best = NULL;
 else if (acquire_in_progress) /* XXX: acquire_in_progress should not happen */
  WARN_ON(1);

 h = __xfrm_dst_hash(daddr, saddr, tmpl->reqid, encap_family, state_ptrs.hmask);
 hlist_for_each_entry_rcu(x, state_ptrs.bydst + h, bydst) {
#ifdef CONFIG_XFRM_OFFLOAD
  if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
   if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
    /* HW states are in the head of list, there is
 * no need to iterate further.
 */

    break;

   /* Packet offload: both policy and SA should
 * have same device.
 */

   if (pol->xdo.dev != x->xso.dev)
    continue;
  } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
   /* Skip HW policy for SW lookups */
   continue;
#endif
  if (x->props.family == encap_family &&
      x->props.reqid == tmpl->reqid &&
      (mark & x->mark.m) == x->mark.v &&
      x->if_id == if_id &&
      !(x->props.flags & XFRM_STATE_WILDRECV) &&
      xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
      tmpl->mode == x->props.mode &&
      tmpl->id.proto == x->id.proto &&
      (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
   xfrm_state_look_at(pol, x, fl, family,
        &best, &acquire_in_progress, &error, pcpu_id);
 }
 if (best || acquire_in_progress)
  goto found;

 h_wildcard = __xfrm_dst_hash(daddr, &saddr_wildcard, tmpl->reqid,
         encap_family, state_ptrs.hmask);
 hlist_for_each_entry_rcu(x, state_ptrs.bydst + h_wildcard, bydst) {
#ifdef CONFIG_XFRM_OFFLOAD
  if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
   if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
    /* HW states are in the head of list, there is
 * no need to iterate further.
 */

    break;

   /* Packet offload: both policy and SA should
 * have same device.
 */

   if (pol->xdo.dev != x->xso.dev)
    continue;
  } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
   /* Skip HW policy for SW lookups */
   continue;
#endif
  if (x->props.family == encap_family &&
      x->props.reqid == tmpl->reqid &&
      (mark & x->mark.m) == x->mark.v &&
      x->if_id == if_id &&
      !(x->props.flags & XFRM_STATE_WILDRECV) &&
      xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
      tmpl->mode == x->props.mode &&
      tmpl->id.proto == x->id.proto &&
      (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
   xfrm_state_look_at(pol, x, fl, family,
        &best, &acquire_in_progress, &error, pcpu_id);
 }

found:
 if (!(pol->flags & XFRM_POLICY_CPU_ACQUIRE) ||
     (best && (best->pcpu_num == pcpu_id)))
  x = best;

 if (!x && !error && !acquire_in_progress) {
  if (tmpl->id.spi &&
      (x0 = __xfrm_state_lookup_all(&state_ptrs, mark, daddr,
        tmpl->id.spi, tmpl->id.proto,
        encap_family,
        &pol->xdo)) != NULL) {
   to_put = x0;
   error = -EEXIST;
   goto out;
  }

  c.net = net;
  /* If the KMs have no listeners (yet...), avoid allocating an SA
 * for each and every packet - garbage collection might not
 * handle the flood.
 */

  if (!km_is_alive(&c)) {
   error = -ESRCH;
   goto out;
  }

  x = xfrm_state_alloc(net);
  if (x == NULL) {
   error = -ENOMEM;
   goto out;
  }
  /* Initialize temporary state matching only
 * to current session. */

  xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
  memcpy(&x->mark, &pol->mark, sizeof(x->mark));
  x->if_id = if_id;
  if ((pol->flags & XFRM_POLICY_CPU_ACQUIRE) && best)
   x->pcpu_num = pcpu_id;

  error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
  if (error) {
   x->km.state = XFRM_STATE_DEAD;
   to_put = x;
   x = NULL;
   goto out;
  }
#ifdef CONFIG_XFRM_OFFLOAD
  if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
   struct xfrm_dev_offload *xdo = &pol->xdo;
   struct xfrm_dev_offload *xso = &x->xso;
   struct net_device *dev = xdo->dev;

   xso->type = XFRM_DEV_OFFLOAD_PACKET;
   xso->dir = xdo->dir;
   xso->dev = dev;
   xso->flags = XFRM_DEV_OFFLOAD_FLAG_ACQ;
   netdev_hold(dev, &xso->dev_tracker, GFP_ATOMIC);
   error = dev->xfrmdev_ops->xdo_dev_state_add(dev, x,
            NULL);
   if (error) {
    xso->dir = 0;
    netdev_put(dev, &xso->dev_tracker);
    xso->dev = NULL;
    xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
    x->km.state = XFRM_STATE_DEAD;
    to_put = x;
    x = NULL;
    goto out;
   }
  }
#endif
  if (km_query(x, tmpl, pol) == 0) {
   spin_lock_bh(&net->xfrm.xfrm_state_lock);
   x->km.state = XFRM_STATE_ACQ;
   x->dir = XFRM_SA_DIR_OUT;
   list_add(&x->km.all, &net->xfrm.state_all);
   h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
   XFRM_STATE_INSERT(bydst, &x->bydst,
       net->xfrm.state_bydst + h,
       x->xso.type);
   h = xfrm_src_hash(net, daddr, saddr, encap_family);
   XFRM_STATE_INSERT(bysrc, &x->bysrc,
       net->xfrm.state_bysrc + h,
       x->xso.type);
   INIT_HLIST_NODE(&x->state_cache);
   if (x->id.spi) {
    h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
    XFRM_STATE_INSERT(byspi, &x->byspi,
        net->xfrm.state_byspi + h,
        x->xso.type);
   }
   if (x->km.seq) {
    h = xfrm_seq_hash(net, x->km.seq);
    XFRM_STATE_INSERT(byseq, &x->byseq,
        net->xfrm.state_byseq + h,
        x->xso.type);
   }
   x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
   hrtimer_start(&x->mtimer,
          ktime_set(net->xfrm.sysctl_acq_expires, 0),
          HRTIMER_MODE_REL_SOFT);
   net->xfrm.state_num++;
   xfrm_hash_grow_check(net, x->bydst.next != NULL);
   spin_unlock_bh(&net->xfrm.xfrm_state_lock);
  } else {
#ifdef CONFIG_XFRM_OFFLOAD
   struct xfrm_dev_offload *xso = &x->xso;

   if (xso->type == XFRM_DEV_OFFLOAD_PACKET) {
    xfrm_dev_state_delete(x);
    xfrm_dev_state_free(x);
   }
#endif
   x->km.state = XFRM_STATE_DEAD;
   to_put = x;
   x = NULL;
   error = -ESRCH;
  }

  /* Use the already installed 'fallback' while the CPU-specific
 * SA acquire is handled*/

  if (best)
   x = best;
 }
out:
 if (x) {
  if (!xfrm_state_hold_rcu(x)) {
   *err = -EAGAIN;
   x = NULL;
  }
 } else {
  *err = acquire_in_progress ? -EAGAIN : error;
 }

 if (x && x->km.state == XFRM_STATE_VALID && !cached &&
     (!(pol->flags & XFRM_POLICY_CPU_ACQUIRE) || x->pcpu_num == pcpu_id)) {
  spin_lock_bh(&net->xfrm.xfrm_state_lock);
  if (hlist_unhashed(&x->state_cache))
   hlist_add_head_rcu(&x->state_cache, &pol->state_cache_list);
  spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 }

 rcu_read_unlock();
 if (to_put)
  xfrm_state_put(to_put);

 if (read_seqcount_retry(&net->xfrm.xfrm_state_hash_generation, sequence)) {
  *err = -EAGAIN;
  if (x) {
   xfrm_state_put(x);
   x = NULL;
  }
 }

 return x;
}

struct xfrm_state *
xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
      xfrm_address_t *daddr, xfrm_address_t *saddr,
      unsigned short family, u8 mode, u8 proto, u32 reqid)
{
 unsigned int h;
 struct xfrm_state *rx = NULL, *x = NULL;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
 hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
  if (x->props.family == family &&
      x->props.reqid == reqid &&
      (mark & x->mark.m) == x->mark.v &&
      x->if_id == if_id &&
      !(x->props.flags & XFRM_STATE_WILDRECV) &&
      xfrm_state_addr_check(x, daddr, saddr, family) &&
      mode == x->props.mode &&
      proto == x->id.proto &&
      x->km.state == XFRM_STATE_VALID) {
   rx = x;
   break;
  }
 }

 if (rx)
  xfrm_state_hold(rx);
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);


 return rx;
}
EXPORT_SYMBOL(xfrm_stateonly_find);

struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi,
           unsigned short family)
{
 struct xfrm_state *x;
 struct xfrm_state_walk *w;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 list_for_each_entry(w, &net->xfrm.state_all, all) {
  x = container_of(w, struct xfrm_state, km);
  if (x->props.family != family ||
   x->id.spi != spi)
   continue;

  xfrm_state_hold(x);
  spin_unlock_bh(&net->xfrm.xfrm_state_lock);
  return x;
 }
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 return NULL;
}
EXPORT_SYMBOL(xfrm_state_lookup_byspi);

static struct xfrm_state *xfrm_state_lookup_spi_proto(struct net *net, __be32 spi, u8 proto)
{
 struct xfrm_state *x;
 unsigned int i;

 rcu_read_lock();
 for (i = 0; i <= net->xfrm.state_hmask; i++) {
  hlist_for_each_entry_rcu(x, &net->xfrm.state_byspi[i], byspi) {
   if (x->id.spi == spi && x->id.proto == proto) {
    if (!xfrm_state_hold_rcu(x))
     continue;
    rcu_read_unlock();
    return x;
   }
  }
 }
 rcu_read_unlock();
 return NULL;
}

static void __xfrm_state_insert(struct xfrm_state *x)
{
 struct net *net = xs_net(x);
 unsigned int h;

 list_add(&x->km.all, &net->xfrm.state_all);

 /* Sanitize mark before store */
 x->mark.v &= x->mark.m;

 h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
     x->props.reqid, x->props.family);
 XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
     x->xso.type);

 h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
 XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
     x->xso.type);

 if (x->id.spi) {
  h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
      x->props.family);

  XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h,
      x->xso.type);
 }

 if (x->km.seq) {
  h = xfrm_seq_hash(net, x->km.seq);

  XFRM_STATE_INSERT(byseq, &x->byseq, net->xfrm.state_byseq + h,
      x->xso.type);
 }

 hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
 if (x->replay_maxage)
  mod_timer(&x->rtimer, jiffies + x->replay_maxage);

 net->xfrm.state_num++;

 xfrm_hash_grow_check(net, x->bydst.next != NULL);
 xfrm_nat_keepalive_state_updated(x);
}

/* net->xfrm.xfrm_state_lock is held */
static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
{
 struct net *net = xs_net(xnew);
 unsigned short family = xnew->props.family;
 u32 reqid = xnew->props.reqid;
 struct xfrm_state *x;
 unsigned int h;
 u32 mark = xnew->mark.v & xnew->mark.m;
 u32 if_id = xnew->if_id;
 u32 cpu_id = xnew->pcpu_num;

 h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
 hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
  if (x->props.family == family &&
      x->props.reqid == reqid &&
      x->if_id  == if_id &&
      x->pcpu_num  == cpu_id &&
      (mark & x->mark.m) == x->mark.v &&
      xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
      xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family))
   x->genid++;
 }
}

void xfrm_state_insert(struct xfrm_state *x)
{
 struct net *net = xs_net(x);

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 __xfrm_state_bump_genids(x);
 __xfrm_state_insert(x);
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
}
EXPORT_SYMBOL(xfrm_state_insert);

/* net->xfrm.xfrm_state_lock is held */
static struct xfrm_state *__find_acq_core(struct net *net,
       const struct xfrm_mark *m,
       unsigned short family, u8 mode,
       u32 reqid, u32 if_id, u32 pcpu_num, u8 proto,
       const xfrm_address_t *daddr,
       const xfrm_address_t *saddr,
       int create)
{
 unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
 struct xfrm_state *x;
 u32 mark = m->v & m->m;

 hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
  if (x->props.reqid  != reqid ||
      x->props.mode   != mode ||
      x->props.family != family ||
      x->km.state     != XFRM_STATE_ACQ ||
      x->id.spi       != 0 ||
      x->id.proto     != proto ||
      (mark & x->mark.m) != x->mark.v ||
      x->pcpu_num != pcpu_num ||
      !xfrm_addr_equal(&x->id.daddr, daddr, family) ||
      !xfrm_addr_equal(&x->props.saddr, saddr, family))
   continue;

  xfrm_state_hold(x);
  return x;
 }

 if (!create)
  return NULL;

 x = xfrm_state_alloc(net);
 if (likely(x)) {
  switch (family) {
  case AF_INET:
   x->sel.daddr.a4 = daddr->a4;
   x->sel.saddr.a4 = saddr->a4;
   x->sel.prefixlen_d = 32;
   x->sel.prefixlen_s = 32;
   x->props.saddr.a4 = saddr->a4;
   x->id.daddr.a4 = daddr->a4;
   break;

  case AF_INET6:
   x->sel.daddr.in6 = daddr->in6;
   x->sel.saddr.in6 = saddr->in6;
   x->sel.prefixlen_d = 128;
   x->sel.prefixlen_s = 128;
   x->props.saddr.in6 = saddr->in6;
   x->id.daddr.in6 = daddr->in6;
   break;
  }

  x->pcpu_num = pcpu_num;
  x->km.state = XFRM_STATE_ACQ;
  x->id.proto = proto;
  x->props.family = family;
  x->props.mode = mode;
  x->props.reqid = reqid;
  x->if_id = if_id;
  x->mark.v = m->v;
  x->mark.m = m->m;
  x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
  xfrm_state_hold(x);
  hrtimer_start(&x->mtimer,
         ktime_set(net->xfrm.sysctl_acq_expires, 0),
         HRTIMER_MODE_REL_SOFT);
  list_add(&x->km.all, &net->xfrm.state_all);
  XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
      x->xso.type);
  h = xfrm_src_hash(net, daddr, saddr, family);
  XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
      x->xso.type);

  net->xfrm.state_num++;

  xfrm_hash_grow_check(net, x->bydst.next != NULL);
 }

 return x;
}

static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num);

int xfrm_state_add(struct xfrm_state *x)
{
 struct net *net = xs_net(x);
 struct xfrm_state *x1, *to_put;
 int family;
 int err;
 u32 mark = x->mark.v & x->mark.m;
 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);

 family = x->props.family;

 to_put = NULL;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);

 x1 = __xfrm_state_locate(x, use_spi, family);
 if (x1) {
  to_put = x1;
  x1 = NULL;
  err = -EEXIST;
  goto out;
 }

 if (use_spi && x->km.seq) {
  x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq, x->pcpu_num);
  if (x1 && ((x1->id.proto != x->id.proto) ||
      !xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) {
   to_put = x1;
   x1 = NULL;
  }
 }

 if (use_spi && !x1)
  x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
         x->props.reqid, x->if_id, x->pcpu_num, x->id.proto,
         &x->id.daddr, &x->props.saddr, 0);

 __xfrm_state_bump_genids(x);
 __xfrm_state_insert(x);
 err = 0;

out:
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 if (x1) {
  xfrm_state_delete(x1);
  xfrm_state_put(x1);
 }

 if (to_put)
  xfrm_state_put(to_put);

 return err;
}
EXPORT_SYMBOL(xfrm_state_add);

#ifdef CONFIG_XFRM_MIGRATE
static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *security)
{
 struct xfrm_user_sec_ctx *uctx;
 int size = sizeof(*uctx) + security->ctx_len;
 int err;

 uctx = kmalloc(size, GFP_KERNEL);
 if (!uctx)
  return -ENOMEM;

 uctx->exttype = XFRMA_SEC_CTX;
 uctx->len = size;
 uctx->ctx_doi = security->ctx_doi;
 uctx->ctx_alg = security->ctx_alg;
 uctx->ctx_len = security->ctx_len;
 memcpy(uctx + 1, security->ctx_str, security->ctx_len);
 err = security_xfrm_state_alloc(x, uctx);
 kfree(uctx);
 if (err)
  return err;

 return 0;
}

static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
        struct xfrm_encap_tmpl *encap,
        struct xfrm_migrate *m)
{
 struct net *net = xs_net(orig);
 struct xfrm_state *x = xfrm_state_alloc(net);
 if (!x)
  goto out;

 memcpy(&x->id, &orig->id, sizeof(x->id));
 memcpy(&x->sel, &orig->sel, sizeof(x->sel));
 memcpy(&x->lft, &orig->lft, sizeof(x->lft));
 x->props.mode = orig->props.mode;
 x->props.replay_window = orig->props.replay_window;
 x->props.reqid = orig->props.reqid;
 x->props.family = orig->props.family;
 x->props.saddr = orig->props.saddr;

 if (orig->aalg) {
  x->aalg = xfrm_algo_auth_clone(orig->aalg);
  if (!x->aalg)
   goto error;
 }
 x->props.aalgo = orig->props.aalgo;

 if (orig->aead) {
  x->aead = xfrm_algo_aead_clone(orig->aead);
  x->geniv = orig->geniv;
  if (!x->aead)
   goto error;
 }
 if (orig->ealg) {
  x->ealg = xfrm_algo_clone(orig->ealg);
  if (!x->ealg)
   goto error;
 }
 x->props.ealgo = orig->props.ealgo;

 if (orig->calg) {
  x->calg = xfrm_algo_clone(orig->calg);
  if (!x->calg)
   goto error;
 }
 x->props.calgo = orig->props.calgo;

 if (encap || orig->encap) {
  if (encap)
   x->encap = kmemdup(encap, sizeof(*x->encap),
     GFP_KERNEL);
  else
   x->encap = kmemdup(orig->encap, sizeof(*x->encap),
     GFP_KERNEL);

  if (!x->encap)
   goto error;
 }

 if (orig->security)
  if (clone_security(x, orig->security))
   goto error;

 if (orig->coaddr) {
  x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr),
        GFP_KERNEL);
  if (!x->coaddr)
   goto error;
 }

 if (orig->replay_esn) {
  if (xfrm_replay_clone(x, orig))
   goto error;
 }

 memcpy(&x->mark, &orig->mark, sizeof(x->mark));
 memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark));

 x->props.flags = orig->props.flags;
 x->props.extra_flags = orig->props.extra_flags;

 x->pcpu_num = orig->pcpu_num;
 x->if_id = orig->if_id;
 x->tfcpad = orig->tfcpad;
 x->replay_maxdiff = orig->replay_maxdiff;
 x->replay_maxage = orig->replay_maxage;
 memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft));
 x->km.state = orig->km.state;
 x->km.seq = orig->km.seq;
 x->replay = orig->replay;
 x->preplay = orig->preplay;
 x->mapping_maxage = orig->mapping_maxage;
 x->lastused = orig->lastused;
 x->new_mapping = 0;
 x->new_mapping_sport = 0;
 x->dir = orig->dir;

 x->mode_cbs = orig->mode_cbs;
 if (x->mode_cbs && x->mode_cbs->clone_state) {
  if (x->mode_cbs->clone_state(x, orig))
   goto error;
 }


 x->props.family = m->new_family;
 memcpy(&x->id.daddr, &m->new_daddr, sizeof(x->id.daddr));
 memcpy(&x->props.saddr, &m->new_saddr, sizeof(x->props.saddr));

 return x;

 error:
 xfrm_state_put(x);
out:
 return NULL;
}

struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
      u32 if_id)
{
 unsigned int h;
 struct xfrm_state *x = NULL;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);

 if (m->reqid) {
  h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr,
      m->reqid, m->old_family);
  hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
   if (x->props.mode != m->mode ||
       x->id.proto != m->proto)
    continue;
   if (m->reqid && x->props.reqid != m->reqid)
    continue;
   if (if_id != 0 && x->if_id != if_id)
    continue;
   if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
          m->old_family) ||
       !xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
          m->old_family))
    continue;
   xfrm_state_hold(x);
   break;
  }
 } else {
  h = xfrm_src_hash(net, &m->old_daddr, &m->old_saddr,
      m->old_family);
  hlist_for_each_entry(x, net->xfrm.state_bysrc+h, bysrc) {
   if (x->props.mode != m->mode ||
       x->id.proto != m->proto)
    continue;
   if (if_id != 0 && x->if_id != if_id)
    continue;
   if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
          m->old_family) ||
       !xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
          m->old_family))
    continue;
   xfrm_state_hold(x);
   break;
  }
 }

 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 return x;
}
EXPORT_SYMBOL(xfrm_migrate_state_find);

struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
          struct xfrm_migrate *m,
          struct xfrm_encap_tmpl *encap,
          struct net *net,
          struct xfrm_user_offload *xuo,
          struct netlink_ext_ack *extack)
{
 struct xfrm_state *xc;

 xc = xfrm_state_clone_and_setup(x, encap, m);
 if (!xc)
  return NULL;

 if (xfrm_init_state(xc) < 0)
  goto error;

 /* configure the hardware if offload is requested */
 if (xuo && xfrm_dev_state_add(net, xc, xuo, extack))
  goto error;

 /* add state */
 if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
  /* a care is needed when the destination address of the
   state is to be updated as it is a part of triplet */

  xfrm_state_insert(xc);
 } else {
  if (xfrm_state_add(xc) < 0)
   goto error;
 }

 return xc;
error:
 xfrm_state_put(xc);
 return NULL;
}
EXPORT_SYMBOL(xfrm_state_migrate);
#endif

int xfrm_state_update(struct xfrm_state *x)
{
 struct xfrm_state *x1, *to_put;
 int err;
 int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);
 struct net *net = xs_net(x);

 to_put = NULL;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 x1 = __xfrm_state_locate(x, use_spi, x->props.family);

 err = -ESRCH;
 if (!x1)
  goto out;

 if (xfrm_state_kern(x1)) {
  to_put = x1;
  err = -EEXIST;
  goto out;
 }

 if (x1->km.state == XFRM_STATE_ACQ) {
  if (x->dir && x1->dir != x->dir)
   goto out;

  __xfrm_state_insert(x);
  x = NULL;
 } else {
  if (x1->dir != x->dir)
   goto out;
 }
 err = 0;

out:
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 if (to_put)
  xfrm_state_put(to_put);

 if (err)
  return err;

 if (!x) {
  xfrm_state_delete(x1);
  xfrm_state_put(x1);
  return 0;
 }

 err = -EINVAL;
 spin_lock_bh(&x1->lock);
 if (likely(x1->km.state == XFRM_STATE_VALID)) {
  if (x->encap && x1->encap &&
      x->encap->encap_type == x1->encap->encap_type)
   memcpy(x1->encap, x->encap, sizeof(*x1->encap));
  else if (x->encap || x1->encap)
   goto fail;

  if (x->coaddr && x1->coaddr) {
   memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));
  }
  if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))
   memcpy(&x1->sel, &x->sel, sizeof(x1->sel));
  memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
  x1->km.dying = 0;

  hrtimer_start(&x1->mtimer, ktime_set(1, 0),
         HRTIMER_MODE_REL_SOFT);
  if (READ_ONCE(x1->curlft.use_time))
   xfrm_state_check_expire(x1);

  if (x->props.smark.m || x->props.smark.v || x->if_id) {
   spin_lock_bh(&net->xfrm.xfrm_state_lock);

   if (x->props.smark.m || x->props.smark.v)
    x1->props.smark = x->props.smark;

   if (x->if_id)
    x1->if_id = x->if_id;

   __xfrm_state_bump_genids(x1);
   spin_unlock_bh(&net->xfrm.xfrm_state_lock);
  }

  err = 0;
  x->km.state = XFRM_STATE_DEAD;
  __xfrm_state_put(x);
 }

fail:
 spin_unlock_bh(&x1->lock);

 xfrm_state_put(x1);

 return err;
}
EXPORT_SYMBOL(xfrm_state_update);

int xfrm_state_check_expire(struct xfrm_state *x)
{
 /* All counters which are needed to decide if state is expired
 * are handled by SW for non-packet offload modes. Simply skip
 * the following update and save extra boilerplate in drivers.
 */

 if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
  xfrm_dev_state_update_stats(x);

 if (!READ_ONCE(x->curlft.use_time))
  WRITE_ONCE(x->curlft.use_time, ktime_get_real_seconds());

 if (x->curlft.bytes >= x->lft.hard_byte_limit ||
     x->curlft.packets >= x->lft.hard_packet_limit) {
  x->km.state = XFRM_STATE_EXPIRED;
  hrtimer_start(&x->mtimer, 0, HRTIMER_MODE_REL_SOFT);
  return -EINVAL;
 }

 if (!x->km.dying &&
     (x->curlft.bytes >= x->lft.soft_byte_limit ||
      x->curlft.packets >= x->lft.soft_packet_limit)) {
  x->km.dying = 1;
  km_state_expired(x, 0, 0);
 }
 return 0;
}
EXPORT_SYMBOL(xfrm_state_check_expire);

void xfrm_state_update_stats(struct net *net)
{
 struct xfrm_state *x;
 int i;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 for (i = 0; i <= net->xfrm.state_hmask; i++) {
  hlist_for_each_entry(x, net->xfrm.state_bydst + i, bydst)
   xfrm_dev_state_update_stats(x);
 }
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
}

struct xfrm_state *
xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi,
    u8 proto, unsigned short family)
{
 struct xfrm_hash_state_ptrs state_ptrs;
 struct xfrm_state *x;

 rcu_read_lock();
 xfrm_hash_ptrs_get(net, &state_ptrs);

 x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);
 rcu_read_unlock();
 return x;
}
EXPORT_SYMBOL(xfrm_state_lookup);

struct xfrm_state *
xfrm_state_lookup_byaddr(struct net *net, u32 mark,
    const xfrm_address_t *daddr, const xfrm_address_t *saddr,
    u8 proto, unsigned short family)
{
 struct xfrm_hash_state_ptrs state_ptrs;
 struct xfrm_state *x;

 rcu_read_lock();

 xfrm_hash_ptrs_get(net, &state_ptrs);

 x = __xfrm_state_lookup_byaddr(&state_ptrs, mark, daddr, saddr, proto, family);
 rcu_read_unlock();
 return x;
}
EXPORT_SYMBOL(xfrm_state_lookup_byaddr);

struct xfrm_state *
xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
       u32 if_id, u32 pcpu_num, u8 proto, const xfrm_address_t *daddr,
       const xfrm_address_t *saddr, int create, unsigned short family)
{
 struct xfrm_state *x;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 x = __find_acq_core(net, mark, family, mode, reqid, if_id, pcpu_num,
       proto, daddr, saddr, create);
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);

 return x;
}
EXPORT_SYMBOL(xfrm_find_acq);

#ifdef CONFIG_XFRM_SUB_POLICY
#if IS_ENABLED(CONFIG_IPV6)
/* distribution counting sort function for xfrm_state and xfrm_tmpl */
static void
__xfrm6_sort(void **dst, void **src, int n,
      int (*cmp)(const void *p), int maxclass)
{
 int count[XFRM_MAX_DEPTH] = { };
 int class[XFRM_MAX_DEPTH];
 int i;

 for (i = 0; i < n; i++) {
  int c = cmp(src[i]);

  class[i] = c;
  count[c]++;
 }

 for (i = 2; i < maxclass; i++)
  count[i] += count[i - 1];

 for (i = 0; i < n; i++) {
  dst[count[class[i] - 1]++] = src[i];
  src[i] = NULL;
 }
}

/* Rule for xfrm_state:
 *
 * rule 1: select IPsec transport except AH
 * rule 2: select MIPv6 RO or inbound trigger
 * rule 3: select IPsec transport AH
 * rule 4: select IPsec tunnel
 * rule 5: others
 */

static int __xfrm6_state_sort_cmp(const void *p)
{
 const struct xfrm_state *v = p;

 switch (v->props.mode) {
 case XFRM_MODE_TRANSPORT:
  if (v->id.proto != IPPROTO_AH)
   return 1;
  else
   return 3;
#if IS_ENABLED(CONFIG_IPV6_MIP6)
 case XFRM_MODE_ROUTEOPTIMIZATION:
 case XFRM_MODE_IN_TRIGGER:
  return 2;
#endif
 case XFRM_MODE_TUNNEL:
 case XFRM_MODE_BEET:
 case XFRM_MODE_IPTFS:
  return 4;
 }
 return 5;
}

/* Rule for xfrm_tmpl:
 *
 * rule 1: select IPsec transport
 * rule 2: select MIPv6 RO or inbound trigger
 * rule 3: select IPsec tunnel
 * rule 4: others
 */

static int __xfrm6_tmpl_sort_cmp(const void *p)
{
 const struct xfrm_tmpl *v = p;

 switch (v->mode) {
 case XFRM_MODE_TRANSPORT:
  return 1;
#if IS_ENABLED(CONFIG_IPV6_MIP6)
 case XFRM_MODE_ROUTEOPTIMIZATION:
 case XFRM_MODE_IN_TRIGGER:
  return 2;
#endif
 case XFRM_MODE_TUNNEL:
 case XFRM_MODE_BEET:
 case XFRM_MODE_IPTFS:
  return 3;
 }
 return 4;
}
#else
static inline int __xfrm6_state_sort_cmp(const void *p) { return 5; }
static inline int __xfrm6_tmpl_sort_cmp(const void *p) { return 4; }

static inline void
__xfrm6_sort(void **dst, void **src, int n,
      int (*cmp)(const void *p), int maxclass)
{
 int i;

 for (i = 0; i < n; i++)
  dst[i] = src[i];
}
#endif /* CONFIG_IPV6 */

void
xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
        unsigned short family)
{
 int i;

 if (family == AF_INET6)
  __xfrm6_sort((void **)dst, (void **)src, n,
        __xfrm6_tmpl_sort_cmp, 5);
 else
  for (i = 0; i < n; i++)
   dst[i] = src[i];
}

void
xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
  unsigned short family)
{
 int i;

 if (family == AF_INET6)
  __xfrm6_sort((void **)dst, (void **)src, n,
        __xfrm6_state_sort_cmp, 6);
 else
  for (i = 0; i < n; i++)
   dst[i] = src[i];
}
#endif

/* Silly enough, but I'm lazy to build resolution list */

static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num)
{
 unsigned int h = xfrm_seq_hash(net, seq);
 struct xfrm_state *x;

 hlist_for_each_entry_rcu(x, net->xfrm.state_byseq + h, byseq) {
  if (x->km.seq == seq &&
      (mark & x->mark.m) == x->mark.v &&
      x->pcpu_num == pcpu_num &&
      x->km.state == XFRM_STATE_ACQ) {
   xfrm_state_hold(x);
   return x;
  }
 }

 return NULL;
}

struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num)
{
 struct xfrm_state *x;

 spin_lock_bh(&net->xfrm.xfrm_state_lock);
 x = __xfrm_find_acq_byseq(net, mark, seq, pcpu_num);
 spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 return x;
}
EXPORT_SYMBOL(xfrm_find_acq_byseq);

u32 xfrm_get_acqseq(void)
{
 u32 res;
 static atomic_t acqseq;

 do {
  res = atomic_inc_return(&acqseq);
 } while (!res);

 return res;
}
EXPORT_SYMBOL(xfrm_get_acqseq);

int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack)
{
 switch (proto) {
 case IPPROTO_AH:
 case IPPROTO_ESP:
  break;

 case IPPROTO_COMP:
  /* IPCOMP spi is 16-bits. */
  if (max >= 0x10000) {
   NL_SET_ERR_MSG(extack, "IPCOMP SPI must be <= 65535");
   return -EINVAL;
  }
  break;

 default:
  NL_SET_ERR_MSG(extack, "Invalid protocol, must be one of AH, ESP, IPCOMP");
  return -EINVAL;
 }

 if (min > max) {
  NL_SET_ERR_MSG(extack, "Invalid SPI range: min > max");
  return -EINVAL;
 }

 return 0;
}
EXPORT_SYMBOL(verify_spi_info);

int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
     struct netlink_ext_ack *extack)
{
 struct net *net = xs_net(x);
 unsigned int h;
 struct xfrm_state *x0;
 int err = -ENOENT;
 u32 range = high - low + 1;
 __be32 newspi = 0;

 spin_lock_bh(&x->lock);
 if (x->km.state == XFRM_STATE_DEAD) {
  NL_SET_ERR_MSG(extack, "Target ACQUIRE is in DEAD state");
  goto unlock;
 }

 err = 0;
 if (x->id.spi)
  goto unlock;

 err = -ENOENT;

 for (h = 0; h < range; h++) {
  u32 spi = (low == high) ? low : get_random_u32_inclusive(low, high);
  if (spi == 0)
   goto next;
  newspi = htonl(spi);

  spin_lock_bh(&net->xfrm.xfrm_state_lock);
  x0 = xfrm_state_lookup_spi_proto(net, newspi, x->id.proto);
  if (!x0) {
   x->id.spi = newspi;
   h = xfrm_spi_hash(net, &x->id.daddr, newspi, x->id.proto, x->props.family);
   XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, x->xso.type);
   spin_unlock_bh(&net->xfrm.xfrm_state_lock);
   err = 0;
   goto unlock;
  }
  xfrm_state_put(x0);
  spin_unlock_bh(&net->xfrm.xfrm_state_lock);

next:
  if (signal_pending(current)) {
   err = -ERESTARTSYS;
   goto unlock;
  }

  if (low == high)
   break;
 }

 if (err)
  NL_SET_ERR_MSG(extack, "No SPI available in the requested range");

unlock:
 spin_unlock_bh(&x->lock);

 return err;
}
EXPORT_SYMBOL(xfrm_alloc_spi);

static bool __xfrm_state_filter_match(struct xfrm_state *x,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.