11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * xfrm_state.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Changes: 51da177e4SLinus Torvalds * Mitsuru KANDA @USAGI 61da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 71da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 81da177e4SLinus Torvalds * IPv6 support 91da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 101da177e4SLinus Torvalds * Split up af-specific functions 111da177e4SLinus Torvalds * Derek Atkins <derek@ihtfp.com> 121da177e4SLinus Torvalds * Add UDP Encapsulation 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds #include <linux/workqueue.h> 171da177e4SLinus Torvalds #include <net/xfrm.h> 181da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 191da177e4SLinus Torvalds #include <linux/ipsec.h> 201da177e4SLinus Torvalds #include <linux/module.h> 21f034b5d4SDavid S. Miller #include <linux/cache.h> 221da177e4SLinus Torvalds #include <asm/uaccess.h> 231da177e4SLinus Torvalds 2444e36b42SDavid S. Miller #include "xfrm_hash.h" 2544e36b42SDavid S. Miller 26ee857a7dSDavid S. Miller struct sock *xfrm_nl; 27ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl); 28ee857a7dSDavid S. Miller 29f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME; 30a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime); 31a70fcb0bSDavid S. Miller 32f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE; 33a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth); 34a70fcb0bSDavid S. Miller 351da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables: 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl) 38a624c108SDavid S. Miller 2. Hash table by (daddr,family,reqid) to find what SAs exist for given 391da177e4SLinus Torvalds destination/tunnel endpoint. (output) 401da177e4SLinus Torvalds */ 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock); 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint 451da177e4SLinus Torvalds * of tunnel or destination of transport mode) allowed by selector. 461da177e4SLinus Torvalds * 471da177e4SLinus Torvalds * Main use is finding SA after policy selected tunnel or transport mode. 481da177e4SLinus Torvalds * Also, it can be used by ah/esp icmp error handler to find offending SA. 491da177e4SLinus Torvalds */ 50f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly; 51f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly; 52f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly; 53f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly; 54f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; 55f034b5d4SDavid S. Miller static unsigned int xfrm_state_num; 569d4a706dSDavid S. Miller static unsigned int xfrm_state_genid; 571da177e4SLinus Torvalds 58c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, 59c1969f29SDavid S. Miller xfrm_address_t *saddr, 60c1969f29SDavid S. Miller u32 reqid, 61a624c108SDavid S. Miller unsigned short family) 62a624c108SDavid S. Miller { 63c1969f29SDavid S. Miller return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask); 64a624c108SDavid S. Miller } 65a624c108SDavid S. Miller 6644e36b42SDavid S. Miller static inline unsigned int xfrm_src_hash(xfrm_address_t *addr, 6744e36b42SDavid S. Miller unsigned short family) 68f034b5d4SDavid S. Miller { 69f034b5d4SDavid S. Miller return __xfrm_src_hash(addr, family, xfrm_state_hmask); 70f034b5d4SDavid S. Miller } 71f034b5d4SDavid S. Miller 722575b654SDavid S. Miller static inline unsigned int 738122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 74f034b5d4SDavid S. Miller { 75c1969f29SDavid S. Miller return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask); 76f034b5d4SDavid S. Miller } 77f034b5d4SDavid S. Miller 78f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list, 79f034b5d4SDavid S. Miller struct hlist_head *ndsttable, 80f034b5d4SDavid S. Miller struct hlist_head *nsrctable, 81f034b5d4SDavid S. Miller struct hlist_head *nspitable, 82f034b5d4SDavid S. Miller unsigned int nhashmask) 83f034b5d4SDavid S. Miller { 84f034b5d4SDavid S. Miller struct hlist_node *entry, *tmp; 85f034b5d4SDavid S. Miller struct xfrm_state *x; 86f034b5d4SDavid S. Miller 87f034b5d4SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, list, bydst) { 88f034b5d4SDavid S. Miller unsigned int h; 89f034b5d4SDavid S. Miller 90c1969f29SDavid S. Miller h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 91c1969f29SDavid S. Miller x->props.reqid, x->props.family, 92c1969f29SDavid S. Miller nhashmask); 93f034b5d4SDavid S. Miller hlist_add_head(&x->bydst, ndsttable+h); 94f034b5d4SDavid S. Miller 95f034b5d4SDavid S. Miller h = __xfrm_src_hash(&x->props.saddr, x->props.family, 96f034b5d4SDavid S. Miller nhashmask); 97f034b5d4SDavid S. Miller hlist_add_head(&x->bysrc, nsrctable+h); 98f034b5d4SDavid S. Miller 99f034b5d4SDavid S. Miller h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, 100f034b5d4SDavid S. Miller x->props.family, nhashmask); 101f034b5d4SDavid S. Miller hlist_add_head(&x->byspi, nspitable+h); 102f034b5d4SDavid S. Miller } 103f034b5d4SDavid S. Miller } 104f034b5d4SDavid S. Miller 105f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void) 106f034b5d4SDavid S. Miller { 107f034b5d4SDavid S. Miller return ((xfrm_state_hmask + 1) << 1) * 108f034b5d4SDavid S. Miller sizeof(struct hlist_head); 109f034b5d4SDavid S. Miller } 110f034b5d4SDavid S. Miller 111f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex); 112f034b5d4SDavid S. Miller 113f034b5d4SDavid S. Miller static void xfrm_hash_resize(void *__unused) 114f034b5d4SDavid S. Miller { 115f034b5d4SDavid S. Miller struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; 116f034b5d4SDavid S. Miller unsigned long nsize, osize; 117f034b5d4SDavid S. Miller unsigned int nhashmask, ohashmask; 118f034b5d4SDavid S. Miller int i; 119f034b5d4SDavid S. Miller 120f034b5d4SDavid S. Miller mutex_lock(&hash_resize_mutex); 121f034b5d4SDavid S. Miller 122f034b5d4SDavid S. Miller nsize = xfrm_hash_new_size(); 12344e36b42SDavid S. Miller ndst = xfrm_hash_alloc(nsize); 124f034b5d4SDavid S. Miller if (!ndst) 125f034b5d4SDavid S. Miller goto out_unlock; 12644e36b42SDavid S. Miller nsrc = xfrm_hash_alloc(nsize); 127f034b5d4SDavid S. Miller if (!nsrc) { 12844e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 129f034b5d4SDavid S. Miller goto out_unlock; 130f034b5d4SDavid S. Miller } 13144e36b42SDavid S. Miller nspi = xfrm_hash_alloc(nsize); 132f034b5d4SDavid S. Miller if (!nspi) { 13344e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 13444e36b42SDavid S. Miller xfrm_hash_free(nsrc, nsize); 135f034b5d4SDavid S. Miller goto out_unlock; 136f034b5d4SDavid S. Miller } 137f034b5d4SDavid S. Miller 138f034b5d4SDavid S. Miller spin_lock_bh(&xfrm_state_lock); 139f034b5d4SDavid S. Miller 140f034b5d4SDavid S. Miller nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; 141f034b5d4SDavid S. Miller for (i = xfrm_state_hmask; i >= 0; i--) 142f034b5d4SDavid S. Miller xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi, 143f034b5d4SDavid S. Miller nhashmask); 144f034b5d4SDavid S. Miller 145f034b5d4SDavid S. Miller odst = xfrm_state_bydst; 146f034b5d4SDavid S. Miller osrc = xfrm_state_bysrc; 147f034b5d4SDavid S. Miller ospi = xfrm_state_byspi; 148f034b5d4SDavid S. Miller ohashmask = xfrm_state_hmask; 149f034b5d4SDavid S. Miller 150f034b5d4SDavid S. Miller xfrm_state_bydst = ndst; 151f034b5d4SDavid S. Miller xfrm_state_bysrc = nsrc; 152f034b5d4SDavid S. Miller xfrm_state_byspi = nspi; 153f034b5d4SDavid S. Miller xfrm_state_hmask = nhashmask; 154f034b5d4SDavid S. Miller 155f034b5d4SDavid S. Miller spin_unlock_bh(&xfrm_state_lock); 156f034b5d4SDavid S. Miller 157f034b5d4SDavid S. Miller osize = (ohashmask + 1) * sizeof(struct hlist_head); 15844e36b42SDavid S. Miller xfrm_hash_free(odst, osize); 15944e36b42SDavid S. Miller xfrm_hash_free(osrc, osize); 16044e36b42SDavid S. Miller xfrm_hash_free(ospi, osize); 161f034b5d4SDavid S. Miller 162f034b5d4SDavid S. Miller out_unlock: 163f034b5d4SDavid S. Miller mutex_unlock(&hash_resize_mutex); 164f034b5d4SDavid S. Miller } 165f034b5d4SDavid S. Miller 166f034b5d4SDavid S. Miller static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL); 167f034b5d4SDavid S. Miller 1681da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq); 1691da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq); 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock); 1721da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work; 1758f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list); 1761da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock); 1771da177e4SLinus Torvalds 17853bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x); 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family); 1811da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); 1821da177e4SLinus Torvalds 183980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 18453bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid); 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x) 1871da177e4SLinus Torvalds { 188a47f0ce0SDavid S. Miller del_timer_sync(&x->timer); 189a47f0ce0SDavid S. Miller del_timer_sync(&x->rtimer); 1901da177e4SLinus Torvalds kfree(x->aalg); 1911da177e4SLinus Torvalds kfree(x->ealg); 1921da177e4SLinus Torvalds kfree(x->calg); 1931da177e4SLinus Torvalds kfree(x->encap); 194060f02a3SNoriaki TAKAMIYA kfree(x->coaddr); 195b59f45d0SHerbert Xu if (x->mode) 196b59f45d0SHerbert Xu xfrm_put_mode(x->mode); 1971da177e4SLinus Torvalds if (x->type) { 1981da177e4SLinus Torvalds x->type->destructor(x); 1991da177e4SLinus Torvalds xfrm_put_type(x->type); 2001da177e4SLinus Torvalds } 201df71837dSTrent Jaeger security_xfrm_state_free(x); 2021da177e4SLinus Torvalds kfree(x); 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds static void xfrm_state_gc_task(void *data) 2061da177e4SLinus Torvalds { 2071da177e4SLinus Torvalds struct xfrm_state *x; 2088f126e37SDavid S. Miller struct hlist_node *entry, *tmp; 2098f126e37SDavid S. Miller struct hlist_head gc_list; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 2128f126e37SDavid S. Miller gc_list.first = xfrm_state_gc_list.first; 2138f126e37SDavid S. Miller INIT_HLIST_HEAD(&xfrm_state_gc_list); 2141da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 2151da177e4SLinus Torvalds 2168f126e37SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst) 2171da177e4SLinus Torvalds xfrm_state_gc_destroy(x); 2188f126e37SDavid S. Miller 2191da177e4SLinus Torvalds wake_up(&km_waitq); 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 2251da177e4SLinus Torvalds return MAX_SCHEDULE_TIMEOUT-1; 2261da177e4SLinus Torvalds else 2271da177e4SLinus Torvalds return secs*HZ; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data) 2311da177e4SLinus Torvalds { 2321da177e4SLinus Torvalds struct xfrm_state *x = (struct xfrm_state*)data; 2331da177e4SLinus Torvalds unsigned long now = (unsigned long)xtime.tv_sec; 2341da177e4SLinus Torvalds long next = LONG_MAX; 2351da177e4SLinus Torvalds int warn = 0; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds spin_lock(&x->lock); 2381da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_DEAD) 2391da177e4SLinus Torvalds goto out; 2401da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_EXPIRED) 2411da177e4SLinus Torvalds goto expired; 2421da177e4SLinus Torvalds if (x->lft.hard_add_expires_seconds) { 2431da177e4SLinus Torvalds long tmo = x->lft.hard_add_expires_seconds + 2441da177e4SLinus Torvalds x->curlft.add_time - now; 2451da177e4SLinus Torvalds if (tmo <= 0) 2461da177e4SLinus Torvalds goto expired; 2471da177e4SLinus Torvalds if (tmo < next) 2481da177e4SLinus Torvalds next = tmo; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds if (x->lft.hard_use_expires_seconds) { 2511da177e4SLinus Torvalds long tmo = x->lft.hard_use_expires_seconds + 2521da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2531da177e4SLinus Torvalds if (tmo <= 0) 2541da177e4SLinus Torvalds goto expired; 2551da177e4SLinus Torvalds if (tmo < next) 2561da177e4SLinus Torvalds next = tmo; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds if (x->km.dying) 2591da177e4SLinus Torvalds goto resched; 2601da177e4SLinus Torvalds if (x->lft.soft_add_expires_seconds) { 2611da177e4SLinus Torvalds long tmo = x->lft.soft_add_expires_seconds + 2621da177e4SLinus Torvalds x->curlft.add_time - now; 2631da177e4SLinus Torvalds if (tmo <= 0) 2641da177e4SLinus Torvalds warn = 1; 2651da177e4SLinus Torvalds else if (tmo < next) 2661da177e4SLinus Torvalds next = tmo; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds if (x->lft.soft_use_expires_seconds) { 2691da177e4SLinus Torvalds long tmo = x->lft.soft_use_expires_seconds + 2701da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2711da177e4SLinus Torvalds if (tmo <= 0) 2721da177e4SLinus Torvalds warn = 1; 2731da177e4SLinus Torvalds else if (tmo < next) 2741da177e4SLinus Torvalds next = tmo; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2774666faabSHerbert Xu x->km.dying = warn; 2781da177e4SLinus Torvalds if (warn) 27953bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 2801da177e4SLinus Torvalds resched: 281a47f0ce0SDavid S. Miller if (next != LONG_MAX) 282a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + make_jiffies(next)); 283a47f0ce0SDavid S. Miller 2841da177e4SLinus Torvalds goto out; 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds expired: 2871da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { 2881da177e4SLinus Torvalds x->km.state = XFRM_STATE_EXPIRED; 2891da177e4SLinus Torvalds wake_up(&km_waitq); 2901da177e4SLinus Torvalds next = 2; 2911da177e4SLinus Torvalds goto resched; 2921da177e4SLinus Torvalds } 2934666faabSHerbert Xu if (!__xfrm_state_delete(x) && x->id.spi) 29453bc6b4dSJamal Hadi Salim km_state_expired(x, 1, 0); 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds out: 2971da177e4SLinus Torvalds spin_unlock(&x->lock); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3000ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data); 3010ac84752SDavid S. Miller 3021da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void) 3031da177e4SLinus Torvalds { 3041da177e4SLinus Torvalds struct xfrm_state *x; 3051da177e4SLinus Torvalds 3060da974f4SPanagiotis Issaris x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds if (x) { 3091da177e4SLinus Torvalds atomic_set(&x->refcnt, 1); 3101da177e4SLinus Torvalds atomic_set(&x->tunnel_users, 0); 3118f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bydst); 3128f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bysrc); 3138f126e37SDavid S. Miller INIT_HLIST_NODE(&x->byspi); 3141da177e4SLinus Torvalds init_timer(&x->timer); 3151da177e4SLinus Torvalds x->timer.function = xfrm_timer_handler; 3161da177e4SLinus Torvalds x->timer.data = (unsigned long)x; 317f8cd5488SJamal Hadi Salim init_timer(&x->rtimer); 318f8cd5488SJamal Hadi Salim x->rtimer.function = xfrm_replay_timer_handler; 319f8cd5488SJamal Hadi Salim x->rtimer.data = (unsigned long)x; 3201da177e4SLinus Torvalds x->curlft.add_time = (unsigned long)xtime.tv_sec; 3211da177e4SLinus Torvalds x->lft.soft_byte_limit = XFRM_INF; 3221da177e4SLinus Torvalds x->lft.soft_packet_limit = XFRM_INF; 3231da177e4SLinus Torvalds x->lft.hard_byte_limit = XFRM_INF; 3241da177e4SLinus Torvalds x->lft.hard_packet_limit = XFRM_INF; 325f8cd5488SJamal Hadi Salim x->replay_maxage = 0; 326f8cd5488SJamal Hadi Salim x->replay_maxdiff = 0; 3271da177e4SLinus Torvalds spin_lock_init(&x->lock); 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds return x; 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc); 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x) 3341da177e4SLinus Torvalds { 3351da177e4SLinus Torvalds BUG_TRAP(x->km.state == XFRM_STATE_DEAD); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 3388f126e37SDavid S. Miller hlist_add_head(&x->bydst, &xfrm_state_gc_list); 3391da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 3401da177e4SLinus Torvalds schedule_work(&xfrm_state_gc_work); 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy); 3431da177e4SLinus Torvalds 34453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x) 3451da177e4SLinus Torvalds { 34626b15dadSJamal Hadi Salim int err = -ESRCH; 34726b15dadSJamal Hadi Salim 3481da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_DEAD) { 3491da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 3501da177e4SLinus Torvalds spin_lock(&xfrm_state_lock); 3518f126e37SDavid S. Miller hlist_del(&x->bydst); 3528f126e37SDavid S. Miller hlist_del(&x->bysrc); 353a47f0ce0SDavid S. Miller if (x->id.spi) 3548f126e37SDavid S. Miller hlist_del(&x->byspi); 355f034b5d4SDavid S. Miller xfrm_state_num--; 3561da177e4SLinus Torvalds spin_unlock(&xfrm_state_lock); 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds /* All xfrm_state objects are created by xfrm_state_alloc. 3591da177e4SLinus Torvalds * The xfrm_state_alloc call gives a reference, and that 3601da177e4SLinus Torvalds * is what we are dropping here. 3611da177e4SLinus Torvalds */ 36221380b81SHerbert Xu __xfrm_state_put(x); 36326b15dadSJamal Hadi Salim err = 0; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 36626b15dadSJamal Hadi Salim return err; 36726b15dadSJamal Hadi Salim } 36853bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete); 36926b15dadSJamal Hadi Salim 37026b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x) 3711da177e4SLinus Torvalds { 37226b15dadSJamal Hadi Salim int err; 37326b15dadSJamal Hadi Salim 3741da177e4SLinus Torvalds spin_lock_bh(&x->lock); 37526b15dadSJamal Hadi Salim err = __xfrm_state_delete(x); 3761da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 37726b15dadSJamal Hadi Salim 37826b15dadSJamal Hadi Salim return err; 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds void xfrm_state_flush(u8 proto) 3831da177e4SLinus Torvalds { 3841da177e4SLinus Torvalds int i; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 387a9917c06SMasahide NAKAMURA for (i = 0; i <= xfrm_state_hmask; i++) { 3888f126e37SDavid S. Miller struct hlist_node *entry; 3898f126e37SDavid S. Miller struct xfrm_state *x; 3901da177e4SLinus Torvalds restart: 3918f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 3921da177e4SLinus Torvalds if (!xfrm_state_kern(x) && 3935794708fSMasahide NAKAMURA xfrm_id_proto_match(x->id.proto, proto)) { 3941da177e4SLinus Torvalds xfrm_state_hold(x); 3951da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds xfrm_state_delete(x); 3981da177e4SLinus Torvalds xfrm_state_put(x); 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 4011da177e4SLinus Torvalds goto restart; 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4061da177e4SLinus Torvalds wake_up(&km_waitq); 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush); 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds static int 4111da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, 4121da177e4SLinus Torvalds struct xfrm_tmpl *tmpl, 4131da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 4141da177e4SLinus Torvalds unsigned short family) 4151da177e4SLinus Torvalds { 4161da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 4171da177e4SLinus Torvalds if (!afinfo) 4181da177e4SLinus Torvalds return -1; 4191da177e4SLinus Torvalds afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); 4201da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 4211da177e4SLinus Torvalds return 0; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds 424a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 425edcd5821SDavid S. Miller { 426edcd5821SDavid S. Miller unsigned int h = xfrm_spi_hash(daddr, spi, proto, family); 427edcd5821SDavid S. Miller struct xfrm_state *x; 4288f126e37SDavid S. Miller struct hlist_node *entry; 429edcd5821SDavid S. Miller 4308f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) { 431edcd5821SDavid S. Miller if (x->props.family != family || 432edcd5821SDavid S. Miller x->id.spi != spi || 433edcd5821SDavid S. Miller x->id.proto != proto) 434edcd5821SDavid S. Miller continue; 435edcd5821SDavid S. Miller 436edcd5821SDavid S. Miller switch (family) { 437edcd5821SDavid S. Miller case AF_INET: 438edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4) 439edcd5821SDavid S. Miller continue; 440edcd5821SDavid S. Miller break; 441edcd5821SDavid S. Miller case AF_INET6: 442edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 443edcd5821SDavid S. Miller (struct in6_addr *) 444edcd5821SDavid S. Miller x->id.daddr.a6)) 445edcd5821SDavid S. Miller continue; 446edcd5821SDavid S. Miller break; 447edcd5821SDavid S. Miller }; 448edcd5821SDavid S. Miller 449edcd5821SDavid S. Miller xfrm_state_hold(x); 450edcd5821SDavid S. Miller return x; 451edcd5821SDavid S. Miller } 452edcd5821SDavid S. Miller 453edcd5821SDavid S. Miller return NULL; 454edcd5821SDavid S. Miller } 455edcd5821SDavid S. Miller 456edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family) 457edcd5821SDavid S. Miller { 458edcd5821SDavid S. Miller unsigned int h = xfrm_src_hash(saddr, family); 459edcd5821SDavid S. Miller struct xfrm_state *x; 4608f126e37SDavid S. Miller struct hlist_node *entry; 461edcd5821SDavid S. Miller 4628f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) { 463edcd5821SDavid S. Miller if (x->props.family != family || 464edcd5821SDavid S. Miller x->id.proto != proto) 465edcd5821SDavid S. Miller continue; 466edcd5821SDavid S. Miller 467edcd5821SDavid S. Miller switch (family) { 468edcd5821SDavid S. Miller case AF_INET: 469edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 470edcd5821SDavid S. Miller x->props.saddr.a4 != saddr->a4) 471edcd5821SDavid S. Miller continue; 472edcd5821SDavid S. Miller break; 473edcd5821SDavid S. Miller case AF_INET6: 474edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 475edcd5821SDavid S. Miller (struct in6_addr *) 476edcd5821SDavid S. Miller x->id.daddr.a6) || 477edcd5821SDavid S. Miller !ipv6_addr_equal((struct in6_addr *)saddr, 478edcd5821SDavid S. Miller (struct in6_addr *) 479edcd5821SDavid S. Miller x->props.saddr.a6)) 480edcd5821SDavid S. Miller continue; 481edcd5821SDavid S. Miller break; 482edcd5821SDavid S. Miller }; 483edcd5821SDavid S. Miller 484edcd5821SDavid S. Miller xfrm_state_hold(x); 485edcd5821SDavid S. Miller return x; 486edcd5821SDavid S. Miller } 487edcd5821SDavid S. Miller 488edcd5821SDavid S. Miller return NULL; 489edcd5821SDavid S. Miller } 490edcd5821SDavid S. Miller 491edcd5821SDavid S. Miller static inline struct xfrm_state * 492edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family) 493edcd5821SDavid S. Miller { 494edcd5821SDavid S. Miller if (use_spi) 495edcd5821SDavid S. Miller return __xfrm_state_lookup(&x->id.daddr, x->id.spi, 496edcd5821SDavid S. Miller x->id.proto, family); 497edcd5821SDavid S. Miller else 498edcd5821SDavid S. Miller return __xfrm_state_lookup_byaddr(&x->id.daddr, 499edcd5821SDavid S. Miller &x->props.saddr, 500edcd5821SDavid S. Miller x->id.proto, family); 501edcd5821SDavid S. Miller } 502edcd5821SDavid S. Miller 5031da177e4SLinus Torvalds struct xfrm_state * 5041da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 5051da177e4SLinus Torvalds struct flowi *fl, struct xfrm_tmpl *tmpl, 5061da177e4SLinus Torvalds struct xfrm_policy *pol, int *err, 5071da177e4SLinus Torvalds unsigned short family) 5081da177e4SLinus Torvalds { 509c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); 5108f126e37SDavid S. Miller struct hlist_node *entry; 5111da177e4SLinus Torvalds struct xfrm_state *x, *x0; 5121da177e4SLinus Torvalds int acquire_in_progress = 0; 5131da177e4SLinus Torvalds int error = 0; 5141da177e4SLinus Torvalds struct xfrm_state *best = NULL; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 5178f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 5181da177e4SLinus Torvalds if (x->props.family == family && 5191da177e4SLinus Torvalds x->props.reqid == tmpl->reqid && 520fbd9a5b4SMasahide NAKAMURA !(x->props.flags & XFRM_STATE_WILDRECV) && 5211da177e4SLinus Torvalds xfrm_state_addr_check(x, daddr, saddr, family) && 5221da177e4SLinus Torvalds tmpl->mode == x->props.mode && 5231da177e4SLinus Torvalds tmpl->id.proto == x->id.proto && 5241da177e4SLinus Torvalds (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { 5251da177e4SLinus Torvalds /* Resolution logic: 5261da177e4SLinus Torvalds 1. There is a valid state with matching selector. 5271da177e4SLinus Torvalds Done. 5281da177e4SLinus Torvalds 2. Valid state with inappropriate selector. Skip. 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds Entering area of "sysdeps". 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds 3. If state is not valid, selector is temporary, 5331da177e4SLinus Torvalds it selects only session which triggered 5341da177e4SLinus Torvalds previous resolution. Key manager will do 5351da177e4SLinus Torvalds something to install a state with proper 5361da177e4SLinus Torvalds selector. 5371da177e4SLinus Torvalds */ 5381da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID) { 539df71837dSTrent Jaeger if (!xfrm_selector_match(&x->sel, fl, family) || 540e0d1caa7SVenkat Yekkirala !security_xfrm_state_pol_flow_match(x, pol, fl)) 5411da177e4SLinus Torvalds continue; 5421da177e4SLinus Torvalds if (!best || 5431da177e4SLinus Torvalds best->km.dying > x->km.dying || 5441da177e4SLinus Torvalds (best->km.dying == x->km.dying && 5451da177e4SLinus Torvalds best->curlft.add_time < x->curlft.add_time)) 5461da177e4SLinus Torvalds best = x; 5471da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ACQ) { 5481da177e4SLinus Torvalds acquire_in_progress = 1; 5491da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ERROR || 5501da177e4SLinus Torvalds x->km.state == XFRM_STATE_EXPIRED) { 551df71837dSTrent Jaeger if (xfrm_selector_match(&x->sel, fl, family) && 552e0d1caa7SVenkat Yekkirala security_xfrm_state_pol_flow_match(x, pol, fl)) 5531da177e4SLinus Torvalds error = -ESRCH; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds x = best; 5591da177e4SLinus Torvalds if (!x && !error && !acquire_in_progress) { 5605c5d281aSPatrick McHardy if (tmpl->id.spi && 561edcd5821SDavid S. Miller (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi, 562edcd5821SDavid S. Miller tmpl->id.proto, family)) != NULL) { 5631da177e4SLinus Torvalds xfrm_state_put(x0); 5641da177e4SLinus Torvalds error = -EEXIST; 5651da177e4SLinus Torvalds goto out; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds x = xfrm_state_alloc(); 5681da177e4SLinus Torvalds if (x == NULL) { 5691da177e4SLinus Torvalds error = -ENOMEM; 5701da177e4SLinus Torvalds goto out; 5711da177e4SLinus Torvalds } 5721da177e4SLinus Torvalds /* Initialize temporary selector matching only 5731da177e4SLinus Torvalds * to current session. */ 5741da177e4SLinus Torvalds xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); 5751da177e4SLinus Torvalds 576e0d1caa7SVenkat Yekkirala error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); 577e0d1caa7SVenkat Yekkirala if (error) { 578e0d1caa7SVenkat Yekkirala x->km.state = XFRM_STATE_DEAD; 579e0d1caa7SVenkat Yekkirala xfrm_state_put(x); 580e0d1caa7SVenkat Yekkirala x = NULL; 581e0d1caa7SVenkat Yekkirala goto out; 582e0d1caa7SVenkat Yekkirala } 583e0d1caa7SVenkat Yekkirala 5841da177e4SLinus Torvalds if (km_query(x, tmpl, pol) == 0) { 5851da177e4SLinus Torvalds x->km.state = XFRM_STATE_ACQ; 5868f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 5878f126e37SDavid S. Miller h = xfrm_src_hash(saddr, family); 5888f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 5891da177e4SLinus Torvalds if (x->id.spi) { 5901da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); 5918f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 5941da177e4SLinus Torvalds x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 5951da177e4SLinus Torvalds add_timer(&x->timer); 5961da177e4SLinus Torvalds } else { 5971da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 5981da177e4SLinus Torvalds xfrm_state_put(x); 5991da177e4SLinus Torvalds x = NULL; 6001da177e4SLinus Torvalds error = -ESRCH; 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds out: 6041da177e4SLinus Torvalds if (x) 6051da177e4SLinus Torvalds xfrm_state_hold(x); 6061da177e4SLinus Torvalds else 6071da177e4SLinus Torvalds *err = acquire_in_progress ? -EAGAIN : error; 6081da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6091da177e4SLinus Torvalds return x; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x) 6131da177e4SLinus Torvalds { 614a624c108SDavid S. Miller unsigned int h; 6151da177e4SLinus Torvalds 6169d4a706dSDavid S. Miller x->genid = ++xfrm_state_genid; 6179d4a706dSDavid S. Miller 618c1969f29SDavid S. Miller h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 619c1969f29SDavid S. Miller x->props.reqid, x->props.family); 6208f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 6211da177e4SLinus Torvalds 6226c44e6b7SMasahide NAKAMURA h = xfrm_src_hash(&x->props.saddr, x->props.family); 6238f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 6246c44e6b7SMasahide NAKAMURA 6256c44e6b7SMasahide NAKAMURA if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) { 6266c44e6b7SMasahide NAKAMURA h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, 6276c44e6b7SMasahide NAKAMURA x->props.family); 6281da177e4SLinus Torvalds 6298f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 6306c44e6b7SMasahide NAKAMURA } 6311da177e4SLinus Torvalds 632a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + HZ); 633a47f0ce0SDavid S. Miller if (x->replay_maxage) 634a47f0ce0SDavid S. Miller mod_timer(&x->rtimer, jiffies + x->replay_maxage); 635f8cd5488SJamal Hadi Salim 6361da177e4SLinus Torvalds wake_up(&km_waitq); 637f034b5d4SDavid S. Miller 638f034b5d4SDavid S. Miller xfrm_state_num++; 639f034b5d4SDavid S. Miller 640f034b5d4SDavid S. Miller if (x->bydst.next != NULL && 641f034b5d4SDavid S. Miller (xfrm_state_hmask + 1) < xfrm_state_hashmax && 642f034b5d4SDavid S. Miller xfrm_state_num > xfrm_state_hmask) 643f034b5d4SDavid S. Miller schedule_work(&xfrm_hash_work); 6441da177e4SLinus Torvalds } 6451da177e4SLinus Torvalds 646c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */ 647c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew) 648c7f5ea3aSDavid S. Miller { 649c7f5ea3aSDavid S. Miller unsigned short family = xnew->props.family; 650c7f5ea3aSDavid S. Miller u32 reqid = xnew->props.reqid; 651c7f5ea3aSDavid S. Miller struct xfrm_state *x; 652c7f5ea3aSDavid S. Miller struct hlist_node *entry; 653c7f5ea3aSDavid S. Miller unsigned int h; 654c7f5ea3aSDavid S. Miller 655c1969f29SDavid S. Miller h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family); 656c7f5ea3aSDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 657c7f5ea3aSDavid S. Miller if (x->props.family == family && 658c7f5ea3aSDavid S. Miller x->props.reqid == reqid && 659c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) && 660c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family)) 661c7f5ea3aSDavid S. Miller x->genid = xfrm_state_genid; 662c7f5ea3aSDavid S. Miller } 663c7f5ea3aSDavid S. Miller } 664c7f5ea3aSDavid S. Miller 6651da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x) 6661da177e4SLinus Torvalds { 6671da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 668c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 6691da177e4SLinus Torvalds __xfrm_state_insert(x); 6701da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert); 6731da177e4SLinus Torvalds 6742770834cSDavid S. Miller /* xfrm_state_lock is held */ 6752770834cSDavid S. Miller static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create) 6762770834cSDavid S. Miller { 677c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); 6788f126e37SDavid S. Miller struct hlist_node *entry; 6792770834cSDavid S. Miller struct xfrm_state *x; 6802770834cSDavid S. Miller 6818f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 6822770834cSDavid S. Miller if (x->props.reqid != reqid || 6832770834cSDavid S. Miller x->props.mode != mode || 6842770834cSDavid S. Miller x->props.family != family || 6852770834cSDavid S. Miller x->km.state != XFRM_STATE_ACQ || 6862770834cSDavid S. Miller x->id.spi != 0) 6872770834cSDavid S. Miller continue; 6882770834cSDavid S. Miller 6892770834cSDavid S. Miller switch (family) { 6902770834cSDavid S. Miller case AF_INET: 6912770834cSDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 6922770834cSDavid S. Miller x->props.saddr.a4 != saddr->a4) 6932770834cSDavid S. Miller continue; 6942770834cSDavid S. Miller break; 6952770834cSDavid S. Miller case AF_INET6: 6962770834cSDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6, 6972770834cSDavid S. Miller (struct in6_addr *)daddr) || 6982770834cSDavid S. Miller !ipv6_addr_equal((struct in6_addr *) 6992770834cSDavid S. Miller x->props.saddr.a6, 7002770834cSDavid S. Miller (struct in6_addr *)saddr)) 7012770834cSDavid S. Miller continue; 7022770834cSDavid S. Miller break; 7032770834cSDavid S. Miller }; 7042770834cSDavid S. Miller 7052770834cSDavid S. Miller xfrm_state_hold(x); 7062770834cSDavid S. Miller return x; 7072770834cSDavid S. Miller } 7082770834cSDavid S. Miller 7092770834cSDavid S. Miller if (!create) 7102770834cSDavid S. Miller return NULL; 7112770834cSDavid S. Miller 7122770834cSDavid S. Miller x = xfrm_state_alloc(); 7132770834cSDavid S. Miller if (likely(x)) { 7142770834cSDavid S. Miller switch (family) { 7152770834cSDavid S. Miller case AF_INET: 7162770834cSDavid S. Miller x->sel.daddr.a4 = daddr->a4; 7172770834cSDavid S. Miller x->sel.saddr.a4 = saddr->a4; 7182770834cSDavid S. Miller x->sel.prefixlen_d = 32; 7192770834cSDavid S. Miller x->sel.prefixlen_s = 32; 7202770834cSDavid S. Miller x->props.saddr.a4 = saddr->a4; 7212770834cSDavid S. Miller x->id.daddr.a4 = daddr->a4; 7222770834cSDavid S. Miller break; 7232770834cSDavid S. Miller 7242770834cSDavid S. Miller case AF_INET6: 7252770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, 7262770834cSDavid S. Miller (struct in6_addr *)daddr); 7272770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, 7282770834cSDavid S. Miller (struct in6_addr *)saddr); 7292770834cSDavid S. Miller x->sel.prefixlen_d = 128; 7302770834cSDavid S. Miller x->sel.prefixlen_s = 128; 7312770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, 7322770834cSDavid S. Miller (struct in6_addr *)saddr); 7332770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, 7342770834cSDavid S. Miller (struct in6_addr *)daddr); 7352770834cSDavid S. Miller break; 7362770834cSDavid S. Miller }; 7372770834cSDavid S. Miller 7382770834cSDavid S. Miller x->km.state = XFRM_STATE_ACQ; 7392770834cSDavid S. Miller x->id.proto = proto; 7402770834cSDavid S. Miller x->props.family = family; 7412770834cSDavid S. Miller x->props.mode = mode; 7422770834cSDavid S. Miller x->props.reqid = reqid; 7432770834cSDavid S. Miller x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 7442770834cSDavid S. Miller xfrm_state_hold(x); 7452770834cSDavid S. Miller x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 7462770834cSDavid S. Miller add_timer(&x->timer); 7478f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 7482770834cSDavid S. Miller h = xfrm_src_hash(saddr, family); 7498f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 7502770834cSDavid S. Miller wake_up(&km_waitq); 7512770834cSDavid S. Miller } 7522770834cSDavid S. Miller 7532770834cSDavid S. Miller return x; 7542770834cSDavid S. Miller } 7552770834cSDavid S. Miller 7561da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x) 7591da177e4SLinus Torvalds { 7601da177e4SLinus Torvalds struct xfrm_state *x1; 7611da177e4SLinus Torvalds int family; 7621da177e4SLinus Torvalds int err; 763eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds family = x->props.family; 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7681da177e4SLinus Torvalds 769edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, family); 7701da177e4SLinus Torvalds if (x1) { 7711da177e4SLinus Torvalds xfrm_state_put(x1); 7721da177e4SLinus Torvalds x1 = NULL; 7731da177e4SLinus Torvalds err = -EEXIST; 7741da177e4SLinus Torvalds goto out; 7751da177e4SLinus Torvalds } 7761da177e4SLinus Torvalds 777eb2971b6SMasahide NAKAMURA if (use_spi && x->km.seq) { 7781da177e4SLinus Torvalds x1 = __xfrm_find_acq_byseq(x->km.seq); 7791da177e4SLinus Torvalds if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) { 7801da177e4SLinus Torvalds xfrm_state_put(x1); 7811da177e4SLinus Torvalds x1 = NULL; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds 785eb2971b6SMasahide NAKAMURA if (use_spi && !x1) 7862770834cSDavid S. Miller x1 = __find_acq_core(family, x->props.mode, x->props.reqid, 7872770834cSDavid S. Miller x->id.proto, 7881da177e4SLinus Torvalds &x->id.daddr, &x->props.saddr, 0); 7891da177e4SLinus Torvalds 790c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 7911da177e4SLinus Torvalds __xfrm_state_insert(x); 7921da177e4SLinus Torvalds err = 0; 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds out: 7951da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds if (x1) { 7981da177e4SLinus Torvalds xfrm_state_delete(x1); 7991da177e4SLinus Torvalds xfrm_state_put(x1); 8001da177e4SLinus Torvalds } 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds return err; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add); 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x) 8071da177e4SLinus Torvalds { 8081da177e4SLinus Torvalds struct xfrm_state *x1; 8091da177e4SLinus Torvalds int err; 810eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 813edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, x->props.family); 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds err = -ESRCH; 8161da177e4SLinus Torvalds if (!x1) 8171da177e4SLinus Torvalds goto out; 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds if (xfrm_state_kern(x1)) { 8201da177e4SLinus Torvalds xfrm_state_put(x1); 8211da177e4SLinus Torvalds err = -EEXIST; 8221da177e4SLinus Torvalds goto out; 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds if (x1->km.state == XFRM_STATE_ACQ) { 8261da177e4SLinus Torvalds __xfrm_state_insert(x); 8271da177e4SLinus Torvalds x = NULL; 8281da177e4SLinus Torvalds } 8291da177e4SLinus Torvalds err = 0; 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds out: 8321da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds if (err) 8351da177e4SLinus Torvalds return err; 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds if (!x) { 8381da177e4SLinus Torvalds xfrm_state_delete(x1); 8391da177e4SLinus Torvalds xfrm_state_put(x1); 8401da177e4SLinus Torvalds return 0; 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds err = -EINVAL; 8441da177e4SLinus Torvalds spin_lock_bh(&x1->lock); 8451da177e4SLinus Torvalds if (likely(x1->km.state == XFRM_STATE_VALID)) { 8461da177e4SLinus Torvalds if (x->encap && x1->encap) 8471da177e4SLinus Torvalds memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 848060f02a3SNoriaki TAKAMIYA if (x->coaddr && x1->coaddr) { 849060f02a3SNoriaki TAKAMIYA memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr)); 850060f02a3SNoriaki TAKAMIYA } 851060f02a3SNoriaki TAKAMIYA if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel))) 852060f02a3SNoriaki TAKAMIYA memcpy(&x1->sel, &x->sel, sizeof(x1->sel)); 8531da177e4SLinus Torvalds memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 8541da177e4SLinus Torvalds x1->km.dying = 0; 8551da177e4SLinus Torvalds 856a47f0ce0SDavid S. Miller mod_timer(&x1->timer, jiffies + HZ); 8571da177e4SLinus Torvalds if (x1->curlft.use_time) 8581da177e4SLinus Torvalds xfrm_state_check_expire(x1); 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds err = 0; 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds spin_unlock_bh(&x1->lock); 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds xfrm_state_put(x1); 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds return err; 8671da177e4SLinus Torvalds } 8681da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update); 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x) 8711da177e4SLinus Torvalds { 8721da177e4SLinus Torvalds if (!x->curlft.use_time) 8731da177e4SLinus Torvalds x->curlft.use_time = (unsigned long)xtime.tv_sec; 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_VALID) 8761da177e4SLinus Torvalds return -EINVAL; 8771da177e4SLinus Torvalds 8781da177e4SLinus Torvalds if (x->curlft.bytes >= x->lft.hard_byte_limit || 8791da177e4SLinus Torvalds x->curlft.packets >= x->lft.hard_packet_limit) { 8804666faabSHerbert Xu x->km.state = XFRM_STATE_EXPIRED; 881a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies); 8821da177e4SLinus Torvalds return -EINVAL; 8831da177e4SLinus Torvalds } 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds if (!x->km.dying && 8861da177e4SLinus Torvalds (x->curlft.bytes >= x->lft.soft_byte_limit || 8874666faabSHerbert Xu x->curlft.packets >= x->lft.soft_packet_limit)) { 8884666faabSHerbert Xu x->km.dying = 1; 88953bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 8904666faabSHerbert Xu } 8911da177e4SLinus Torvalds return 0; 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire); 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 8961da177e4SLinus Torvalds { 8971da177e4SLinus Torvalds int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 8981da177e4SLinus Torvalds - skb_headroom(skb); 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds if (nhead > 0) 9011da177e4SLinus Torvalds return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds /* Check tail too... */ 9041da177e4SLinus Torvalds return 0; 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 9081da177e4SLinus Torvalds { 9091da177e4SLinus Torvalds int err = xfrm_state_check_expire(x); 9101da177e4SLinus Torvalds if (err < 0) 9111da177e4SLinus Torvalds goto err; 9121da177e4SLinus Torvalds err = xfrm_state_check_space(x, skb); 9131da177e4SLinus Torvalds err: 9141da177e4SLinus Torvalds return err; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check); 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds struct xfrm_state * 919a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, 9201da177e4SLinus Torvalds unsigned short family) 9211da177e4SLinus Torvalds { 9221da177e4SLinus Torvalds struct xfrm_state *x; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 925edcd5821SDavid S. Miller x = __xfrm_state_lookup(daddr, spi, proto, family); 9261da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9271da177e4SLinus Torvalds return x; 9281da177e4SLinus Torvalds } 9291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup); 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds struct xfrm_state * 932eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, 933eb2971b6SMasahide NAKAMURA u8 proto, unsigned short family) 934eb2971b6SMasahide NAKAMURA { 935eb2971b6SMasahide NAKAMURA struct xfrm_state *x; 936eb2971b6SMasahide NAKAMURA 937eb2971b6SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 938edcd5821SDavid S. Miller x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family); 939eb2971b6SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 940eb2971b6SMasahide NAKAMURA return x; 941eb2971b6SMasahide NAKAMURA } 942eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr); 943eb2971b6SMasahide NAKAMURA 944eb2971b6SMasahide NAKAMURA struct xfrm_state * 9451da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto, 9461da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 9471da177e4SLinus Torvalds int create, unsigned short family) 9481da177e4SLinus Torvalds { 9491da177e4SLinus Torvalds struct xfrm_state *x; 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 9522770834cSDavid S. Miller x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create); 9531da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9542770834cSDavid S. Miller 9551da177e4SLinus Torvalds return x; 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq); 9581da177e4SLinus Torvalds 95941a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 96041a49cc3SMasahide NAKAMURA int 96141a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, 96241a49cc3SMasahide NAKAMURA unsigned short family) 96341a49cc3SMasahide NAKAMURA { 96441a49cc3SMasahide NAKAMURA int err = 0; 96541a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 96641a49cc3SMasahide NAKAMURA if (!afinfo) 96741a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 96841a49cc3SMasahide NAKAMURA 96941a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 97041a49cc3SMasahide NAKAMURA if (afinfo->tmpl_sort) 97141a49cc3SMasahide NAKAMURA err = afinfo->tmpl_sort(dst, src, n); 97241a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 97341a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 97441a49cc3SMasahide NAKAMURA return err; 97541a49cc3SMasahide NAKAMURA } 97641a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort); 97741a49cc3SMasahide NAKAMURA 97841a49cc3SMasahide NAKAMURA int 97941a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, 98041a49cc3SMasahide NAKAMURA unsigned short family) 98141a49cc3SMasahide NAKAMURA { 98241a49cc3SMasahide NAKAMURA int err = 0; 98341a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 98441a49cc3SMasahide NAKAMURA if (!afinfo) 98541a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 98641a49cc3SMasahide NAKAMURA 98741a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 98841a49cc3SMasahide NAKAMURA if (afinfo->state_sort) 98941a49cc3SMasahide NAKAMURA err = afinfo->state_sort(dst, src, n); 99041a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 99141a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 99241a49cc3SMasahide NAKAMURA return err; 99341a49cc3SMasahide NAKAMURA } 99441a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort); 99541a49cc3SMasahide NAKAMURA #endif 99641a49cc3SMasahide NAKAMURA 9971da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */ 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq) 10001da177e4SLinus Torvalds { 10011da177e4SLinus Torvalds int i; 10021da177e4SLinus Torvalds 1003f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 10048f126e37SDavid S. Miller struct hlist_node *entry; 10058f126e37SDavid S. Miller struct xfrm_state *x; 10068f126e37SDavid S. Miller 10078f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 10088f126e37SDavid S. Miller if (x->km.seq == seq && 10098f126e37SDavid S. Miller x->km.state == XFRM_STATE_ACQ) { 10101da177e4SLinus Torvalds xfrm_state_hold(x); 10111da177e4SLinus Torvalds return x; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds } 10141da177e4SLinus Torvalds } 10151da177e4SLinus Torvalds return NULL; 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq) 10191da177e4SLinus Torvalds { 10201da177e4SLinus Torvalds struct xfrm_state *x; 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10231da177e4SLinus Torvalds x = __xfrm_find_acq_byseq(seq); 10241da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10251da177e4SLinus Torvalds return x; 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq); 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds u32 xfrm_get_acqseq(void) 10301da177e4SLinus Torvalds { 10311da177e4SLinus Torvalds u32 res; 10321da177e4SLinus Torvalds static u32 acqseq; 10331da177e4SLinus Torvalds static DEFINE_SPINLOCK(acqseq_lock); 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds spin_lock_bh(&acqseq_lock); 10361da177e4SLinus Torvalds res = (++acqseq ? : ++acqseq); 10371da177e4SLinus Torvalds spin_unlock_bh(&acqseq_lock); 10381da177e4SLinus Torvalds return res; 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq); 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds void 104326977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi) 10441da177e4SLinus Torvalds { 1045f034b5d4SDavid S. Miller unsigned int h; 10461da177e4SLinus Torvalds struct xfrm_state *x0; 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds if (x->id.spi) 10491da177e4SLinus Torvalds return; 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds if (minspi == maxspi) { 10521da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); 10531da177e4SLinus Torvalds if (x0) { 10541da177e4SLinus Torvalds xfrm_state_put(x0); 10551da177e4SLinus Torvalds return; 10561da177e4SLinus Torvalds } 10571da177e4SLinus Torvalds x->id.spi = minspi; 10581da177e4SLinus Torvalds } else { 10591da177e4SLinus Torvalds u32 spi = 0; 106026977b4eSAl Viro u32 low = ntohl(minspi); 106126977b4eSAl Viro u32 high = ntohl(maxspi); 106226977b4eSAl Viro for (h=0; h<high-low+1; h++) { 106326977b4eSAl Viro spi = low + net_random()%(high-low+1); 10641da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family); 10651da177e4SLinus Torvalds if (x0 == NULL) { 10661da177e4SLinus Torvalds x->id.spi = htonl(spi); 10671da177e4SLinus Torvalds break; 10681da177e4SLinus Torvalds } 10691da177e4SLinus Torvalds xfrm_state_put(x0); 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds } 10721da177e4SLinus Torvalds if (x->id.spi) { 10731da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10741da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 10758f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 10761da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10771da177e4SLinus Torvalds wake_up(&km_waitq); 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds } 10801da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi); 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), 10831da177e4SLinus Torvalds void *data) 10841da177e4SLinus Torvalds { 10851da177e4SLinus Torvalds int i; 10861da177e4SLinus Torvalds struct xfrm_state *x; 10878f126e37SDavid S. Miller struct hlist_node *entry; 10881da177e4SLinus Torvalds int count = 0; 10891da177e4SLinus Torvalds int err = 0; 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 1092f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 10938f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 10945794708fSMasahide NAKAMURA if (xfrm_id_proto_match(x->id.proto, proto)) 10951da177e4SLinus Torvalds count++; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds if (count == 0) { 10991da177e4SLinus Torvalds err = -ENOENT; 11001da177e4SLinus Torvalds goto out; 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds 1103f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 11048f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 11055794708fSMasahide NAKAMURA if (!xfrm_id_proto_match(x->id.proto, proto)) 11061da177e4SLinus Torvalds continue; 11071da177e4SLinus Torvalds err = func(x, --count, data); 11081da177e4SLinus Torvalds if (err) 11091da177e4SLinus Torvalds goto out; 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds out: 11131da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11141da177e4SLinus Torvalds return err; 11151da177e4SLinus Torvalds } 11161da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk); 11171da177e4SLinus Torvalds 1118f8cd5488SJamal Hadi Salim 1119f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event) 1120f8cd5488SJamal Hadi Salim { 1121f8cd5488SJamal Hadi Salim struct km_event c; 1122f8cd5488SJamal Hadi Salim /* we send notify messages in case 1123f8cd5488SJamal Hadi Salim * 1. we updated on of the sequence numbers, and the seqno difference 1124f8cd5488SJamal Hadi Salim * is at least x->replay_maxdiff, in this case we also update the 1125f8cd5488SJamal Hadi Salim * timeout of our timer function 1126f8cd5488SJamal Hadi Salim * 2. if x->replay_maxage has elapsed since last update, 1127f8cd5488SJamal Hadi Salim * and there were changes 1128f8cd5488SJamal Hadi Salim * 1129f8cd5488SJamal Hadi Salim * The state structure must be locked! 1130f8cd5488SJamal Hadi Salim */ 1131f8cd5488SJamal Hadi Salim 1132f8cd5488SJamal Hadi Salim switch (event) { 1133f8cd5488SJamal Hadi Salim case XFRM_REPLAY_UPDATE: 1134f8cd5488SJamal Hadi Salim if (x->replay_maxdiff && 1135f8cd5488SJamal Hadi Salim (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 11362717096aSJamal Hadi Salim (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { 11372717096aSJamal Hadi Salim if (x->xflags & XFRM_TIME_DEFER) 11382717096aSJamal Hadi Salim event = XFRM_REPLAY_TIMEOUT; 11392717096aSJamal Hadi Salim else 1140f8cd5488SJamal Hadi Salim return; 11412717096aSJamal Hadi Salim } 1142f8cd5488SJamal Hadi Salim 1143f8cd5488SJamal Hadi Salim break; 1144f8cd5488SJamal Hadi Salim 1145f8cd5488SJamal Hadi Salim case XFRM_REPLAY_TIMEOUT: 1146f8cd5488SJamal Hadi Salim if ((x->replay.seq == x->preplay.seq) && 1147f8cd5488SJamal Hadi Salim (x->replay.bitmap == x->preplay.bitmap) && 11482717096aSJamal Hadi Salim (x->replay.oseq == x->preplay.oseq)) { 11492717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 1150f8cd5488SJamal Hadi Salim return; 11512717096aSJamal Hadi Salim } 1152f8cd5488SJamal Hadi Salim 1153f8cd5488SJamal Hadi Salim break; 1154f8cd5488SJamal Hadi Salim } 1155f8cd5488SJamal Hadi Salim 1156f8cd5488SJamal Hadi Salim memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 1157f8cd5488SJamal Hadi Salim c.event = XFRM_MSG_NEWAE; 1158f8cd5488SJamal Hadi Salim c.data.aevent = event; 1159f8cd5488SJamal Hadi Salim km_state_notify(x, &c); 1160f8cd5488SJamal Hadi Salim 1161f8cd5488SJamal Hadi Salim if (x->replay_maxage && 1162a47f0ce0SDavid S. Miller !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 11632717096aSJamal Hadi Salim x->xflags &= ~XFRM_TIME_DEFER; 11642717096aSJamal Hadi Salim } 1165a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify); 1166f8cd5488SJamal Hadi Salim 1167f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data) 1168f8cd5488SJamal Hadi Salim { 1169f8cd5488SJamal Hadi Salim struct xfrm_state *x = (struct xfrm_state*)data; 1170f8cd5488SJamal Hadi Salim 1171f8cd5488SJamal Hadi Salim spin_lock(&x->lock); 1172f8cd5488SJamal Hadi Salim 11732717096aSJamal Hadi Salim if (x->km.state == XFRM_STATE_VALID) { 11742717096aSJamal Hadi Salim if (xfrm_aevent_is_on()) 1175f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); 11762717096aSJamal Hadi Salim else 11772717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 11782717096aSJamal Hadi Salim } 1179f8cd5488SJamal Hadi Salim 1180f8cd5488SJamal Hadi Salim spin_unlock(&x->lock); 1181f8cd5488SJamal Hadi Salim } 1182f8cd5488SJamal Hadi Salim 1183a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) 11841da177e4SLinus Torvalds { 11851da177e4SLinus Torvalds u32 diff; 1186a252cc23SAl Viro u32 seq = ntohl(net_seq); 11871da177e4SLinus Torvalds 11881da177e4SLinus Torvalds if (unlikely(seq == 0)) 11891da177e4SLinus Torvalds return -EINVAL; 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds if (likely(seq > x->replay.seq)) 11921da177e4SLinus Torvalds return 0; 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds diff = x->replay.seq - seq; 11951da177e4SLinus Torvalds if (diff >= x->props.replay_window) { 11961da177e4SLinus Torvalds x->stats.replay_window++; 11971da177e4SLinus Torvalds return -EINVAL; 11981da177e4SLinus Torvalds } 11991da177e4SLinus Torvalds 12001da177e4SLinus Torvalds if (x->replay.bitmap & (1U << diff)) { 12011da177e4SLinus Torvalds x->stats.replay++; 12021da177e4SLinus Torvalds return -EINVAL; 12031da177e4SLinus Torvalds } 12041da177e4SLinus Torvalds return 0; 12051da177e4SLinus Torvalds } 12061da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check); 12071da177e4SLinus Torvalds 120861f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) 12091da177e4SLinus Torvalds { 12101da177e4SLinus Torvalds u32 diff; 121161f4627bSAl Viro u32 seq = ntohl(net_seq); 12121da177e4SLinus Torvalds 12131da177e4SLinus Torvalds if (seq > x->replay.seq) { 12141da177e4SLinus Torvalds diff = seq - x->replay.seq; 12151da177e4SLinus Torvalds if (diff < x->props.replay_window) 12161da177e4SLinus Torvalds x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 12171da177e4SLinus Torvalds else 12181da177e4SLinus Torvalds x->replay.bitmap = 1; 12191da177e4SLinus Torvalds x->replay.seq = seq; 12201da177e4SLinus Torvalds } else { 12211da177e4SLinus Torvalds diff = x->replay.seq - seq; 12221da177e4SLinus Torvalds x->replay.bitmap |= (1U << diff); 12231da177e4SLinus Torvalds } 1224f8cd5488SJamal Hadi Salim 1225f8cd5488SJamal Hadi Salim if (xfrm_aevent_is_on()) 1226f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance); 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); 12311da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock); 12321da177e4SLinus Torvalds 123326b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 12341da177e4SLinus Torvalds { 12351da177e4SLinus Torvalds struct xfrm_mgr *km; 12361da177e4SLinus Torvalds 123726b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 123826b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 123926b15dadSJamal Hadi Salim if (km->notify_policy) 124026b15dadSJamal Hadi Salim km->notify_policy(xp, dir, c); 124126b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 124226b15dadSJamal Hadi Salim } 124326b15dadSJamal Hadi Salim 124426b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c) 124526b15dadSJamal Hadi Salim { 124626b15dadSJamal Hadi Salim struct xfrm_mgr *km; 124726b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 124826b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 124926b15dadSJamal Hadi Salim if (km->notify) 125026b15dadSJamal Hadi Salim km->notify(x, c); 125126b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 125226b15dadSJamal Hadi Salim } 125326b15dadSJamal Hadi Salim 125426b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify); 125526b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify); 125626b15dadSJamal Hadi Salim 125753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid) 125826b15dadSJamal Hadi Salim { 125926b15dadSJamal Hadi Salim struct km_event c; 126026b15dadSJamal Hadi Salim 1261bf08867fSHerbert Xu c.data.hard = hard; 126253bc6b4dSJamal Hadi Salim c.pid = pid; 1263f60f6b8fSHerbert Xu c.event = XFRM_MSG_EXPIRE; 126426b15dadSJamal Hadi Salim km_state_notify(x, &c); 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds if (hard) 12671da177e4SLinus Torvalds wake_up(&km_waitq); 12681da177e4SLinus Torvalds } 12691da177e4SLinus Torvalds 127053bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired); 127126b15dadSJamal Hadi Salim /* 127226b15dadSJamal Hadi Salim * We send to all registered managers regardless of failure 127326b15dadSJamal Hadi Salim * We are happy with one success 127426b15dadSJamal Hadi Salim */ 1275980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 12761da177e4SLinus Torvalds { 127726b15dadSJamal Hadi Salim int err = -EINVAL, acqret; 12781da177e4SLinus Torvalds struct xfrm_mgr *km; 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 12811da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 128226b15dadSJamal Hadi Salim acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); 128326b15dadSJamal Hadi Salim if (!acqret) 128426b15dadSJamal Hadi Salim err = acqret; 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 12871da177e4SLinus Torvalds return err; 12881da177e4SLinus Torvalds } 1289980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query); 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport) 12921da177e4SLinus Torvalds { 12931da177e4SLinus Torvalds int err = -EINVAL; 12941da177e4SLinus Torvalds struct xfrm_mgr *km; 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 12971da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 12981da177e4SLinus Torvalds if (km->new_mapping) 12991da177e4SLinus Torvalds err = km->new_mapping(x, ipaddr, sport); 13001da177e4SLinus Torvalds if (!err) 13011da177e4SLinus Torvalds break; 13021da177e4SLinus Torvalds } 13031da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13041da177e4SLinus Torvalds return err; 13051da177e4SLinus Torvalds } 13061da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping); 13071da177e4SLinus Torvalds 13086c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) 13091da177e4SLinus Torvalds { 131026b15dadSJamal Hadi Salim struct km_event c; 13111da177e4SLinus Torvalds 1312bf08867fSHerbert Xu c.data.hard = hard; 13136c5c8ca7SJamal Hadi Salim c.pid = pid; 1314f60f6b8fSHerbert Xu c.event = XFRM_MSG_POLEXPIRE; 131526b15dadSJamal Hadi Salim km_policy_notify(pol, dir, &c); 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds if (hard) 13181da177e4SLinus Torvalds wake_up(&km_waitq); 13191da177e4SLinus Torvalds } 1320a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired); 13211da177e4SLinus Torvalds 132297a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) 132397a64b45SMasahide NAKAMURA { 132497a64b45SMasahide NAKAMURA int err = -EINVAL; 132597a64b45SMasahide NAKAMURA int ret; 132697a64b45SMasahide NAKAMURA struct xfrm_mgr *km; 132797a64b45SMasahide NAKAMURA 132897a64b45SMasahide NAKAMURA read_lock(&xfrm_km_lock); 132997a64b45SMasahide NAKAMURA list_for_each_entry(km, &xfrm_km_list, list) { 133097a64b45SMasahide NAKAMURA if (km->report) { 133197a64b45SMasahide NAKAMURA ret = km->report(proto, sel, addr); 133297a64b45SMasahide NAKAMURA if (!ret) 133397a64b45SMasahide NAKAMURA err = ret; 133497a64b45SMasahide NAKAMURA } 133597a64b45SMasahide NAKAMURA } 133697a64b45SMasahide NAKAMURA read_unlock(&xfrm_km_lock); 133797a64b45SMasahide NAKAMURA return err; 133897a64b45SMasahide NAKAMURA } 133997a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report); 134097a64b45SMasahide NAKAMURA 13411da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 13421da177e4SLinus Torvalds { 13431da177e4SLinus Torvalds int err; 13441da177e4SLinus Torvalds u8 *data; 13451da177e4SLinus Torvalds struct xfrm_mgr *km; 13461da177e4SLinus Torvalds struct xfrm_policy *pol = NULL; 13471da177e4SLinus Torvalds 13481da177e4SLinus Torvalds if (optlen <= 0 || optlen > PAGE_SIZE) 13491da177e4SLinus Torvalds return -EMSGSIZE; 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds data = kmalloc(optlen, GFP_KERNEL); 13521da177e4SLinus Torvalds if (!data) 13531da177e4SLinus Torvalds return -ENOMEM; 13541da177e4SLinus Torvalds 13551da177e4SLinus Torvalds err = -EFAULT; 13561da177e4SLinus Torvalds if (copy_from_user(data, optval, optlen)) 13571da177e4SLinus Torvalds goto out; 13581da177e4SLinus Torvalds 13591da177e4SLinus Torvalds err = -EINVAL; 13601da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13611da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 1362cb969f07SVenkat Yekkirala pol = km->compile_policy(sk, optname, data, 13631da177e4SLinus Torvalds optlen, &err); 13641da177e4SLinus Torvalds if (err >= 0) 13651da177e4SLinus Torvalds break; 13661da177e4SLinus Torvalds } 13671da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13681da177e4SLinus Torvalds 13691da177e4SLinus Torvalds if (err >= 0) { 13701da177e4SLinus Torvalds xfrm_sk_policy_insert(sk, err, pol); 13711da177e4SLinus Torvalds xfrm_pol_put(pol); 13721da177e4SLinus Torvalds err = 0; 13731da177e4SLinus Torvalds } 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds out: 13761da177e4SLinus Torvalds kfree(data); 13771da177e4SLinus Torvalds return err; 13781da177e4SLinus Torvalds } 13791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy); 13801da177e4SLinus Torvalds 13811da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km) 13821da177e4SLinus Torvalds { 13831da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 13841da177e4SLinus Torvalds list_add_tail(&km->list, &xfrm_km_list); 13851da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 13861da177e4SLinus Torvalds return 0; 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km); 13891da177e4SLinus Torvalds 13901da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km) 13911da177e4SLinus Torvalds { 13921da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 13931da177e4SLinus Torvalds list_del(&km->list); 13941da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 13951da177e4SLinus Torvalds return 0; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km); 13981da177e4SLinus Torvalds 13991da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 14001da177e4SLinus Torvalds { 14011da177e4SLinus Torvalds int err = 0; 14021da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14031da177e4SLinus Torvalds return -EINVAL; 14041da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14051da177e4SLinus Torvalds return -EAFNOSUPPORT; 1406f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14071da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 14081da177e4SLinus Torvalds err = -ENOBUFS; 1409edcd5821SDavid S. Miller else 14101da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = afinfo; 1411f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14121da177e4SLinus Torvalds return err; 14131da177e4SLinus Torvalds } 14141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo); 14151da177e4SLinus Torvalds 14161da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 14171da177e4SLinus Torvalds { 14181da177e4SLinus Torvalds int err = 0; 14191da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14201da177e4SLinus Torvalds return -EINVAL; 14211da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14221da177e4SLinus Torvalds return -EAFNOSUPPORT; 1423f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14241da177e4SLinus Torvalds if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 14251da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) 14261da177e4SLinus Torvalds err = -EINVAL; 1427edcd5821SDavid S. Miller else 14281da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = NULL; 14291da177e4SLinus Torvalds } 1430f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14311da177e4SLinus Torvalds return err; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo); 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) 14361da177e4SLinus Torvalds { 14371da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 14381da177e4SLinus Torvalds if (unlikely(family >= NPROTO)) 14391da177e4SLinus Torvalds return NULL; 14401da177e4SLinus Torvalds read_lock(&xfrm_state_afinfo_lock); 14411da177e4SLinus Torvalds afinfo = xfrm_state_afinfo[family]; 1442546be240SHerbert Xu if (unlikely(!afinfo)) 14431da177e4SLinus Torvalds read_unlock(&xfrm_state_afinfo_lock); 14441da177e4SLinus Torvalds return afinfo; 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds 14471da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) 14481da177e4SLinus Torvalds { 1449546be240SHerbert Xu read_unlock(&xfrm_state_afinfo_lock); 14501da177e4SLinus Torvalds } 14511da177e4SLinus Torvalds 14521da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 14531da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x) 14541da177e4SLinus Torvalds { 14551da177e4SLinus Torvalds if (x->tunnel) { 14561da177e4SLinus Torvalds struct xfrm_state *t = x->tunnel; 14571da177e4SLinus Torvalds 14581da177e4SLinus Torvalds if (atomic_read(&t->tunnel_users) == 2) 14591da177e4SLinus Torvalds xfrm_state_delete(t); 14601da177e4SLinus Torvalds atomic_dec(&t->tunnel_users); 14611da177e4SLinus Torvalds xfrm_state_put(t); 14621da177e4SLinus Torvalds x->tunnel = NULL; 14631da177e4SLinus Torvalds } 14641da177e4SLinus Torvalds } 14651da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel); 14661da177e4SLinus Torvalds 146780b30c10SHerbert Xu /* 146880b30c10SHerbert Xu * This function is NOT optimal. For example, with ESP it will give an 146980b30c10SHerbert Xu * MTU that's usually two bytes short of being optimal. However, it will 147080b30c10SHerbert Xu * usually give an answer that's a multiple of 4 provided the input is 147180b30c10SHerbert Xu * also a multiple of 4. 147280b30c10SHerbert Xu */ 14731da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu) 14741da177e4SLinus Torvalds { 14751da177e4SLinus Torvalds int res = mtu; 14761da177e4SLinus Torvalds 14771da177e4SLinus Torvalds res -= x->props.header_len; 14781da177e4SLinus Torvalds 14791da177e4SLinus Torvalds for (;;) { 14801da177e4SLinus Torvalds int m = res; 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds if (m < 68) 14831da177e4SLinus Torvalds return 68; 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds spin_lock_bh(&x->lock); 14861da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID && 14871da177e4SLinus Torvalds x->type && x->type->get_max_size) 14881da177e4SLinus Torvalds m = x->type->get_max_size(x, m); 14891da177e4SLinus Torvalds else 14901da177e4SLinus Torvalds m += x->props.header_len; 14911da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 14921da177e4SLinus Torvalds 14931da177e4SLinus Torvalds if (m <= mtu) 14941da177e4SLinus Torvalds break; 14951da177e4SLinus Torvalds res -= (m - mtu); 14961da177e4SLinus Torvalds } 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds return res; 14991da177e4SLinus Torvalds } 15001da177e4SLinus Torvalds 150172cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x) 150272cb6962SHerbert Xu { 1503d094cd83SHerbert Xu struct xfrm_state_afinfo *afinfo; 1504d094cd83SHerbert Xu int family = x->props.family; 150572cb6962SHerbert Xu int err; 150672cb6962SHerbert Xu 1507d094cd83SHerbert Xu err = -EAFNOSUPPORT; 1508d094cd83SHerbert Xu afinfo = xfrm_state_get_afinfo(family); 1509d094cd83SHerbert Xu if (!afinfo) 1510d094cd83SHerbert Xu goto error; 1511d094cd83SHerbert Xu 1512d094cd83SHerbert Xu err = 0; 1513d094cd83SHerbert Xu if (afinfo->init_flags) 1514d094cd83SHerbert Xu err = afinfo->init_flags(x); 1515d094cd83SHerbert Xu 1516d094cd83SHerbert Xu xfrm_state_put_afinfo(afinfo); 1517d094cd83SHerbert Xu 1518d094cd83SHerbert Xu if (err) 1519d094cd83SHerbert Xu goto error; 1520d094cd83SHerbert Xu 1521d094cd83SHerbert Xu err = -EPROTONOSUPPORT; 1522d094cd83SHerbert Xu x->type = xfrm_get_type(x->id.proto, family); 152372cb6962SHerbert Xu if (x->type == NULL) 152472cb6962SHerbert Xu goto error; 152572cb6962SHerbert Xu 152672cb6962SHerbert Xu err = x->type->init_state(x); 152772cb6962SHerbert Xu if (err) 152872cb6962SHerbert Xu goto error; 152972cb6962SHerbert Xu 1530b59f45d0SHerbert Xu x->mode = xfrm_get_mode(x->props.mode, family); 1531b59f45d0SHerbert Xu if (x->mode == NULL) 1532b59f45d0SHerbert Xu goto error; 1533b59f45d0SHerbert Xu 153472cb6962SHerbert Xu x->km.state = XFRM_STATE_VALID; 153572cb6962SHerbert Xu 153672cb6962SHerbert Xu error: 153772cb6962SHerbert Xu return err; 153872cb6962SHerbert Xu } 153972cb6962SHerbert Xu 154072cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state); 15411da177e4SLinus Torvalds 15421da177e4SLinus Torvalds void __init xfrm_state_init(void) 15431da177e4SLinus Torvalds { 1544f034b5d4SDavid S. Miller unsigned int sz; 15451da177e4SLinus Torvalds 1546f034b5d4SDavid S. Miller sz = sizeof(struct hlist_head) * 8; 1547f034b5d4SDavid S. Miller 154844e36b42SDavid S. Miller xfrm_state_bydst = xfrm_hash_alloc(sz); 154944e36b42SDavid S. Miller xfrm_state_bysrc = xfrm_hash_alloc(sz); 155044e36b42SDavid S. Miller xfrm_state_byspi = xfrm_hash_alloc(sz); 1551f034b5d4SDavid S. Miller if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi) 1552f034b5d4SDavid S. Miller panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes."); 1553f034b5d4SDavid S. Miller xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1); 1554f034b5d4SDavid S. Miller 15551da177e4SLinus Torvalds INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL); 15561da177e4SLinus Torvalds } 15571da177e4SLinus Torvalds 1558