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 66667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr, 67667bbcb6SMasahide NAKAMURA xfrm_address_t *saddr, 6844e36b42SDavid S. Miller unsigned short family) 69f034b5d4SDavid S. Miller { 70667bbcb6SMasahide NAKAMURA return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask); 71f034b5d4SDavid S. Miller } 72f034b5d4SDavid S. Miller 732575b654SDavid S. Miller static inline unsigned int 748122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 75f034b5d4SDavid S. Miller { 76c1969f29SDavid S. Miller return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask); 77f034b5d4SDavid S. Miller } 78f034b5d4SDavid S. Miller 79f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list, 80f034b5d4SDavid S. Miller struct hlist_head *ndsttable, 81f034b5d4SDavid S. Miller struct hlist_head *nsrctable, 82f034b5d4SDavid S. Miller struct hlist_head *nspitable, 83f034b5d4SDavid S. Miller unsigned int nhashmask) 84f034b5d4SDavid S. Miller { 85f034b5d4SDavid S. Miller struct hlist_node *entry, *tmp; 86f034b5d4SDavid S. Miller struct xfrm_state *x; 87f034b5d4SDavid S. Miller 88f034b5d4SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, list, bydst) { 89f034b5d4SDavid S. Miller unsigned int h; 90f034b5d4SDavid S. Miller 91c1969f29SDavid S. Miller h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 92c1969f29SDavid S. Miller x->props.reqid, x->props.family, 93c1969f29SDavid S. Miller nhashmask); 94f034b5d4SDavid S. Miller hlist_add_head(&x->bydst, ndsttable+h); 95f034b5d4SDavid S. Miller 96667bbcb6SMasahide NAKAMURA h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, 97667bbcb6SMasahide NAKAMURA x->props.family, 98f034b5d4SDavid S. Miller nhashmask); 99f034b5d4SDavid S. Miller hlist_add_head(&x->bysrc, nsrctable+h); 100f034b5d4SDavid S. Miller 1017b4dc360SMasahide NAKAMURA if (x->id.spi) { 1027b4dc360SMasahide NAKAMURA h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, 1037b4dc360SMasahide NAKAMURA x->id.proto, x->props.family, 1047b4dc360SMasahide NAKAMURA nhashmask); 105f034b5d4SDavid S. Miller hlist_add_head(&x->byspi, nspitable+h); 106f034b5d4SDavid S. Miller } 107f034b5d4SDavid S. Miller } 1087b4dc360SMasahide NAKAMURA } 109f034b5d4SDavid S. Miller 110f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void) 111f034b5d4SDavid S. Miller { 112f034b5d4SDavid S. Miller return ((xfrm_state_hmask + 1) << 1) * 113f034b5d4SDavid S. Miller sizeof(struct hlist_head); 114f034b5d4SDavid S. Miller } 115f034b5d4SDavid S. Miller 116f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex); 117f034b5d4SDavid S. Miller 118c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused) 119f034b5d4SDavid S. Miller { 120f034b5d4SDavid S. Miller struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; 121f034b5d4SDavid S. Miller unsigned long nsize, osize; 122f034b5d4SDavid S. Miller unsigned int nhashmask, ohashmask; 123f034b5d4SDavid S. Miller int i; 124f034b5d4SDavid S. Miller 125f034b5d4SDavid S. Miller mutex_lock(&hash_resize_mutex); 126f034b5d4SDavid S. Miller 127f034b5d4SDavid S. Miller nsize = xfrm_hash_new_size(); 12844e36b42SDavid S. Miller ndst = xfrm_hash_alloc(nsize); 129f034b5d4SDavid S. Miller if (!ndst) 130f034b5d4SDavid S. Miller goto out_unlock; 13144e36b42SDavid S. Miller nsrc = xfrm_hash_alloc(nsize); 132f034b5d4SDavid S. Miller if (!nsrc) { 13344e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 134f034b5d4SDavid S. Miller goto out_unlock; 135f034b5d4SDavid S. Miller } 13644e36b42SDavid S. Miller nspi = xfrm_hash_alloc(nsize); 137f034b5d4SDavid S. Miller if (!nspi) { 13844e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 13944e36b42SDavid S. Miller xfrm_hash_free(nsrc, nsize); 140f034b5d4SDavid S. Miller goto out_unlock; 141f034b5d4SDavid S. Miller } 142f034b5d4SDavid S. Miller 143f034b5d4SDavid S. Miller spin_lock_bh(&xfrm_state_lock); 144f034b5d4SDavid S. Miller 145f034b5d4SDavid S. Miller nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; 146f034b5d4SDavid S. Miller for (i = xfrm_state_hmask; i >= 0; i--) 147f034b5d4SDavid S. Miller xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi, 148f034b5d4SDavid S. Miller nhashmask); 149f034b5d4SDavid S. Miller 150f034b5d4SDavid S. Miller odst = xfrm_state_bydst; 151f034b5d4SDavid S. Miller osrc = xfrm_state_bysrc; 152f034b5d4SDavid S. Miller ospi = xfrm_state_byspi; 153f034b5d4SDavid S. Miller ohashmask = xfrm_state_hmask; 154f034b5d4SDavid S. Miller 155f034b5d4SDavid S. Miller xfrm_state_bydst = ndst; 156f034b5d4SDavid S. Miller xfrm_state_bysrc = nsrc; 157f034b5d4SDavid S. Miller xfrm_state_byspi = nspi; 158f034b5d4SDavid S. Miller xfrm_state_hmask = nhashmask; 159f034b5d4SDavid S. Miller 160f034b5d4SDavid S. Miller spin_unlock_bh(&xfrm_state_lock); 161f034b5d4SDavid S. Miller 162f034b5d4SDavid S. Miller osize = (ohashmask + 1) * sizeof(struct hlist_head); 16344e36b42SDavid S. Miller xfrm_hash_free(odst, osize); 16444e36b42SDavid S. Miller xfrm_hash_free(osrc, osize); 16544e36b42SDavid S. Miller xfrm_hash_free(ospi, osize); 166f034b5d4SDavid S. Miller 167f034b5d4SDavid S. Miller out_unlock: 168f034b5d4SDavid S. Miller mutex_unlock(&hash_resize_mutex); 169f034b5d4SDavid S. Miller } 170f034b5d4SDavid S. Miller 171c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize); 172f034b5d4SDavid S. Miller 1731da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq); 1741da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq); 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock); 1771da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work; 1808f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list); 1811da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock); 1821da177e4SLinus Torvalds 18353bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x); 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family); 1861da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); 1871da177e4SLinus Torvalds 188980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 18953bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid); 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x) 1921da177e4SLinus Torvalds { 193a47f0ce0SDavid S. Miller del_timer_sync(&x->timer); 194a47f0ce0SDavid S. Miller del_timer_sync(&x->rtimer); 1951da177e4SLinus Torvalds kfree(x->aalg); 1961da177e4SLinus Torvalds kfree(x->ealg); 1971da177e4SLinus Torvalds kfree(x->calg); 1981da177e4SLinus Torvalds kfree(x->encap); 199060f02a3SNoriaki TAKAMIYA kfree(x->coaddr); 200b59f45d0SHerbert Xu if (x->mode) 201b59f45d0SHerbert Xu xfrm_put_mode(x->mode); 2021da177e4SLinus Torvalds if (x->type) { 2031da177e4SLinus Torvalds x->type->destructor(x); 2041da177e4SLinus Torvalds xfrm_put_type(x->type); 2051da177e4SLinus Torvalds } 206df71837dSTrent Jaeger security_xfrm_state_free(x); 2071da177e4SLinus Torvalds kfree(x); 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 210c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds struct xfrm_state *x; 2138f126e37SDavid S. Miller struct hlist_node *entry, *tmp; 2148f126e37SDavid S. Miller struct hlist_head gc_list; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 2178f126e37SDavid S. Miller gc_list.first = xfrm_state_gc_list.first; 2188f126e37SDavid S. Miller INIT_HLIST_HEAD(&xfrm_state_gc_list); 2191da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 2201da177e4SLinus Torvalds 2218f126e37SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst) 2221da177e4SLinus Torvalds xfrm_state_gc_destroy(x); 2238f126e37SDavid S. Miller 2241da177e4SLinus Torvalds wake_up(&km_waitq); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs) 2281da177e4SLinus Torvalds { 2291da177e4SLinus Torvalds if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 2301da177e4SLinus Torvalds return MAX_SCHEDULE_TIMEOUT-1; 2311da177e4SLinus Torvalds else 2321da177e4SLinus Torvalds return secs*HZ; 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data) 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds struct xfrm_state *x = (struct xfrm_state*)data; 2381da177e4SLinus Torvalds unsigned long now = (unsigned long)xtime.tv_sec; 2391da177e4SLinus Torvalds long next = LONG_MAX; 2401da177e4SLinus Torvalds int warn = 0; 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds spin_lock(&x->lock); 2431da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_DEAD) 2441da177e4SLinus Torvalds goto out; 2451da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_EXPIRED) 2461da177e4SLinus Torvalds goto expired; 2471da177e4SLinus Torvalds if (x->lft.hard_add_expires_seconds) { 2481da177e4SLinus Torvalds long tmo = x->lft.hard_add_expires_seconds + 2491da177e4SLinus Torvalds x->curlft.add_time - now; 2501da177e4SLinus Torvalds if (tmo <= 0) 2511da177e4SLinus Torvalds goto expired; 2521da177e4SLinus Torvalds if (tmo < next) 2531da177e4SLinus Torvalds next = tmo; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds if (x->lft.hard_use_expires_seconds) { 2561da177e4SLinus Torvalds long tmo = x->lft.hard_use_expires_seconds + 2571da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2581da177e4SLinus Torvalds if (tmo <= 0) 2591da177e4SLinus Torvalds goto expired; 2601da177e4SLinus Torvalds if (tmo < next) 2611da177e4SLinus Torvalds next = tmo; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds if (x->km.dying) 2641da177e4SLinus Torvalds goto resched; 2651da177e4SLinus Torvalds if (x->lft.soft_add_expires_seconds) { 2661da177e4SLinus Torvalds long tmo = x->lft.soft_add_expires_seconds + 2671da177e4SLinus Torvalds x->curlft.add_time - now; 2681da177e4SLinus Torvalds if (tmo <= 0) 2691da177e4SLinus Torvalds warn = 1; 2701da177e4SLinus Torvalds else if (tmo < next) 2711da177e4SLinus Torvalds next = tmo; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds if (x->lft.soft_use_expires_seconds) { 2741da177e4SLinus Torvalds long tmo = x->lft.soft_use_expires_seconds + 2751da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2761da177e4SLinus Torvalds if (tmo <= 0) 2771da177e4SLinus Torvalds warn = 1; 2781da177e4SLinus Torvalds else if (tmo < next) 2791da177e4SLinus Torvalds next = tmo; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2824666faabSHerbert Xu x->km.dying = warn; 2831da177e4SLinus Torvalds if (warn) 28453bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 2851da177e4SLinus Torvalds resched: 286a47f0ce0SDavid S. Miller if (next != LONG_MAX) 287a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + make_jiffies(next)); 288a47f0ce0SDavid S. Miller 2891da177e4SLinus Torvalds goto out; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds expired: 2921da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { 2931da177e4SLinus Torvalds x->km.state = XFRM_STATE_EXPIRED; 2941da177e4SLinus Torvalds wake_up(&km_waitq); 2951da177e4SLinus Torvalds next = 2; 2961da177e4SLinus Torvalds goto resched; 2971da177e4SLinus Torvalds } 2984666faabSHerbert Xu if (!__xfrm_state_delete(x) && x->id.spi) 29953bc6b4dSJamal Hadi Salim km_state_expired(x, 1, 0); 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds out: 3021da177e4SLinus Torvalds spin_unlock(&x->lock); 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3050ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data); 3060ac84752SDavid S. Miller 3071da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds struct xfrm_state *x; 3101da177e4SLinus Torvalds 3110da974f4SPanagiotis Issaris x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds if (x) { 3141da177e4SLinus Torvalds atomic_set(&x->refcnt, 1); 3151da177e4SLinus Torvalds atomic_set(&x->tunnel_users, 0); 3168f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bydst); 3178f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bysrc); 3188f126e37SDavid S. Miller INIT_HLIST_NODE(&x->byspi); 3191da177e4SLinus Torvalds init_timer(&x->timer); 3201da177e4SLinus Torvalds x->timer.function = xfrm_timer_handler; 3211da177e4SLinus Torvalds x->timer.data = (unsigned long)x; 322f8cd5488SJamal Hadi Salim init_timer(&x->rtimer); 323f8cd5488SJamal Hadi Salim x->rtimer.function = xfrm_replay_timer_handler; 324f8cd5488SJamal Hadi Salim x->rtimer.data = (unsigned long)x; 3251da177e4SLinus Torvalds x->curlft.add_time = (unsigned long)xtime.tv_sec; 3261da177e4SLinus Torvalds x->lft.soft_byte_limit = XFRM_INF; 3271da177e4SLinus Torvalds x->lft.soft_packet_limit = XFRM_INF; 3281da177e4SLinus Torvalds x->lft.hard_byte_limit = XFRM_INF; 3291da177e4SLinus Torvalds x->lft.hard_packet_limit = XFRM_INF; 330f8cd5488SJamal Hadi Salim x->replay_maxage = 0; 331f8cd5488SJamal Hadi Salim x->replay_maxdiff = 0; 3321da177e4SLinus Torvalds spin_lock_init(&x->lock); 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds return x; 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc); 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x) 3391da177e4SLinus Torvalds { 3401da177e4SLinus Torvalds BUG_TRAP(x->km.state == XFRM_STATE_DEAD); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 3438f126e37SDavid S. Miller hlist_add_head(&x->bydst, &xfrm_state_gc_list); 3441da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 3451da177e4SLinus Torvalds schedule_work(&xfrm_state_gc_work); 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy); 3481da177e4SLinus Torvalds 34953bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x) 3501da177e4SLinus Torvalds { 35126b15dadSJamal Hadi Salim int err = -ESRCH; 35226b15dadSJamal Hadi Salim 3531da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_DEAD) { 3541da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 3551da177e4SLinus Torvalds spin_lock(&xfrm_state_lock); 3568f126e37SDavid S. Miller hlist_del(&x->bydst); 3578f126e37SDavid S. Miller hlist_del(&x->bysrc); 358a47f0ce0SDavid S. Miller if (x->id.spi) 3598f126e37SDavid S. Miller hlist_del(&x->byspi); 360f034b5d4SDavid S. Miller xfrm_state_num--; 3611da177e4SLinus Torvalds spin_unlock(&xfrm_state_lock); 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds /* All xfrm_state objects are created by xfrm_state_alloc. 3641da177e4SLinus Torvalds * The xfrm_state_alloc call gives a reference, and that 3651da177e4SLinus Torvalds * is what we are dropping here. 3661da177e4SLinus Torvalds */ 36721380b81SHerbert Xu __xfrm_state_put(x); 36826b15dadSJamal Hadi Salim err = 0; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 37126b15dadSJamal Hadi Salim return err; 37226b15dadSJamal Hadi Salim } 37353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete); 37426b15dadSJamal Hadi Salim 37526b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x) 3761da177e4SLinus Torvalds { 37726b15dadSJamal Hadi Salim int err; 37826b15dadSJamal Hadi Salim 3791da177e4SLinus Torvalds spin_lock_bh(&x->lock); 38026b15dadSJamal Hadi Salim err = __xfrm_state_delete(x); 3811da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 38226b15dadSJamal Hadi Salim 38326b15dadSJamal Hadi Salim return err; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete); 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds void xfrm_state_flush(u8 proto) 3881da177e4SLinus Torvalds { 3891da177e4SLinus Torvalds int i; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 392a9917c06SMasahide NAKAMURA for (i = 0; i <= xfrm_state_hmask; i++) { 3938f126e37SDavid S. Miller struct hlist_node *entry; 3948f126e37SDavid S. Miller struct xfrm_state *x; 3951da177e4SLinus Torvalds restart: 3968f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 3971da177e4SLinus Torvalds if (!xfrm_state_kern(x) && 3985794708fSMasahide NAKAMURA xfrm_id_proto_match(x->id.proto, proto)) { 3991da177e4SLinus Torvalds xfrm_state_hold(x); 4001da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds xfrm_state_delete(x); 4031da177e4SLinus Torvalds xfrm_state_put(x); 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 4061da177e4SLinus Torvalds goto restart; 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds } 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4111da177e4SLinus Torvalds wake_up(&km_waitq); 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds static int 4161da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, 4171da177e4SLinus Torvalds struct xfrm_tmpl *tmpl, 4181da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 4191da177e4SLinus Torvalds unsigned short family) 4201da177e4SLinus Torvalds { 4211da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 4221da177e4SLinus Torvalds if (!afinfo) 4231da177e4SLinus Torvalds return -1; 4241da177e4SLinus Torvalds afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); 4251da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 4261da177e4SLinus Torvalds return 0; 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds 429a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 430edcd5821SDavid S. Miller { 431edcd5821SDavid S. Miller unsigned int h = xfrm_spi_hash(daddr, spi, proto, family); 432edcd5821SDavid S. Miller struct xfrm_state *x; 4338f126e37SDavid S. Miller struct hlist_node *entry; 434edcd5821SDavid S. Miller 4358f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) { 436edcd5821SDavid S. Miller if (x->props.family != family || 437edcd5821SDavid S. Miller x->id.spi != spi || 438edcd5821SDavid S. Miller x->id.proto != proto) 439edcd5821SDavid S. Miller continue; 440edcd5821SDavid S. Miller 441edcd5821SDavid S. Miller switch (family) { 442edcd5821SDavid S. Miller case AF_INET: 443edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4) 444edcd5821SDavid S. Miller continue; 445edcd5821SDavid S. Miller break; 446edcd5821SDavid S. Miller case AF_INET6: 447edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 448edcd5821SDavid S. Miller (struct in6_addr *) 449edcd5821SDavid S. Miller x->id.daddr.a6)) 450edcd5821SDavid S. Miller continue; 451edcd5821SDavid S. Miller break; 452edcd5821SDavid S. Miller }; 453edcd5821SDavid S. Miller 454edcd5821SDavid S. Miller xfrm_state_hold(x); 455edcd5821SDavid S. Miller return x; 456edcd5821SDavid S. Miller } 457edcd5821SDavid S. Miller 458edcd5821SDavid S. Miller return NULL; 459edcd5821SDavid S. Miller } 460edcd5821SDavid S. Miller 461edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family) 462edcd5821SDavid S. Miller { 463667bbcb6SMasahide NAKAMURA unsigned int h = xfrm_src_hash(daddr, saddr, family); 464edcd5821SDavid S. Miller struct xfrm_state *x; 4658f126e37SDavid S. Miller struct hlist_node *entry; 466edcd5821SDavid S. Miller 4678f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) { 468edcd5821SDavid S. Miller if (x->props.family != family || 469edcd5821SDavid S. Miller x->id.proto != proto) 470edcd5821SDavid S. Miller continue; 471edcd5821SDavid S. Miller 472edcd5821SDavid S. Miller switch (family) { 473edcd5821SDavid S. Miller case AF_INET: 474edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 475edcd5821SDavid S. Miller x->props.saddr.a4 != saddr->a4) 476edcd5821SDavid S. Miller continue; 477edcd5821SDavid S. Miller break; 478edcd5821SDavid S. Miller case AF_INET6: 479edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 480edcd5821SDavid S. Miller (struct in6_addr *) 481edcd5821SDavid S. Miller x->id.daddr.a6) || 482edcd5821SDavid S. Miller !ipv6_addr_equal((struct in6_addr *)saddr, 483edcd5821SDavid S. Miller (struct in6_addr *) 484edcd5821SDavid S. Miller x->props.saddr.a6)) 485edcd5821SDavid S. Miller continue; 486edcd5821SDavid S. Miller break; 487edcd5821SDavid S. Miller }; 488edcd5821SDavid S. Miller 489edcd5821SDavid S. Miller xfrm_state_hold(x); 490edcd5821SDavid S. Miller return x; 491edcd5821SDavid S. Miller } 492edcd5821SDavid S. Miller 493edcd5821SDavid S. Miller return NULL; 494edcd5821SDavid S. Miller } 495edcd5821SDavid S. Miller 496edcd5821SDavid S. Miller static inline struct xfrm_state * 497edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family) 498edcd5821SDavid S. Miller { 499edcd5821SDavid S. Miller if (use_spi) 500edcd5821SDavid S. Miller return __xfrm_state_lookup(&x->id.daddr, x->id.spi, 501edcd5821SDavid S. Miller x->id.proto, family); 502edcd5821SDavid S. Miller else 503edcd5821SDavid S. Miller return __xfrm_state_lookup_byaddr(&x->id.daddr, 504edcd5821SDavid S. Miller &x->props.saddr, 505edcd5821SDavid S. Miller x->id.proto, family); 506edcd5821SDavid S. Miller } 507edcd5821SDavid S. Miller 5082fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision) 5092fab22f2SPatrick McHardy { 5102fab22f2SPatrick McHardy if (have_hash_collision && 5112fab22f2SPatrick McHardy (xfrm_state_hmask + 1) < xfrm_state_hashmax && 5122fab22f2SPatrick McHardy xfrm_state_num > xfrm_state_hmask) 5132fab22f2SPatrick McHardy schedule_work(&xfrm_hash_work); 5142fab22f2SPatrick McHardy } 5152fab22f2SPatrick McHardy 5161da177e4SLinus Torvalds struct xfrm_state * 5171da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 5181da177e4SLinus Torvalds struct flowi *fl, struct xfrm_tmpl *tmpl, 5191da177e4SLinus Torvalds struct xfrm_policy *pol, int *err, 5201da177e4SLinus Torvalds unsigned short family) 5211da177e4SLinus Torvalds { 522c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); 5238f126e37SDavid S. Miller struct hlist_node *entry; 5241da177e4SLinus Torvalds struct xfrm_state *x, *x0; 5251da177e4SLinus Torvalds int acquire_in_progress = 0; 5261da177e4SLinus Torvalds int error = 0; 5271da177e4SLinus Torvalds struct xfrm_state *best = NULL; 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 5308f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 5311da177e4SLinus Torvalds if (x->props.family == family && 5321da177e4SLinus Torvalds x->props.reqid == tmpl->reqid && 533fbd9a5b4SMasahide NAKAMURA !(x->props.flags & XFRM_STATE_WILDRECV) && 5341da177e4SLinus Torvalds xfrm_state_addr_check(x, daddr, saddr, family) && 5351da177e4SLinus Torvalds tmpl->mode == x->props.mode && 5361da177e4SLinus Torvalds tmpl->id.proto == x->id.proto && 5371da177e4SLinus Torvalds (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { 5381da177e4SLinus Torvalds /* Resolution logic: 5391da177e4SLinus Torvalds 1. There is a valid state with matching selector. 5401da177e4SLinus Torvalds Done. 5411da177e4SLinus Torvalds 2. Valid state with inappropriate selector. Skip. 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds Entering area of "sysdeps". 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds 3. If state is not valid, selector is temporary, 5461da177e4SLinus Torvalds it selects only session which triggered 5471da177e4SLinus Torvalds previous resolution. Key manager will do 5481da177e4SLinus Torvalds something to install a state with proper 5491da177e4SLinus Torvalds selector. 5501da177e4SLinus Torvalds */ 5511da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID) { 552df71837dSTrent Jaeger if (!xfrm_selector_match(&x->sel, fl, family) || 553e0d1caa7SVenkat Yekkirala !security_xfrm_state_pol_flow_match(x, pol, fl)) 5541da177e4SLinus Torvalds continue; 5551da177e4SLinus Torvalds if (!best || 5561da177e4SLinus Torvalds best->km.dying > x->km.dying || 5571da177e4SLinus Torvalds (best->km.dying == x->km.dying && 5581da177e4SLinus Torvalds best->curlft.add_time < x->curlft.add_time)) 5591da177e4SLinus Torvalds best = x; 5601da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ACQ) { 5611da177e4SLinus Torvalds acquire_in_progress = 1; 5621da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ERROR || 5631da177e4SLinus Torvalds x->km.state == XFRM_STATE_EXPIRED) { 564df71837dSTrent Jaeger if (xfrm_selector_match(&x->sel, fl, family) && 565e0d1caa7SVenkat Yekkirala security_xfrm_state_pol_flow_match(x, pol, fl)) 5661da177e4SLinus Torvalds error = -ESRCH; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds x = best; 5721da177e4SLinus Torvalds if (!x && !error && !acquire_in_progress) { 5735c5d281aSPatrick McHardy if (tmpl->id.spi && 574edcd5821SDavid S. Miller (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi, 575edcd5821SDavid S. Miller tmpl->id.proto, family)) != NULL) { 5761da177e4SLinus Torvalds xfrm_state_put(x0); 5771da177e4SLinus Torvalds error = -EEXIST; 5781da177e4SLinus Torvalds goto out; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds x = xfrm_state_alloc(); 5811da177e4SLinus Torvalds if (x == NULL) { 5821da177e4SLinus Torvalds error = -ENOMEM; 5831da177e4SLinus Torvalds goto out; 5841da177e4SLinus Torvalds } 5851da177e4SLinus Torvalds /* Initialize temporary selector matching only 5861da177e4SLinus Torvalds * to current session. */ 5871da177e4SLinus Torvalds xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); 5881da177e4SLinus Torvalds 589e0d1caa7SVenkat Yekkirala error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); 590e0d1caa7SVenkat Yekkirala if (error) { 591e0d1caa7SVenkat Yekkirala x->km.state = XFRM_STATE_DEAD; 592e0d1caa7SVenkat Yekkirala xfrm_state_put(x); 593e0d1caa7SVenkat Yekkirala x = NULL; 594e0d1caa7SVenkat Yekkirala goto out; 595e0d1caa7SVenkat Yekkirala } 596e0d1caa7SVenkat Yekkirala 5971da177e4SLinus Torvalds if (km_query(x, tmpl, pol) == 0) { 5981da177e4SLinus Torvalds x->km.state = XFRM_STATE_ACQ; 5998f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 600667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(daddr, saddr, family); 6018f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 6021da177e4SLinus Torvalds if (x->id.spi) { 6031da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); 6048f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 6071da177e4SLinus Torvalds x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 6081da177e4SLinus Torvalds add_timer(&x->timer); 6092fab22f2SPatrick McHardy xfrm_state_num++; 6102fab22f2SPatrick McHardy xfrm_hash_grow_check(x->bydst.next != NULL); 6111da177e4SLinus Torvalds } else { 6121da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 6131da177e4SLinus Torvalds xfrm_state_put(x); 6141da177e4SLinus Torvalds x = NULL; 6151da177e4SLinus Torvalds error = -ESRCH; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds out: 6191da177e4SLinus Torvalds if (x) 6201da177e4SLinus Torvalds xfrm_state_hold(x); 6211da177e4SLinus Torvalds else 6221da177e4SLinus Torvalds *err = acquire_in_progress ? -EAGAIN : error; 6231da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6241da177e4SLinus Torvalds return x; 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds 6271da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x) 6281da177e4SLinus Torvalds { 629a624c108SDavid S. Miller unsigned int h; 6301da177e4SLinus Torvalds 6319d4a706dSDavid S. Miller x->genid = ++xfrm_state_genid; 6329d4a706dSDavid S. Miller 633c1969f29SDavid S. Miller h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 634c1969f29SDavid S. Miller x->props.reqid, x->props.family); 6358f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 6361da177e4SLinus Torvalds 637667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family); 6388f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 6396c44e6b7SMasahide NAKAMURA 6407b4dc360SMasahide NAKAMURA if (x->id.spi) { 6416c44e6b7SMasahide NAKAMURA h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, 6426c44e6b7SMasahide NAKAMURA x->props.family); 6431da177e4SLinus Torvalds 6448f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 6456c44e6b7SMasahide NAKAMURA } 6461da177e4SLinus Torvalds 647a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + HZ); 648a47f0ce0SDavid S. Miller if (x->replay_maxage) 649a47f0ce0SDavid S. Miller mod_timer(&x->rtimer, jiffies + x->replay_maxage); 650f8cd5488SJamal Hadi Salim 6511da177e4SLinus Torvalds wake_up(&km_waitq); 652f034b5d4SDavid S. Miller 653f034b5d4SDavid S. Miller xfrm_state_num++; 654f034b5d4SDavid S. Miller 655918049f0SDavid S. Miller xfrm_hash_grow_check(x->bydst.next != NULL); 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 658c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */ 659c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew) 660c7f5ea3aSDavid S. Miller { 661c7f5ea3aSDavid S. Miller unsigned short family = xnew->props.family; 662c7f5ea3aSDavid S. Miller u32 reqid = xnew->props.reqid; 663c7f5ea3aSDavid S. Miller struct xfrm_state *x; 664c7f5ea3aSDavid S. Miller struct hlist_node *entry; 665c7f5ea3aSDavid S. Miller unsigned int h; 666c7f5ea3aSDavid S. Miller 667c1969f29SDavid S. Miller h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family); 668c7f5ea3aSDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 669c7f5ea3aSDavid S. Miller if (x->props.family == family && 670c7f5ea3aSDavid S. Miller x->props.reqid == reqid && 671c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) && 672c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family)) 673c7f5ea3aSDavid S. Miller x->genid = xfrm_state_genid; 674c7f5ea3aSDavid S. Miller } 675c7f5ea3aSDavid S. Miller } 676c7f5ea3aSDavid S. Miller 6771da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x) 6781da177e4SLinus Torvalds { 6791da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 680c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 6811da177e4SLinus Torvalds __xfrm_state_insert(x); 6821da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert); 6851da177e4SLinus Torvalds 6862770834cSDavid S. Miller /* xfrm_state_lock is held */ 6872770834cSDavid 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) 6882770834cSDavid S. Miller { 689c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); 6908f126e37SDavid S. Miller struct hlist_node *entry; 6912770834cSDavid S. Miller struct xfrm_state *x; 6922770834cSDavid S. Miller 6938f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 6942770834cSDavid S. Miller if (x->props.reqid != reqid || 6952770834cSDavid S. Miller x->props.mode != mode || 6962770834cSDavid S. Miller x->props.family != family || 6972770834cSDavid S. Miller x->km.state != XFRM_STATE_ACQ || 6982770834cSDavid S. Miller x->id.spi != 0) 6992770834cSDavid S. Miller continue; 7002770834cSDavid S. Miller 7012770834cSDavid S. Miller switch (family) { 7022770834cSDavid S. Miller case AF_INET: 7032770834cSDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 7042770834cSDavid S. Miller x->props.saddr.a4 != saddr->a4) 7052770834cSDavid S. Miller continue; 7062770834cSDavid S. Miller break; 7072770834cSDavid S. Miller case AF_INET6: 7082770834cSDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6, 7092770834cSDavid S. Miller (struct in6_addr *)daddr) || 7102770834cSDavid S. Miller !ipv6_addr_equal((struct in6_addr *) 7112770834cSDavid S. Miller x->props.saddr.a6, 7122770834cSDavid S. Miller (struct in6_addr *)saddr)) 7132770834cSDavid S. Miller continue; 7142770834cSDavid S. Miller break; 7152770834cSDavid S. Miller }; 7162770834cSDavid S. Miller 7172770834cSDavid S. Miller xfrm_state_hold(x); 7182770834cSDavid S. Miller return x; 7192770834cSDavid S. Miller } 7202770834cSDavid S. Miller 7212770834cSDavid S. Miller if (!create) 7222770834cSDavid S. Miller return NULL; 7232770834cSDavid S. Miller 7242770834cSDavid S. Miller x = xfrm_state_alloc(); 7252770834cSDavid S. Miller if (likely(x)) { 7262770834cSDavid S. Miller switch (family) { 7272770834cSDavid S. Miller case AF_INET: 7282770834cSDavid S. Miller x->sel.daddr.a4 = daddr->a4; 7292770834cSDavid S. Miller x->sel.saddr.a4 = saddr->a4; 7302770834cSDavid S. Miller x->sel.prefixlen_d = 32; 7312770834cSDavid S. Miller x->sel.prefixlen_s = 32; 7322770834cSDavid S. Miller x->props.saddr.a4 = saddr->a4; 7332770834cSDavid S. Miller x->id.daddr.a4 = daddr->a4; 7342770834cSDavid S. Miller break; 7352770834cSDavid S. Miller 7362770834cSDavid S. Miller case AF_INET6: 7372770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, 7382770834cSDavid S. Miller (struct in6_addr *)daddr); 7392770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, 7402770834cSDavid S. Miller (struct in6_addr *)saddr); 7412770834cSDavid S. Miller x->sel.prefixlen_d = 128; 7422770834cSDavid S. Miller x->sel.prefixlen_s = 128; 7432770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, 7442770834cSDavid S. Miller (struct in6_addr *)saddr); 7452770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, 7462770834cSDavid S. Miller (struct in6_addr *)daddr); 7472770834cSDavid S. Miller break; 7482770834cSDavid S. Miller }; 7492770834cSDavid S. Miller 7502770834cSDavid S. Miller x->km.state = XFRM_STATE_ACQ; 7512770834cSDavid S. Miller x->id.proto = proto; 7522770834cSDavid S. Miller x->props.family = family; 7532770834cSDavid S. Miller x->props.mode = mode; 7542770834cSDavid S. Miller x->props.reqid = reqid; 7552770834cSDavid S. Miller x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 7562770834cSDavid S. Miller xfrm_state_hold(x); 7572770834cSDavid S. Miller x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 7582770834cSDavid S. Miller add_timer(&x->timer); 7598f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 760667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(daddr, saddr, family); 7618f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 7622770834cSDavid S. Miller wake_up(&km_waitq); 763918049f0SDavid S. Miller 764918049f0SDavid S. Miller xfrm_state_num++; 765918049f0SDavid S. Miller 766918049f0SDavid S. Miller xfrm_hash_grow_check(x->bydst.next != NULL); 7672770834cSDavid S. Miller } 7682770834cSDavid S. Miller 7692770834cSDavid S. Miller return x; 7702770834cSDavid S. Miller } 7712770834cSDavid S. Miller 7721da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x) 7751da177e4SLinus Torvalds { 7761da177e4SLinus Torvalds struct xfrm_state *x1; 7771da177e4SLinus Torvalds int family; 7781da177e4SLinus Torvalds int err; 779eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 7801da177e4SLinus Torvalds 7811da177e4SLinus Torvalds family = x->props.family; 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7841da177e4SLinus Torvalds 785edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, family); 7861da177e4SLinus Torvalds if (x1) { 7871da177e4SLinus Torvalds xfrm_state_put(x1); 7881da177e4SLinus Torvalds x1 = NULL; 7891da177e4SLinus Torvalds err = -EEXIST; 7901da177e4SLinus Torvalds goto out; 7911da177e4SLinus Torvalds } 7921da177e4SLinus Torvalds 793eb2971b6SMasahide NAKAMURA if (use_spi && x->km.seq) { 7941da177e4SLinus Torvalds x1 = __xfrm_find_acq_byseq(x->km.seq); 7951da177e4SLinus Torvalds if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) { 7961da177e4SLinus Torvalds xfrm_state_put(x1); 7971da177e4SLinus Torvalds x1 = NULL; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds 801eb2971b6SMasahide NAKAMURA if (use_spi && !x1) 8022770834cSDavid S. Miller x1 = __find_acq_core(family, x->props.mode, x->props.reqid, 8032770834cSDavid S. Miller x->id.proto, 8041da177e4SLinus Torvalds &x->id.daddr, &x->props.saddr, 0); 8051da177e4SLinus Torvalds 806c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 8071da177e4SLinus Torvalds __xfrm_state_insert(x); 8081da177e4SLinus Torvalds err = 0; 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds out: 8111da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds if (x1) { 8141da177e4SLinus Torvalds xfrm_state_delete(x1); 8151da177e4SLinus Torvalds xfrm_state_put(x1); 8161da177e4SLinus Torvalds } 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds return err; 8191da177e4SLinus Torvalds } 8201da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add); 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x) 8231da177e4SLinus Torvalds { 8241da177e4SLinus Torvalds struct xfrm_state *x1; 8251da177e4SLinus Torvalds int err; 826eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 8271da177e4SLinus Torvalds 8281da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 829edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, x->props.family); 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds err = -ESRCH; 8321da177e4SLinus Torvalds if (!x1) 8331da177e4SLinus Torvalds goto out; 8341da177e4SLinus Torvalds 8351da177e4SLinus Torvalds if (xfrm_state_kern(x1)) { 8361da177e4SLinus Torvalds xfrm_state_put(x1); 8371da177e4SLinus Torvalds err = -EEXIST; 8381da177e4SLinus Torvalds goto out; 8391da177e4SLinus Torvalds } 8401da177e4SLinus Torvalds 8411da177e4SLinus Torvalds if (x1->km.state == XFRM_STATE_ACQ) { 8421da177e4SLinus Torvalds __xfrm_state_insert(x); 8431da177e4SLinus Torvalds x = NULL; 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds err = 0; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds out: 8481da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds if (err) 8511da177e4SLinus Torvalds return err; 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds if (!x) { 8541da177e4SLinus Torvalds xfrm_state_delete(x1); 8551da177e4SLinus Torvalds xfrm_state_put(x1); 8561da177e4SLinus Torvalds return 0; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds err = -EINVAL; 8601da177e4SLinus Torvalds spin_lock_bh(&x1->lock); 8611da177e4SLinus Torvalds if (likely(x1->km.state == XFRM_STATE_VALID)) { 8621da177e4SLinus Torvalds if (x->encap && x1->encap) 8631da177e4SLinus Torvalds memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 864060f02a3SNoriaki TAKAMIYA if (x->coaddr && x1->coaddr) { 865060f02a3SNoriaki TAKAMIYA memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr)); 866060f02a3SNoriaki TAKAMIYA } 867060f02a3SNoriaki TAKAMIYA if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel))) 868060f02a3SNoriaki TAKAMIYA memcpy(&x1->sel, &x->sel, sizeof(x1->sel)); 8691da177e4SLinus Torvalds memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 8701da177e4SLinus Torvalds x1->km.dying = 0; 8711da177e4SLinus Torvalds 872a47f0ce0SDavid S. Miller mod_timer(&x1->timer, jiffies + HZ); 8731da177e4SLinus Torvalds if (x1->curlft.use_time) 8741da177e4SLinus Torvalds xfrm_state_check_expire(x1); 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds err = 0; 8771da177e4SLinus Torvalds } 8781da177e4SLinus Torvalds spin_unlock_bh(&x1->lock); 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds xfrm_state_put(x1); 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds return err; 8831da177e4SLinus Torvalds } 8841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update); 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x) 8871da177e4SLinus Torvalds { 8881da177e4SLinus Torvalds if (!x->curlft.use_time) 8891da177e4SLinus Torvalds x->curlft.use_time = (unsigned long)xtime.tv_sec; 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_VALID) 8921da177e4SLinus Torvalds return -EINVAL; 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds if (x->curlft.bytes >= x->lft.hard_byte_limit || 8951da177e4SLinus Torvalds x->curlft.packets >= x->lft.hard_packet_limit) { 8964666faabSHerbert Xu x->km.state = XFRM_STATE_EXPIRED; 897a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies); 8981da177e4SLinus Torvalds return -EINVAL; 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds 9011da177e4SLinus Torvalds if (!x->km.dying && 9021da177e4SLinus Torvalds (x->curlft.bytes >= x->lft.soft_byte_limit || 9034666faabSHerbert Xu x->curlft.packets >= x->lft.soft_packet_limit)) { 9044666faabSHerbert Xu x->km.dying = 1; 90553bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 9064666faabSHerbert Xu } 9071da177e4SLinus Torvalds return 0; 9081da177e4SLinus Torvalds } 9091da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire); 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 9121da177e4SLinus Torvalds { 9131da177e4SLinus Torvalds int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 9141da177e4SLinus Torvalds - skb_headroom(skb); 9151da177e4SLinus Torvalds 9161da177e4SLinus Torvalds if (nhead > 0) 9171da177e4SLinus Torvalds return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 9181da177e4SLinus Torvalds 9191da177e4SLinus Torvalds /* Check tail too... */ 9201da177e4SLinus Torvalds return 0; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 9241da177e4SLinus Torvalds { 9251da177e4SLinus Torvalds int err = xfrm_state_check_expire(x); 9261da177e4SLinus Torvalds if (err < 0) 9271da177e4SLinus Torvalds goto err; 9281da177e4SLinus Torvalds err = xfrm_state_check_space(x, skb); 9291da177e4SLinus Torvalds err: 9301da177e4SLinus Torvalds return err; 9311da177e4SLinus Torvalds } 9321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check); 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds struct xfrm_state * 935a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, 9361da177e4SLinus Torvalds unsigned short family) 9371da177e4SLinus Torvalds { 9381da177e4SLinus Torvalds struct xfrm_state *x; 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 941edcd5821SDavid S. Miller x = __xfrm_state_lookup(daddr, spi, proto, family); 9421da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9431da177e4SLinus Torvalds return x; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup); 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds struct xfrm_state * 948eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, 949eb2971b6SMasahide NAKAMURA u8 proto, unsigned short family) 950eb2971b6SMasahide NAKAMURA { 951eb2971b6SMasahide NAKAMURA struct xfrm_state *x; 952eb2971b6SMasahide NAKAMURA 953eb2971b6SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 954edcd5821SDavid S. Miller x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family); 955eb2971b6SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 956eb2971b6SMasahide NAKAMURA return x; 957eb2971b6SMasahide NAKAMURA } 958eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr); 959eb2971b6SMasahide NAKAMURA 960eb2971b6SMasahide NAKAMURA struct xfrm_state * 9611da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto, 9621da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 9631da177e4SLinus Torvalds int create, unsigned short family) 9641da177e4SLinus Torvalds { 9651da177e4SLinus Torvalds struct xfrm_state *x; 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 9682770834cSDavid S. Miller x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create); 9691da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9702770834cSDavid S. Miller 9711da177e4SLinus Torvalds return x; 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq); 9741da177e4SLinus Torvalds 97541a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 97641a49cc3SMasahide NAKAMURA int 97741a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, 97841a49cc3SMasahide NAKAMURA unsigned short family) 97941a49cc3SMasahide NAKAMURA { 98041a49cc3SMasahide NAKAMURA int err = 0; 98141a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 98241a49cc3SMasahide NAKAMURA if (!afinfo) 98341a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 98441a49cc3SMasahide NAKAMURA 98541a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 98641a49cc3SMasahide NAKAMURA if (afinfo->tmpl_sort) 98741a49cc3SMasahide NAKAMURA err = afinfo->tmpl_sort(dst, src, n); 98841a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 98941a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 99041a49cc3SMasahide NAKAMURA return err; 99141a49cc3SMasahide NAKAMURA } 99241a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort); 99341a49cc3SMasahide NAKAMURA 99441a49cc3SMasahide NAKAMURA int 99541a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, 99641a49cc3SMasahide NAKAMURA unsigned short family) 99741a49cc3SMasahide NAKAMURA { 99841a49cc3SMasahide NAKAMURA int err = 0; 99941a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 100041a49cc3SMasahide NAKAMURA if (!afinfo) 100141a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 100241a49cc3SMasahide NAKAMURA 100341a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 100441a49cc3SMasahide NAKAMURA if (afinfo->state_sort) 100541a49cc3SMasahide NAKAMURA err = afinfo->state_sort(dst, src, n); 100641a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 100741a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 100841a49cc3SMasahide NAKAMURA return err; 100941a49cc3SMasahide NAKAMURA } 101041a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort); 101141a49cc3SMasahide NAKAMURA #endif 101241a49cc3SMasahide NAKAMURA 10131da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */ 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq) 10161da177e4SLinus Torvalds { 10171da177e4SLinus Torvalds int i; 10181da177e4SLinus Torvalds 1019f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 10208f126e37SDavid S. Miller struct hlist_node *entry; 10218f126e37SDavid S. Miller struct xfrm_state *x; 10228f126e37SDavid S. Miller 10238f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 10248f126e37SDavid S. Miller if (x->km.seq == seq && 10258f126e37SDavid S. Miller x->km.state == XFRM_STATE_ACQ) { 10261da177e4SLinus Torvalds xfrm_state_hold(x); 10271da177e4SLinus Torvalds return x; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds } 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds return NULL; 10321da177e4SLinus Torvalds } 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq) 10351da177e4SLinus Torvalds { 10361da177e4SLinus Torvalds struct xfrm_state *x; 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10391da177e4SLinus Torvalds x = __xfrm_find_acq_byseq(seq); 10401da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10411da177e4SLinus Torvalds return x; 10421da177e4SLinus Torvalds } 10431da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq); 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds u32 xfrm_get_acqseq(void) 10461da177e4SLinus Torvalds { 10471da177e4SLinus Torvalds u32 res; 10481da177e4SLinus Torvalds static u32 acqseq; 10491da177e4SLinus Torvalds static DEFINE_SPINLOCK(acqseq_lock); 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds spin_lock_bh(&acqseq_lock); 10521da177e4SLinus Torvalds res = (++acqseq ? : ++acqseq); 10531da177e4SLinus Torvalds spin_unlock_bh(&acqseq_lock); 10541da177e4SLinus Torvalds return res; 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq); 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds void 105926977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi) 10601da177e4SLinus Torvalds { 1061f034b5d4SDavid S. Miller unsigned int h; 10621da177e4SLinus Torvalds struct xfrm_state *x0; 10631da177e4SLinus Torvalds 10641da177e4SLinus Torvalds if (x->id.spi) 10651da177e4SLinus Torvalds return; 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds if (minspi == maxspi) { 10681da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); 10691da177e4SLinus Torvalds if (x0) { 10701da177e4SLinus Torvalds xfrm_state_put(x0); 10711da177e4SLinus Torvalds return; 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds x->id.spi = minspi; 10741da177e4SLinus Torvalds } else { 10751da177e4SLinus Torvalds u32 spi = 0; 107626977b4eSAl Viro u32 low = ntohl(minspi); 107726977b4eSAl Viro u32 high = ntohl(maxspi); 107826977b4eSAl Viro for (h=0; h<high-low+1; h++) { 107926977b4eSAl Viro spi = low + net_random()%(high-low+1); 10801da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family); 10811da177e4SLinus Torvalds if (x0 == NULL) { 10821da177e4SLinus Torvalds x->id.spi = htonl(spi); 10831da177e4SLinus Torvalds break; 10841da177e4SLinus Torvalds } 10851da177e4SLinus Torvalds xfrm_state_put(x0); 10861da177e4SLinus Torvalds } 10871da177e4SLinus Torvalds } 10881da177e4SLinus Torvalds if (x->id.spi) { 10891da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10901da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 10918f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 10921da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10931da177e4SLinus Torvalds wake_up(&km_waitq); 10941da177e4SLinus Torvalds } 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi); 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), 10991da177e4SLinus Torvalds void *data) 11001da177e4SLinus Torvalds { 11011da177e4SLinus Torvalds int i; 11021da177e4SLinus Torvalds struct xfrm_state *x; 11038f126e37SDavid S. Miller struct hlist_node *entry; 11041da177e4SLinus Torvalds int count = 0; 11051da177e4SLinus Torvalds int err = 0; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 1108f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 11098f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 11105794708fSMasahide NAKAMURA if (xfrm_id_proto_match(x->id.proto, proto)) 11111da177e4SLinus Torvalds count++; 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds if (count == 0) { 11151da177e4SLinus Torvalds err = -ENOENT; 11161da177e4SLinus Torvalds goto out; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds 1119f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 11208f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 11215794708fSMasahide NAKAMURA if (!xfrm_id_proto_match(x->id.proto, proto)) 11221da177e4SLinus Torvalds continue; 11231da177e4SLinus Torvalds err = func(x, --count, data); 11241da177e4SLinus Torvalds if (err) 11251da177e4SLinus Torvalds goto out; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds } 11281da177e4SLinus Torvalds out: 11291da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11301da177e4SLinus Torvalds return err; 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk); 11331da177e4SLinus Torvalds 1134f8cd5488SJamal Hadi Salim 1135f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event) 1136f8cd5488SJamal Hadi Salim { 1137f8cd5488SJamal Hadi Salim struct km_event c; 1138f8cd5488SJamal Hadi Salim /* we send notify messages in case 1139f8cd5488SJamal Hadi Salim * 1. we updated on of the sequence numbers, and the seqno difference 1140f8cd5488SJamal Hadi Salim * is at least x->replay_maxdiff, in this case we also update the 1141f8cd5488SJamal Hadi Salim * timeout of our timer function 1142f8cd5488SJamal Hadi Salim * 2. if x->replay_maxage has elapsed since last update, 1143f8cd5488SJamal Hadi Salim * and there were changes 1144f8cd5488SJamal Hadi Salim * 1145f8cd5488SJamal Hadi Salim * The state structure must be locked! 1146f8cd5488SJamal Hadi Salim */ 1147f8cd5488SJamal Hadi Salim 1148f8cd5488SJamal Hadi Salim switch (event) { 1149f8cd5488SJamal Hadi Salim case XFRM_REPLAY_UPDATE: 1150f8cd5488SJamal Hadi Salim if (x->replay_maxdiff && 1151f8cd5488SJamal Hadi Salim (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 11522717096aSJamal Hadi Salim (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { 11532717096aSJamal Hadi Salim if (x->xflags & XFRM_TIME_DEFER) 11542717096aSJamal Hadi Salim event = XFRM_REPLAY_TIMEOUT; 11552717096aSJamal Hadi Salim else 1156f8cd5488SJamal Hadi Salim return; 11572717096aSJamal Hadi Salim } 1158f8cd5488SJamal Hadi Salim 1159f8cd5488SJamal Hadi Salim break; 1160f8cd5488SJamal Hadi Salim 1161f8cd5488SJamal Hadi Salim case XFRM_REPLAY_TIMEOUT: 1162f8cd5488SJamal Hadi Salim if ((x->replay.seq == x->preplay.seq) && 1163f8cd5488SJamal Hadi Salim (x->replay.bitmap == x->preplay.bitmap) && 11642717096aSJamal Hadi Salim (x->replay.oseq == x->preplay.oseq)) { 11652717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 1166f8cd5488SJamal Hadi Salim return; 11672717096aSJamal Hadi Salim } 1168f8cd5488SJamal Hadi Salim 1169f8cd5488SJamal Hadi Salim break; 1170f8cd5488SJamal Hadi Salim } 1171f8cd5488SJamal Hadi Salim 1172f8cd5488SJamal Hadi Salim memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 1173f8cd5488SJamal Hadi Salim c.event = XFRM_MSG_NEWAE; 1174f8cd5488SJamal Hadi Salim c.data.aevent = event; 1175f8cd5488SJamal Hadi Salim km_state_notify(x, &c); 1176f8cd5488SJamal Hadi Salim 1177f8cd5488SJamal Hadi Salim if (x->replay_maxage && 1178a47f0ce0SDavid S. Miller !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 11792717096aSJamal Hadi Salim x->xflags &= ~XFRM_TIME_DEFER; 11802717096aSJamal Hadi Salim } 1181a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify); 1182f8cd5488SJamal Hadi Salim 1183f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data) 1184f8cd5488SJamal Hadi Salim { 1185f8cd5488SJamal Hadi Salim struct xfrm_state *x = (struct xfrm_state*)data; 1186f8cd5488SJamal Hadi Salim 1187f8cd5488SJamal Hadi Salim spin_lock(&x->lock); 1188f8cd5488SJamal Hadi Salim 11892717096aSJamal Hadi Salim if (x->km.state == XFRM_STATE_VALID) { 11902717096aSJamal Hadi Salim if (xfrm_aevent_is_on()) 1191f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); 11922717096aSJamal Hadi Salim else 11932717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 11942717096aSJamal Hadi Salim } 1195f8cd5488SJamal Hadi Salim 1196f8cd5488SJamal Hadi Salim spin_unlock(&x->lock); 1197f8cd5488SJamal Hadi Salim } 1198f8cd5488SJamal Hadi Salim 1199a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds u32 diff; 1202a252cc23SAl Viro u32 seq = ntohl(net_seq); 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds if (unlikely(seq == 0)) 12051da177e4SLinus Torvalds return -EINVAL; 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds if (likely(seq > x->replay.seq)) 12081da177e4SLinus Torvalds return 0; 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds diff = x->replay.seq - seq; 12111da177e4SLinus Torvalds if (diff >= x->props.replay_window) { 12121da177e4SLinus Torvalds x->stats.replay_window++; 12131da177e4SLinus Torvalds return -EINVAL; 12141da177e4SLinus Torvalds } 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds if (x->replay.bitmap & (1U << diff)) { 12171da177e4SLinus Torvalds x->stats.replay++; 12181da177e4SLinus Torvalds return -EINVAL; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds return 0; 12211da177e4SLinus Torvalds } 12221da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check); 12231da177e4SLinus Torvalds 122461f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) 12251da177e4SLinus Torvalds { 12261da177e4SLinus Torvalds u32 diff; 122761f4627bSAl Viro u32 seq = ntohl(net_seq); 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds if (seq > x->replay.seq) { 12301da177e4SLinus Torvalds diff = seq - x->replay.seq; 12311da177e4SLinus Torvalds if (diff < x->props.replay_window) 12321da177e4SLinus Torvalds x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 12331da177e4SLinus Torvalds else 12341da177e4SLinus Torvalds x->replay.bitmap = 1; 12351da177e4SLinus Torvalds x->replay.seq = seq; 12361da177e4SLinus Torvalds } else { 12371da177e4SLinus Torvalds diff = x->replay.seq - seq; 12381da177e4SLinus Torvalds x->replay.bitmap |= (1U << diff); 12391da177e4SLinus Torvalds } 1240f8cd5488SJamal Hadi Salim 1241f8cd5488SJamal Hadi Salim if (xfrm_aevent_is_on()) 1242f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance); 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); 12471da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock); 12481da177e4SLinus Torvalds 124926b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 12501da177e4SLinus Torvalds { 12511da177e4SLinus Torvalds struct xfrm_mgr *km; 12521da177e4SLinus Torvalds 125326b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 125426b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 125526b15dadSJamal Hadi Salim if (km->notify_policy) 125626b15dadSJamal Hadi Salim km->notify_policy(xp, dir, c); 125726b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 125826b15dadSJamal Hadi Salim } 125926b15dadSJamal Hadi Salim 126026b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c) 126126b15dadSJamal Hadi Salim { 126226b15dadSJamal Hadi Salim struct xfrm_mgr *km; 126326b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 126426b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 126526b15dadSJamal Hadi Salim if (km->notify) 126626b15dadSJamal Hadi Salim km->notify(x, c); 126726b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 126826b15dadSJamal Hadi Salim } 126926b15dadSJamal Hadi Salim 127026b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify); 127126b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify); 127226b15dadSJamal Hadi Salim 127353bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid) 127426b15dadSJamal Hadi Salim { 127526b15dadSJamal Hadi Salim struct km_event c; 127626b15dadSJamal Hadi Salim 1277bf08867fSHerbert Xu c.data.hard = hard; 127853bc6b4dSJamal Hadi Salim c.pid = pid; 1279f60f6b8fSHerbert Xu c.event = XFRM_MSG_EXPIRE; 128026b15dadSJamal Hadi Salim km_state_notify(x, &c); 12811da177e4SLinus Torvalds 12821da177e4SLinus Torvalds if (hard) 12831da177e4SLinus Torvalds wake_up(&km_waitq); 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds 128653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired); 128726b15dadSJamal Hadi Salim /* 128826b15dadSJamal Hadi Salim * We send to all registered managers regardless of failure 128926b15dadSJamal Hadi Salim * We are happy with one success 129026b15dadSJamal Hadi Salim */ 1291980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 12921da177e4SLinus Torvalds { 129326b15dadSJamal Hadi Salim int err = -EINVAL, acqret; 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) { 129826b15dadSJamal Hadi Salim acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); 129926b15dadSJamal Hadi Salim if (!acqret) 130026b15dadSJamal Hadi Salim err = acqret; 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13031da177e4SLinus Torvalds return err; 13041da177e4SLinus Torvalds } 1305980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query); 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport) 13081da177e4SLinus Torvalds { 13091da177e4SLinus Torvalds int err = -EINVAL; 13101da177e4SLinus Torvalds struct xfrm_mgr *km; 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13131da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 13141da177e4SLinus Torvalds if (km->new_mapping) 13151da177e4SLinus Torvalds err = km->new_mapping(x, ipaddr, sport); 13161da177e4SLinus Torvalds if (!err) 13171da177e4SLinus Torvalds break; 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13201da177e4SLinus Torvalds return err; 13211da177e4SLinus Torvalds } 13221da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping); 13231da177e4SLinus Torvalds 13246c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) 13251da177e4SLinus Torvalds { 132626b15dadSJamal Hadi Salim struct km_event c; 13271da177e4SLinus Torvalds 1328bf08867fSHerbert Xu c.data.hard = hard; 13296c5c8ca7SJamal Hadi Salim c.pid = pid; 1330f60f6b8fSHerbert Xu c.event = XFRM_MSG_POLEXPIRE; 133126b15dadSJamal Hadi Salim km_policy_notify(pol, dir, &c); 13321da177e4SLinus Torvalds 13331da177e4SLinus Torvalds if (hard) 13341da177e4SLinus Torvalds wake_up(&km_waitq); 13351da177e4SLinus Torvalds } 1336a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired); 13371da177e4SLinus Torvalds 133897a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) 133997a64b45SMasahide NAKAMURA { 134097a64b45SMasahide NAKAMURA int err = -EINVAL; 134197a64b45SMasahide NAKAMURA int ret; 134297a64b45SMasahide NAKAMURA struct xfrm_mgr *km; 134397a64b45SMasahide NAKAMURA 134497a64b45SMasahide NAKAMURA read_lock(&xfrm_km_lock); 134597a64b45SMasahide NAKAMURA list_for_each_entry(km, &xfrm_km_list, list) { 134697a64b45SMasahide NAKAMURA if (km->report) { 134797a64b45SMasahide NAKAMURA ret = km->report(proto, sel, addr); 134897a64b45SMasahide NAKAMURA if (!ret) 134997a64b45SMasahide NAKAMURA err = ret; 135097a64b45SMasahide NAKAMURA } 135197a64b45SMasahide NAKAMURA } 135297a64b45SMasahide NAKAMURA read_unlock(&xfrm_km_lock); 135397a64b45SMasahide NAKAMURA return err; 135497a64b45SMasahide NAKAMURA } 135597a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report); 135697a64b45SMasahide NAKAMURA 13571da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 13581da177e4SLinus Torvalds { 13591da177e4SLinus Torvalds int err; 13601da177e4SLinus Torvalds u8 *data; 13611da177e4SLinus Torvalds struct xfrm_mgr *km; 13621da177e4SLinus Torvalds struct xfrm_policy *pol = NULL; 13631da177e4SLinus Torvalds 13641da177e4SLinus Torvalds if (optlen <= 0 || optlen > PAGE_SIZE) 13651da177e4SLinus Torvalds return -EMSGSIZE; 13661da177e4SLinus Torvalds 13671da177e4SLinus Torvalds data = kmalloc(optlen, GFP_KERNEL); 13681da177e4SLinus Torvalds if (!data) 13691da177e4SLinus Torvalds return -ENOMEM; 13701da177e4SLinus Torvalds 13711da177e4SLinus Torvalds err = -EFAULT; 13721da177e4SLinus Torvalds if (copy_from_user(data, optval, optlen)) 13731da177e4SLinus Torvalds goto out; 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds err = -EINVAL; 13761da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13771da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 1378cb969f07SVenkat Yekkirala pol = km->compile_policy(sk, optname, data, 13791da177e4SLinus Torvalds optlen, &err); 13801da177e4SLinus Torvalds if (err >= 0) 13811da177e4SLinus Torvalds break; 13821da177e4SLinus Torvalds } 13831da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13841da177e4SLinus Torvalds 13851da177e4SLinus Torvalds if (err >= 0) { 13861da177e4SLinus Torvalds xfrm_sk_policy_insert(sk, err, pol); 13871da177e4SLinus Torvalds xfrm_pol_put(pol); 13881da177e4SLinus Torvalds err = 0; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds 13911da177e4SLinus Torvalds out: 13921da177e4SLinus Torvalds kfree(data); 13931da177e4SLinus Torvalds return err; 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy); 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km) 13981da177e4SLinus Torvalds { 13991da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 14001da177e4SLinus Torvalds list_add_tail(&km->list, &xfrm_km_list); 14011da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 14021da177e4SLinus Torvalds return 0; 14031da177e4SLinus Torvalds } 14041da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km); 14051da177e4SLinus Torvalds 14061da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km) 14071da177e4SLinus Torvalds { 14081da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 14091da177e4SLinus Torvalds list_del(&km->list); 14101da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 14111da177e4SLinus Torvalds return 0; 14121da177e4SLinus Torvalds } 14131da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km); 14141da177e4SLinus Torvalds 14151da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 14161da177e4SLinus Torvalds { 14171da177e4SLinus Torvalds int err = 0; 14181da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14191da177e4SLinus Torvalds return -EINVAL; 14201da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14211da177e4SLinus Torvalds return -EAFNOSUPPORT; 1422f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14231da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 14241da177e4SLinus Torvalds err = -ENOBUFS; 1425edcd5821SDavid S. Miller else 14261da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = afinfo; 1427f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14281da177e4SLinus Torvalds return err; 14291da177e4SLinus Torvalds } 14301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo); 14311da177e4SLinus Torvalds 14321da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 14331da177e4SLinus Torvalds { 14341da177e4SLinus Torvalds int err = 0; 14351da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14361da177e4SLinus Torvalds return -EINVAL; 14371da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14381da177e4SLinus Torvalds return -EAFNOSUPPORT; 1439f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14401da177e4SLinus Torvalds if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 14411da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) 14421da177e4SLinus Torvalds err = -EINVAL; 1443edcd5821SDavid S. Miller else 14441da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = NULL; 14451da177e4SLinus Torvalds } 1446f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14471da177e4SLinus Torvalds return err; 14481da177e4SLinus Torvalds } 14491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo); 14501da177e4SLinus Torvalds 14511da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) 14521da177e4SLinus Torvalds { 14531da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 14541da177e4SLinus Torvalds if (unlikely(family >= NPROTO)) 14551da177e4SLinus Torvalds return NULL; 14561da177e4SLinus Torvalds read_lock(&xfrm_state_afinfo_lock); 14571da177e4SLinus Torvalds afinfo = xfrm_state_afinfo[family]; 1458546be240SHerbert Xu if (unlikely(!afinfo)) 14591da177e4SLinus Torvalds read_unlock(&xfrm_state_afinfo_lock); 14601da177e4SLinus Torvalds return afinfo; 14611da177e4SLinus Torvalds } 14621da177e4SLinus Torvalds 14631da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) 14641da177e4SLinus Torvalds { 1465546be240SHerbert Xu read_unlock(&xfrm_state_afinfo_lock); 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 14691da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x) 14701da177e4SLinus Torvalds { 14711da177e4SLinus Torvalds if (x->tunnel) { 14721da177e4SLinus Torvalds struct xfrm_state *t = x->tunnel; 14731da177e4SLinus Torvalds 14741da177e4SLinus Torvalds if (atomic_read(&t->tunnel_users) == 2) 14751da177e4SLinus Torvalds xfrm_state_delete(t); 14761da177e4SLinus Torvalds atomic_dec(&t->tunnel_users); 14771da177e4SLinus Torvalds xfrm_state_put(t); 14781da177e4SLinus Torvalds x->tunnel = NULL; 14791da177e4SLinus Torvalds } 14801da177e4SLinus Torvalds } 14811da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel); 14821da177e4SLinus Torvalds 148380b30c10SHerbert Xu /* 148480b30c10SHerbert Xu * This function is NOT optimal. For example, with ESP it will give an 148580b30c10SHerbert Xu * MTU that's usually two bytes short of being optimal. However, it will 148680b30c10SHerbert Xu * usually give an answer that's a multiple of 4 provided the input is 148780b30c10SHerbert Xu * also a multiple of 4. 148880b30c10SHerbert Xu */ 14891da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu) 14901da177e4SLinus Torvalds { 14911da177e4SLinus Torvalds int res = mtu; 14921da177e4SLinus Torvalds 14931da177e4SLinus Torvalds res -= x->props.header_len; 14941da177e4SLinus Torvalds 14951da177e4SLinus Torvalds for (;;) { 14961da177e4SLinus Torvalds int m = res; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds if (m < 68) 14991da177e4SLinus Torvalds return 68; 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds spin_lock_bh(&x->lock); 15021da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID && 15031da177e4SLinus Torvalds x->type && x->type->get_max_size) 15041da177e4SLinus Torvalds m = x->type->get_max_size(x, m); 15051da177e4SLinus Torvalds else 15061da177e4SLinus Torvalds m += x->props.header_len; 15071da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 15081da177e4SLinus Torvalds 15091da177e4SLinus Torvalds if (m <= mtu) 15101da177e4SLinus Torvalds break; 15111da177e4SLinus Torvalds res -= (m - mtu); 15121da177e4SLinus Torvalds } 15131da177e4SLinus Torvalds 15141da177e4SLinus Torvalds return res; 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds 151772cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x) 151872cb6962SHerbert Xu { 1519d094cd83SHerbert Xu struct xfrm_state_afinfo *afinfo; 1520d094cd83SHerbert Xu int family = x->props.family; 152172cb6962SHerbert Xu int err; 152272cb6962SHerbert Xu 1523d094cd83SHerbert Xu err = -EAFNOSUPPORT; 1524d094cd83SHerbert Xu afinfo = xfrm_state_get_afinfo(family); 1525d094cd83SHerbert Xu if (!afinfo) 1526d094cd83SHerbert Xu goto error; 1527d094cd83SHerbert Xu 1528d094cd83SHerbert Xu err = 0; 1529d094cd83SHerbert Xu if (afinfo->init_flags) 1530d094cd83SHerbert Xu err = afinfo->init_flags(x); 1531d094cd83SHerbert Xu 1532d094cd83SHerbert Xu xfrm_state_put_afinfo(afinfo); 1533d094cd83SHerbert Xu 1534d094cd83SHerbert Xu if (err) 1535d094cd83SHerbert Xu goto error; 1536d094cd83SHerbert Xu 1537d094cd83SHerbert Xu err = -EPROTONOSUPPORT; 1538d094cd83SHerbert Xu x->type = xfrm_get_type(x->id.proto, family); 153972cb6962SHerbert Xu if (x->type == NULL) 154072cb6962SHerbert Xu goto error; 154172cb6962SHerbert Xu 154272cb6962SHerbert Xu err = x->type->init_state(x); 154372cb6962SHerbert Xu if (err) 154472cb6962SHerbert Xu goto error; 154572cb6962SHerbert Xu 1546b59f45d0SHerbert Xu x->mode = xfrm_get_mode(x->props.mode, family); 1547b59f45d0SHerbert Xu if (x->mode == NULL) 1548b59f45d0SHerbert Xu goto error; 1549b59f45d0SHerbert Xu 155072cb6962SHerbert Xu x->km.state = XFRM_STATE_VALID; 155172cb6962SHerbert Xu 155272cb6962SHerbert Xu error: 155372cb6962SHerbert Xu return err; 155472cb6962SHerbert Xu } 155572cb6962SHerbert Xu 155672cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state); 15571da177e4SLinus Torvalds 15581da177e4SLinus Torvalds void __init xfrm_state_init(void) 15591da177e4SLinus Torvalds { 1560f034b5d4SDavid S. Miller unsigned int sz; 15611da177e4SLinus Torvalds 1562f034b5d4SDavid S. Miller sz = sizeof(struct hlist_head) * 8; 1563f034b5d4SDavid S. Miller 156444e36b42SDavid S. Miller xfrm_state_bydst = xfrm_hash_alloc(sz); 156544e36b42SDavid S. Miller xfrm_state_bysrc = xfrm_hash_alloc(sz); 156644e36b42SDavid S. Miller xfrm_state_byspi = xfrm_hash_alloc(sz); 1567f034b5d4SDavid S. Miller if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi) 1568f034b5d4SDavid S. Miller panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes."); 1569f034b5d4SDavid S. Miller xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1); 1570f034b5d4SDavid S. Miller 1571c4028958SDavid Howells INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task); 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds 1574