📄 xfrm_state.c
字号:
/* * 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 <asm/uaccess.h>#include "xfrm_hash.h"struct sock *xfrm_nl;EXPORT_SYMBOL(xfrm_nl);u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);u32 sysctl_xfrm_acq_expires __read_mostly = 30;/* 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 DEFINE_SPINLOCK(xfrm_state_lock);/* Hash table to find appropriate SA towards given target (endpoint * of tunnel or destination of transport mode) allowed by selector. * * Main use is finding SA after policy selected tunnel or transport mode. * Also, it can be used by ah/esp icmp error handler to find offending SA. */static struct hlist_head *xfrm_state_bydst __read_mostly;static struct hlist_head *xfrm_state_bysrc __read_mostly;static struct hlist_head *xfrm_state_byspi __read_mostly;static unsigned int xfrm_state_hmask __read_mostly;static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;static unsigned int xfrm_state_num;static unsigned int xfrm_state_genid;static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, u32 reqid, unsigned short family){ return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);}static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family){ return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);}static inline unsigned intxfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family){ return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);}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 *entry, *tmp; struct xfrm_state *x; hlist_for_each_entry_safe(x, entry, 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); } }}static unsigned long xfrm_hash_new_size(void){ return ((xfrm_state_hmask + 1) << 1) * sizeof(struct hlist_head);}static DEFINE_MUTEX(hash_resize_mutex);static void xfrm_hash_resize(struct work_struct *__unused){ struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; unsigned long nsize, osize; unsigned int nhashmask, ohashmask; int i; mutex_lock(&hash_resize_mutex); nsize = xfrm_hash_new_size(); ndst = xfrm_hash_alloc(nsize); if (!ndst) goto out_unlock; nsrc = xfrm_hash_alloc(nsize); if (!nsrc) { xfrm_hash_free(ndst, nsize); goto out_unlock; } nspi = xfrm_hash_alloc(nsize); if (!nspi) { xfrm_hash_free(ndst, nsize); xfrm_hash_free(nsrc, nsize); goto out_unlock; } spin_lock_bh(&xfrm_state_lock); nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; for (i = xfrm_state_hmask; i >= 0; i--) xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi, nhashmask); odst = xfrm_state_bydst; osrc = xfrm_state_bysrc; ospi = xfrm_state_byspi; ohashmask = xfrm_state_hmask; xfrm_state_bydst = ndst; xfrm_state_bysrc = nsrc; xfrm_state_byspi = nspi; xfrm_state_hmask = nhashmask; spin_unlock_bh(&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);out_unlock: mutex_unlock(&hash_resize_mutex);}static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);DECLARE_WAIT_QUEUE_HEAD(km_waitq);EXPORT_SYMBOL(km_waitq);static DEFINE_RWLOCK(xfrm_state_afinfo_lock);static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];static struct work_struct xfrm_state_gc_work;static HLIST_HEAD(xfrm_state_gc_list);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);void km_state_expired(struct xfrm_state *x, int hard, u32 pid);static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family){ struct xfrm_state_afinfo *afinfo; if (unlikely(family >= NPROTO)) return NULL; write_lock_bh(&xfrm_state_afinfo_lock); afinfo = xfrm_state_afinfo[family]; if (unlikely(!afinfo)) write_unlock_bh(&xfrm_state_afinfo_lock); return afinfo;}static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo){ write_unlock_bh(&xfrm_state_afinfo_lock);}int xfrm_register_type(struct xfrm_type *type, unsigned short family){ struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; if (likely(typemap[type->proto] == NULL)) typemap[type->proto] = type; else err = -EEXIST; xfrm_state_unlock_afinfo(afinfo); return err;}EXPORT_SYMBOL(xfrm_register_type);int xfrm_unregister_type(struct xfrm_type *type, unsigned short family){ struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; if (unlikely(typemap[type->proto] != type)) err = -ENOENT; else typemap[type->proto] = NULL; xfrm_state_unlock_afinfo(afinfo); return err;}EXPORT_SYMBOL(xfrm_unregister_type);static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family){ struct xfrm_state_afinfo *afinfo; struct xfrm_type **typemap; 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;}static void xfrm_put_type(struct xfrm_type *type){ module_put(type->owner);}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_lock_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -EEXIST; modemap = afinfo->mode_map; 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: xfrm_state_unlock_afinfo(afinfo); return err;}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_lock_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -ENOENT; modemap = afinfo->mode_map; if (likely(modemap[mode->encap] == mode)) { modemap[mode->encap] = NULL; module_put(mode->afinfo->owner); err = 0; } xfrm_state_unlock_afinfo(afinfo); return err;}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;}static void xfrm_put_mode(struct xfrm_mode *mode){ module_put(mode->owner);}static void xfrm_state_gc_destroy(struct xfrm_state *x){ del_timer_sync(&x->timer); del_timer_sync(&x->rtimer); kfree(x->aalg); kfree(x->ealg); kfree(x->calg); kfree(x->encap); kfree(x->coaddr); if (x->inner_mode) xfrm_put_mode(x->inner_mode); 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);}static void xfrm_state_gc_task(struct work_struct *data){ struct xfrm_state *x; struct hlist_node *entry, *tmp; struct hlist_head gc_list; spin_lock_bh(&xfrm_state_gc_lock); gc_list.first = xfrm_state_gc_list.first; INIT_HLIST_HEAD(&xfrm_state_gc_list); spin_unlock_bh(&xfrm_state_gc_lock); hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst) xfrm_state_gc_destroy(x); wake_up(&km_waitq);}static inline unsigned long make_jiffies(long secs){ if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) return MAX_SCHEDULE_TIMEOUT-1; else return secs*HZ;}static void xfrm_timer_handler(unsigned long data){ struct xfrm_state *x = (struct xfrm_state*)data; 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) 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; else if (tmo < next) next = 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) mod_timer(&x->timer, jiffies + make_jiffies(next)); goto out;expired: if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { x->km.state = XFRM_STATE_EXPIRED; wake_up(&km_waitq); next = 2; goto resched; } err = __xfrm_state_delete(x); if (!err && x->id.spi) km_state_expired(x, 1, 0); xfrm_audit_state_delete(x, err ? 0 : 1, audit_get_loginuid(current->audit_context), 0);out: spin_unlock(&x->lock);}static void xfrm_replay_timer_handler(unsigned long data);struct xfrm_state *xfrm_state_alloc(void){ struct xfrm_state *x; x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); if (x) { atomic_set(&x->refcnt, 1); atomic_set(&x->tunnel_users, 0); INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->byspi); init_timer(&x->timer); x->timer.function = xfrm_timer_handler; x->timer.data = (unsigned long)x; init_timer(&x->rtimer); x->rtimer.function = xfrm_replay_timer_handler; x->rtimer.data = (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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -