cregit-Linux how code gets into the kernel

Release 4.8 net/xfrm/xfrm_state.c

Directory: net/xfrm
/*
 * 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/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 <asm/uaccess.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

#include "xfrm_hash.h"

/* 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 inline unsigned int xfrm_dst_hash(struct net *net, const xfrm_address_t *daddr, const xfrm_address_t *saddr, u32 reqid, unsigned short family) { return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller3879.17%571.43%
alexey dobriyanalexey dobriyan1020.83%228.57%
Total48100.00%7100.00%


static inline unsigned int xfrm_src_hash(struct net *net, const xfrm_address_t *daddr, const xfrm_address_t *saddr, unsigned short family) { return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller2455.81%450.00%
alexey dobriyanalexey dobriyan1023.26%225.00%
masahide nakamuramasahide nakamura818.60%112.50%
hideaki yoshifujihideaki yoshifuji12.33%112.50%
Total43100.00%8100.00%


static inline unsigned int xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) { return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller3576.09%457.14%
alexey dobriyanalexey dobriyan1021.74%228.57%
al viroal viro12.17%114.29%
Total46100.00%7100.00%


static void xfrm_hash_transfer(struct hlist_head *list, struct hlist_head *ndsttable, struct hlist_head *nsrctable, struct hlist_head *nspitable, 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); hlist_add_head(&x->bydst, ndsttable+h); h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family, nhashmask); hlist_add_head(&x->bysrc, nsrctable+h); if (x->id.spi) { h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family, nhashmask); hlist_add_head(&x->byspi, nspitable+h); } } }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller17791.24%360.00%
masahide nakamuramasahide nakamura178.76%240.00%
Total194100.00%5100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller2485.71%133.33%
alexey dobriyanalexey dobriyan414.29%266.67%
Total28100.00%3100.00%


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, *odst, *osrc, *ospi; 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; } spin_lock_bh(&net->xfrm.xfrm_state_lock); nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; for (i = net->xfrm.state_hmask; i >= 0; i--) xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi, nhashmask); odst = net->xfrm.state_bydst; osrc = net->xfrm.state_bysrc; ospi = net->xfrm.state_byspi; ohashmask = net->xfrm.state_hmask; net->xfrm.state_bydst = ndst; net->xfrm.state_bysrc = nsrc; net->xfrm.state_byspi = nspi; net->xfrm.state_hmask = nhashmask; spin_unlock_bh(&net->xfrm.xfrm_state_lock); osize = (ohashmask + 1) * sizeof(struct hlist_head); xfrm_hash_free(odst, osize); xfrm_hash_free(osrc, osize); xfrm_hash_free(ospi, osize); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller21971.34%327.27%
alexey dobriyanalexey dobriyan7524.43%545.45%
fan dufan du82.61%19.09%
ying xueying xue30.98%19.09%
david howellsdavid howells20.65%19.09%
Total307100.00%11100.00%

static DEFINE_SPINLOCK(xfrm_state_afinfo_lock); static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO]; static DEFINE_SPINLOCK(xfrm_state_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); bool km_is_alive(const struct km_event *c); void km_state_expired(struct xfrm_state *x, int hard, u32 portid); static DEFINE_SPINLOCK(xfrm_type_lock);
int xfrm_register_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; spin_lock_bh(&xfrm_type_lock); if (likely(typemap[type->proto] == NULL)) typemap[type->proto] = type; else err = -EEXIST; spin_unlock_bh(&xfrm_type_lock); xfrm_state_put_afinfo(afinfo); return err; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu8984.76%133.33%
americo wangamerico wang1413.33%133.33%
eric dumazeteric dumazet21.90%133.33%
Total105100.00%3100.00%

EXPORT_SYMBOL(xfrm_register_type);
int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; spin_lock_bh(&xfrm_type_lock); if (unlikely(typemap[type->proto] != type)) err = -ENOENT; else typemap[type->proto] = NULL; spin_unlock_bh(&xfrm_type_lock); xfrm_state_put_afinfo(afinfo); return err; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu8984.76%133.33%
americo wangamerico wang1413.33%133.33%
eric dumazeteric dumazet21.90%133.33%
Total105100.00%3100.00%

EXPORT_SYMBOL(xfrm_unregister_type);
static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) { struct xfrm_state_afinfo *afinfo; const struct xfrm_type **typemap; const struct xfrm_type *type; int modload_attempted = 0; retry: afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return NULL; typemap = afinfo->type_map; type = typemap[proto]; if (unlikely(type && !try_module_get(type->owner))) type = NULL; if (!type && !modload_attempted) { xfrm_state_put_afinfo(afinfo); request_module("xfrm-type-%d-%d", family, proto); modload_attempted = 1; goto retry; } xfrm_state_put_afinfo(afinfo); return type; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu12897.71%150.00%
eric dumazeteric dumazet32.29%150.00%
Total131100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu1894.74%150.00%
eric dumazeteric dumazet15.26%150.00%
Total19100.00%2100.00%

static DEFINE_SPINLOCK(xfrm_mode_lock);
int xfrm_register_mode(struct xfrm_mode *mode, int family) { struct xfrm_state_afinfo *afinfo; struct xfrm_mode **modemap; int err; if (unlikely(mode->encap >= XFRM_MODE_MAX)) return -EINVAL; afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -EEXIST; modemap = afinfo->mode_map; spin_lock_bh(&xfrm_mode_lock); if (modemap[mode->encap]) goto out; err = -ENOENT; if (!try_module_get(afinfo->owner)) goto out; mode->afinfo = afinfo; modemap[mode->encap] = mode; err = 0; out: spin_unlock_bh(&xfrm_mode_lock); xfrm_state_put_afinfo(afinfo); return err; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu13090.28%266.67%
americo wangamerico wang149.72%133.33%
Total144100.00%3100.00%

EXPORT_SYMBOL(xfrm_register_mode);
int xfrm_unregister_mode(struct xfrm_mode *mode, int family) { struct xfrm_state_afinfo *afinfo; struct xfrm_mode **modemap; int err; if (unlikely(mode->encap >= XFRM_MODE_MAX)) return -EINVAL; afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -ENOENT; modemap = afinfo->mode_map; spin_lock_bh(&xfrm_mode_lock); if (likely(modemap[mode->encap] == mode)) { modemap[mode->encap] = NULL; module_put(mode->afinfo->owner); err = 0; } spin_unlock_bh(&xfrm_mode_lock); xfrm_state_put_afinfo(afinfo); return err; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu11789.31%266.67%
americo wangamerico wang1410.69%133.33%
Total131100.00%3100.00%

EXPORT_SYMBOL(xfrm_unregister_mode);
static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family) { struct xfrm_state_afinfo *afinfo; struct xfrm_mode *mode; int modload_attempted = 0; if (unlikely(encap >= XFRM_MODE_MAX)) return NULL; retry: afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return NULL; mode = afinfo->mode_map[encap]; if (unlikely(mode && !try_module_get(mode->owner))) mode = NULL; if (!mode && !modload_attempted) { xfrm_state_put_afinfo(afinfo); request_module("xfrm-mode-%d-%d", family, encap); modload_attempted = 1; goto retry; } xfrm_state_put_afinfo(afinfo); return mode; }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu130100.00%1100.00%
Total130100.00%1100.00%


static void xfrm_put_mode(struct xfrm_mode *mode) { module_put(mode->owner); }

Contributors

PersonTokensPropCommitsCommitProp
herbert xuherbert xu18100.00%1100.00%
Total18100.00%1100.00%


static void xfrm_state_gc_destroy(struct xfrm_state *x) { tasklet_hrtimer_cancel(&x->mtimer); del_timer_sync(&x->rtimer); kfree(x->aead); kfree(x->aalg); kfree(x->ealg); kfree(x->calg); kfree(x->encap); kfree(x->coaddr); kfree(x->replay_esn); kfree(x->preplay_esn); if (x->inner_mode) xfrm_put_mode(x->inner_mode); if (x->inner_mode_iaf) xfrm_put_mode(x->inner_mode_iaf); if (x->outer_mode) xfrm_put_mode(x->outer_mode); if (x->type) { x->type->destructor(x); xfrm_put_type(x->type); } security_xfrm_state_free(x); kfree(x); }

Contributors

PersonTokensPropCommitsCommitProp
james morrisjames morris7447.44%216.67%
herbert xuherbert xu2616.67%216.67%
steffen klassertsteffen klassert148.97%18.33%
kazunori miyazawakazunori miyazawa138.33%18.33%
noriaki takamiyanoriaki takamiya74.49%18.33%
jamal hadi salimjamal hadi salim74.49%18.33%
ilan tayariilan tayari74.49%18.33%
trent jaegertrent jaeger53.21%18.33%
yury polyanskiyyury polyanskiy21.28%18.33%
david s. millerdavid s. miller10.64%18.33%
Total156100.00%12100.00%


static void xfrm_state_gc_task(struct work_struct *work) { struct net *net = container_of(work, struct net, xfrm.state_gc_work); struct xfrm_state *x; struct hlist_node *tmp; struct hlist_head gc_list; spin_lock_bh(&xfrm_state_gc_lock); hlist_move_list(&net->xfrm.state_gc_list, &gc_list); spin_unlock_bh(&xfrm_state_gc_lock); hlist_for_each_entry_safe(x, tmp, &gc_list, gclist) xfrm_state_gc_destroy(x); }

Contributors

PersonTokensPropCommitsCommitProp
james morrisjames morris4150.00%114.29%
alexey dobriyanalexey dobriyan2328.05%228.57%
herbert xuherbert xu1417.07%228.57%
david s. millerdavid s. miller22.44%114.29%
david howellsdavid howells22.44%114.29%
Total82100.00%7100.00%


static inline unsigned long make_jiffies(long secs) { if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) return MAX_SCHEDULE_TIMEOUT-1; else return secs*HZ; }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov34100.00%2100.00%
Total34100.00%2100.00%


static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) { struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer); struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer); unsigned long now = get_seconds(); long next = LONG_MAX; int warn = 0; int err = 0; spin_lock(&x->lock); 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) { long 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. * workarbound: 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) { long tmo = x->lft.hard_use_expires_seconds + (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) { long 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) { long tmo = x->lft.soft_use_expires_seconds + (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 != LONG_MAX) { tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL); } 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 HRTIMER_NORESTART; }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov33266.94%214.29%
fan dufan du6312.70%17.14%
yury polyanskiyyury polyanskiy408.06%17.14%
joy lattenjoy latten244.84%214.29%
herbert xuherbert xu183.63%214.29%
david s. millerdavid s. miller122.42%321.43%
jamal hadi salimjamal hadi salim40.81%17.14%
james morrisjames morris20.40%17.14%
tetsuo handatetsuo handa10.20%17.14%
Total496100.00%14100.00%

static void xfrm_replay_timer_handler(unsigned long data);
struct xfrm_state *xfrm_state_alloc(struct net *net) { struct xfrm_state *x; x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); if (x) { write_pnet(&x->xs_net, net); atomic_set(&x->refcnt, 1); atomic_set(&x->tunnel_users, 0); INIT_LIST_HEAD(&x->km.all); INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->byspi); tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_BOOTTIME, HRTIMER_MODE_ABS); setup_timer(&x->rtimer, xfrm_replay_timer_handler, (unsigned long)x); x->curlft.add_time = get_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->inner_mode = NULL; x->inner_mode_iaf = NULL; spin_lock_init(&x->lock); } return x; }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov10751.94%212.50%
jamal hadi salimjamal hadi salim2411.65%16.25%
alexey dobriyanalexey dobriyan146.80%16.25%
kazunori miyazawakazunori miyazawa125.83%16.25%
james morrisjames morris125.83%212.50%
timo terastimo teras83.88%16.25%
pavel emelianovpavel emelianov73.40%16.25%
masahide nakamuramasahide nakamura73.40%16.25%
yury polyanskiyyury polyanskiy41.94%16.25%
andrew mortonandrew morton41.94%16.25%
david s. millerdavid s. miller31.46%16.25%
herbert xuherbert xu20.97%16.25%
panagiotis issaris*panagiotis issaris*10.49%16.25%
fan dufan du10.49%16.25%
Total206100.00%16100.00%

EXPORT_SYMBOL(xfrm_state_alloc);
void __xfrm_state_destroy(struct xfrm_state *x) { struct net *net = xs_net(x); WARN_ON(x->km.state != XFRM_STATE_DEAD); spin_lock_bh(&xfrm_state_gc_lock); hlist_add_head(&x->gclist, &net->xfrm.state_gc_list); spin_unlock_bh(&xfrm_state_gc_lock); schedule_work(&net->xfrm.state_gc_work); }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov2942.65%327.27%
alexey dobriyanalexey dobriyan2029.41%327.27%
james morrisjames morris1014.71%19.09%
derek atkinsderek atkins57.35%19.09%
herbert xuherbert xu22.94%218.18%
ilpo jarvinenilpo jarvinen22.94%19.09%
Total68100.00%11100.00%

EXPORT_SYMBOL(__xfrm_state_destroy);
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(&x->bydst); hlist_del(&x->bysrc); if (x->id.spi) hlist_del(&x->byspi); net->xfrm.state_num--; spin_unlock(&net->xfrm.xfrm_state_lock); /* 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; }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov6248.44%318.75%
alexey dobriyanalexey dobriyan1511.72%212.50%
jamal hadi salimjamal hadi salim1410.94%16.25%
herbert xuherbert xu118.59%318.75%
fan dufan du86.25%16.25%
masahide nakamuramasahide nakamura75.47%16.25%
david s. millerdavid s. miller53.91%212.50%
james morrisjames morris53.91%212.50%
patrick mchardypatrick mchardy10.78%16.25%
Total128100.00%16100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
alexey kuznetsovalexey kuznetsov3076.92%150.00%
jamal hadi salimjamal hadi salim923.08%150.00%
Total39100.00%2100.00%

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