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> 23161a09e7SJoy Latten #include <linux/audit.h> 241da177e4SLinus Torvalds 2544e36b42SDavid S. Miller #include "xfrm_hash.h" 2644e36b42SDavid S. Miller 27ee857a7dSDavid S. Miller struct sock *xfrm_nl; 28ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl); 29ee857a7dSDavid S. Miller 30f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME; 31a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime); 32a70fcb0bSDavid S. Miller 33f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE; 34a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth); 35a70fcb0bSDavid S. Miller 361da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables: 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl) 39a624c108SDavid S. Miller 2. Hash table by (daddr,family,reqid) to find what SAs exist for given 401da177e4SLinus Torvalds destination/tunnel endpoint. (output) 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock); 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint 461da177e4SLinus Torvalds * of tunnel or destination of transport mode) allowed by selector. 471da177e4SLinus Torvalds * 481da177e4SLinus Torvalds * Main use is finding SA after policy selected tunnel or transport mode. 491da177e4SLinus Torvalds * Also, it can be used by ah/esp icmp error handler to find offending SA. 501da177e4SLinus Torvalds */ 51f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bydst __read_mostly; 52f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_bysrc __read_mostly; 53f034b5d4SDavid S. Miller static struct hlist_head *xfrm_state_byspi __read_mostly; 54f034b5d4SDavid S. Miller static unsigned int xfrm_state_hmask __read_mostly; 55f034b5d4SDavid S. Miller static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; 56f034b5d4SDavid S. Miller static unsigned int xfrm_state_num; 579d4a706dSDavid S. Miller static unsigned int xfrm_state_genid; 581da177e4SLinus Torvalds 59c1969f29SDavid S. Miller static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, 60c1969f29SDavid S. Miller xfrm_address_t *saddr, 61c1969f29SDavid S. Miller u32 reqid, 62a624c108SDavid S. Miller unsigned short family) 63a624c108SDavid S. Miller { 64c1969f29SDavid S. Miller return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask); 65a624c108SDavid S. Miller } 66a624c108SDavid S. Miller 67667bbcb6SMasahide NAKAMURA static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr, 68667bbcb6SMasahide NAKAMURA xfrm_address_t *saddr, 6944e36b42SDavid S. Miller unsigned short family) 70f034b5d4SDavid S. Miller { 71667bbcb6SMasahide NAKAMURA return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask); 72f034b5d4SDavid S. Miller } 73f034b5d4SDavid S. Miller 742575b654SDavid S. Miller static inline unsigned int 758122adf0SAl Viro xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 76f034b5d4SDavid S. Miller { 77c1969f29SDavid S. Miller return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask); 78f034b5d4SDavid S. Miller } 79f034b5d4SDavid S. Miller 80f034b5d4SDavid S. Miller static void xfrm_hash_transfer(struct hlist_head *list, 81f034b5d4SDavid S. Miller struct hlist_head *ndsttable, 82f034b5d4SDavid S. Miller struct hlist_head *nsrctable, 83f034b5d4SDavid S. Miller struct hlist_head *nspitable, 84f034b5d4SDavid S. Miller unsigned int nhashmask) 85f034b5d4SDavid S. Miller { 86f034b5d4SDavid S. Miller struct hlist_node *entry, *tmp; 87f034b5d4SDavid S. Miller struct xfrm_state *x; 88f034b5d4SDavid S. Miller 89f034b5d4SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, list, bydst) { 90f034b5d4SDavid S. Miller unsigned int h; 91f034b5d4SDavid S. Miller 92c1969f29SDavid S. Miller h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 93c1969f29SDavid S. Miller x->props.reqid, x->props.family, 94c1969f29SDavid S. Miller nhashmask); 95f034b5d4SDavid S. Miller hlist_add_head(&x->bydst, ndsttable+h); 96f034b5d4SDavid S. Miller 97667bbcb6SMasahide NAKAMURA h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, 98667bbcb6SMasahide NAKAMURA x->props.family, 99f034b5d4SDavid S. Miller nhashmask); 100f034b5d4SDavid S. Miller hlist_add_head(&x->bysrc, nsrctable+h); 101f034b5d4SDavid S. Miller 1027b4dc360SMasahide NAKAMURA if (x->id.spi) { 1037b4dc360SMasahide NAKAMURA h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, 1047b4dc360SMasahide NAKAMURA x->id.proto, x->props.family, 1057b4dc360SMasahide NAKAMURA nhashmask); 106f034b5d4SDavid S. Miller hlist_add_head(&x->byspi, nspitable+h); 107f034b5d4SDavid S. Miller } 108f034b5d4SDavid S. Miller } 1097b4dc360SMasahide NAKAMURA } 110f034b5d4SDavid S. Miller 111f034b5d4SDavid S. Miller static unsigned long xfrm_hash_new_size(void) 112f034b5d4SDavid S. Miller { 113f034b5d4SDavid S. Miller return ((xfrm_state_hmask + 1) << 1) * 114f034b5d4SDavid S. Miller sizeof(struct hlist_head); 115f034b5d4SDavid S. Miller } 116f034b5d4SDavid S. Miller 117f034b5d4SDavid S. Miller static DEFINE_MUTEX(hash_resize_mutex); 118f034b5d4SDavid S. Miller 119c4028958SDavid Howells static void xfrm_hash_resize(struct work_struct *__unused) 120f034b5d4SDavid S. Miller { 121f034b5d4SDavid S. Miller struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; 122f034b5d4SDavid S. Miller unsigned long nsize, osize; 123f034b5d4SDavid S. Miller unsigned int nhashmask, ohashmask; 124f034b5d4SDavid S. Miller int i; 125f034b5d4SDavid S. Miller 126f034b5d4SDavid S. Miller mutex_lock(&hash_resize_mutex); 127f034b5d4SDavid S. Miller 128f034b5d4SDavid S. Miller nsize = xfrm_hash_new_size(); 12944e36b42SDavid S. Miller ndst = xfrm_hash_alloc(nsize); 130f034b5d4SDavid S. Miller if (!ndst) 131f034b5d4SDavid S. Miller goto out_unlock; 13244e36b42SDavid S. Miller nsrc = xfrm_hash_alloc(nsize); 133f034b5d4SDavid S. Miller if (!nsrc) { 13444e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 135f034b5d4SDavid S. Miller goto out_unlock; 136f034b5d4SDavid S. Miller } 13744e36b42SDavid S. Miller nspi = xfrm_hash_alloc(nsize); 138f034b5d4SDavid S. Miller if (!nspi) { 13944e36b42SDavid S. Miller xfrm_hash_free(ndst, nsize); 14044e36b42SDavid S. Miller xfrm_hash_free(nsrc, nsize); 141f034b5d4SDavid S. Miller goto out_unlock; 142f034b5d4SDavid S. Miller } 143f034b5d4SDavid S. Miller 144f034b5d4SDavid S. Miller spin_lock_bh(&xfrm_state_lock); 145f034b5d4SDavid S. Miller 146f034b5d4SDavid S. Miller nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; 147f034b5d4SDavid S. Miller for (i = xfrm_state_hmask; i >= 0; i--) 148f034b5d4SDavid S. Miller xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi, 149f034b5d4SDavid S. Miller nhashmask); 150f034b5d4SDavid S. Miller 151f034b5d4SDavid S. Miller odst = xfrm_state_bydst; 152f034b5d4SDavid S. Miller osrc = xfrm_state_bysrc; 153f034b5d4SDavid S. Miller ospi = xfrm_state_byspi; 154f034b5d4SDavid S. Miller ohashmask = xfrm_state_hmask; 155f034b5d4SDavid S. Miller 156f034b5d4SDavid S. Miller xfrm_state_bydst = ndst; 157f034b5d4SDavid S. Miller xfrm_state_bysrc = nsrc; 158f034b5d4SDavid S. Miller xfrm_state_byspi = nspi; 159f034b5d4SDavid S. Miller xfrm_state_hmask = nhashmask; 160f034b5d4SDavid S. Miller 161f034b5d4SDavid S. Miller spin_unlock_bh(&xfrm_state_lock); 162f034b5d4SDavid S. Miller 163f034b5d4SDavid S. Miller osize = (ohashmask + 1) * sizeof(struct hlist_head); 16444e36b42SDavid S. Miller xfrm_hash_free(odst, osize); 16544e36b42SDavid S. Miller xfrm_hash_free(osrc, osize); 16644e36b42SDavid S. Miller xfrm_hash_free(ospi, osize); 167f034b5d4SDavid S. Miller 168f034b5d4SDavid S. Miller out_unlock: 169f034b5d4SDavid S. Miller mutex_unlock(&hash_resize_mutex); 170f034b5d4SDavid S. Miller } 171f034b5d4SDavid S. Miller 172c4028958SDavid Howells static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize); 173f034b5d4SDavid S. Miller 1741da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq); 1751da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq); 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock); 1781da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work; 1818f126e37SDavid S. Miller static HLIST_HEAD(xfrm_state_gc_list); 1821da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock); 1831da177e4SLinus Torvalds 18453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x); 1851da177e4SLinus Torvalds 186980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 18753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x) 1901da177e4SLinus Torvalds { 191a47f0ce0SDavid S. Miller del_timer_sync(&x->timer); 192a47f0ce0SDavid S. Miller del_timer_sync(&x->rtimer); 1931da177e4SLinus Torvalds kfree(x->aalg); 1941da177e4SLinus Torvalds kfree(x->ealg); 1951da177e4SLinus Torvalds kfree(x->calg); 1961da177e4SLinus Torvalds kfree(x->encap); 197060f02a3SNoriaki TAKAMIYA kfree(x->coaddr); 198b59f45d0SHerbert Xu if (x->mode) 199b59f45d0SHerbert Xu xfrm_put_mode(x->mode); 2001da177e4SLinus Torvalds if (x->type) { 2011da177e4SLinus Torvalds x->type->destructor(x); 2021da177e4SLinus Torvalds xfrm_put_type(x->type); 2031da177e4SLinus Torvalds } 204df71837dSTrent Jaeger security_xfrm_state_free(x); 2051da177e4SLinus Torvalds kfree(x); 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 208c4028958SDavid Howells static void xfrm_state_gc_task(struct work_struct *data) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds struct xfrm_state *x; 2118f126e37SDavid S. Miller struct hlist_node *entry, *tmp; 2128f126e37SDavid S. Miller struct hlist_head gc_list; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 2158f126e37SDavid S. Miller gc_list.first = xfrm_state_gc_list.first; 2168f126e37SDavid S. Miller INIT_HLIST_HEAD(&xfrm_state_gc_list); 2171da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 2181da177e4SLinus Torvalds 2198f126e37SDavid S. Miller hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst) 2201da177e4SLinus Torvalds xfrm_state_gc_destroy(x); 2218f126e37SDavid S. Miller 2221da177e4SLinus Torvalds wake_up(&km_waitq); 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs) 2261da177e4SLinus Torvalds { 2271da177e4SLinus Torvalds if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 2281da177e4SLinus Torvalds return MAX_SCHEDULE_TIMEOUT-1; 2291da177e4SLinus Torvalds else 2301da177e4SLinus Torvalds return secs*HZ; 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds struct xfrm_state *x = (struct xfrm_state*)data; 2361da177e4SLinus Torvalds unsigned long now = (unsigned long)xtime.tv_sec; 2371da177e4SLinus Torvalds long next = LONG_MAX; 2381da177e4SLinus Torvalds int warn = 0; 239161a09e7SJoy Latten int err = 0; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds spin_lock(&x->lock); 2421da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_DEAD) 2431da177e4SLinus Torvalds goto out; 2441da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_EXPIRED) 2451da177e4SLinus Torvalds goto expired; 2461da177e4SLinus Torvalds if (x->lft.hard_add_expires_seconds) { 2471da177e4SLinus Torvalds long tmo = x->lft.hard_add_expires_seconds + 2481da177e4SLinus Torvalds x->curlft.add_time - now; 2491da177e4SLinus Torvalds if (tmo <= 0) 2501da177e4SLinus Torvalds goto expired; 2511da177e4SLinus Torvalds if (tmo < next) 2521da177e4SLinus Torvalds next = tmo; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds if (x->lft.hard_use_expires_seconds) { 2551da177e4SLinus Torvalds long tmo = x->lft.hard_use_expires_seconds + 2561da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2571da177e4SLinus Torvalds if (tmo <= 0) 2581da177e4SLinus Torvalds goto expired; 2591da177e4SLinus Torvalds if (tmo < next) 2601da177e4SLinus Torvalds next = tmo; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds if (x->km.dying) 2631da177e4SLinus Torvalds goto resched; 2641da177e4SLinus Torvalds if (x->lft.soft_add_expires_seconds) { 2651da177e4SLinus Torvalds long tmo = x->lft.soft_add_expires_seconds + 2661da177e4SLinus Torvalds x->curlft.add_time - now; 2671da177e4SLinus Torvalds if (tmo <= 0) 2681da177e4SLinus Torvalds warn = 1; 2691da177e4SLinus Torvalds else if (tmo < next) 2701da177e4SLinus Torvalds next = tmo; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds if (x->lft.soft_use_expires_seconds) { 2731da177e4SLinus Torvalds long tmo = x->lft.soft_use_expires_seconds + 2741da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 2751da177e4SLinus Torvalds if (tmo <= 0) 2761da177e4SLinus Torvalds warn = 1; 2771da177e4SLinus Torvalds else if (tmo < next) 2781da177e4SLinus Torvalds next = tmo; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2814666faabSHerbert Xu x->km.dying = warn; 2821da177e4SLinus Torvalds if (warn) 28353bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 2841da177e4SLinus Torvalds resched: 285a47f0ce0SDavid S. Miller if (next != LONG_MAX) 286a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + make_jiffies(next)); 287a47f0ce0SDavid S. Miller 2881da177e4SLinus Torvalds goto out; 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds expired: 2911da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { 2921da177e4SLinus Torvalds x->km.state = XFRM_STATE_EXPIRED; 2931da177e4SLinus Torvalds wake_up(&km_waitq); 2941da177e4SLinus Torvalds next = 2; 2951da177e4SLinus Torvalds goto resched; 2961da177e4SLinus Torvalds } 297161a09e7SJoy Latten 298161a09e7SJoy Latten err = __xfrm_state_delete(x); 299161a09e7SJoy Latten if (!err && x->id.spi) 30053bc6b4dSJamal Hadi Salim km_state_expired(x, 1, 0); 3011da177e4SLinus Torvalds 302161a09e7SJoy Latten xfrm_audit_log(audit_get_loginuid(current->audit_context), 0, 303161a09e7SJoy Latten AUDIT_MAC_IPSEC_DELSA, err ? 0 : 1, NULL, x); 304161a09e7SJoy Latten 3051da177e4SLinus Torvalds out: 3061da177e4SLinus Torvalds spin_unlock(&x->lock); 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds 3090ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data); 3100ac84752SDavid S. Miller 3111da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void) 3121da177e4SLinus Torvalds { 3131da177e4SLinus Torvalds struct xfrm_state *x; 3141da177e4SLinus Torvalds 3150da974f4SPanagiotis Issaris x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds if (x) { 3181da177e4SLinus Torvalds atomic_set(&x->refcnt, 1); 3191da177e4SLinus Torvalds atomic_set(&x->tunnel_users, 0); 3208f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bydst); 3218f126e37SDavid S. Miller INIT_HLIST_NODE(&x->bysrc); 3228f126e37SDavid S. Miller INIT_HLIST_NODE(&x->byspi); 3231da177e4SLinus Torvalds init_timer(&x->timer); 3241da177e4SLinus Torvalds x->timer.function = xfrm_timer_handler; 3251da177e4SLinus Torvalds x->timer.data = (unsigned long)x; 326f8cd5488SJamal Hadi Salim init_timer(&x->rtimer); 327f8cd5488SJamal Hadi Salim x->rtimer.function = xfrm_replay_timer_handler; 328f8cd5488SJamal Hadi Salim x->rtimer.data = (unsigned long)x; 3291da177e4SLinus Torvalds x->curlft.add_time = (unsigned long)xtime.tv_sec; 3301da177e4SLinus Torvalds x->lft.soft_byte_limit = XFRM_INF; 3311da177e4SLinus Torvalds x->lft.soft_packet_limit = XFRM_INF; 3321da177e4SLinus Torvalds x->lft.hard_byte_limit = XFRM_INF; 3331da177e4SLinus Torvalds x->lft.hard_packet_limit = XFRM_INF; 334f8cd5488SJamal Hadi Salim x->replay_maxage = 0; 335f8cd5488SJamal Hadi Salim x->replay_maxdiff = 0; 3361da177e4SLinus Torvalds spin_lock_init(&x->lock); 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds return x; 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x) 3431da177e4SLinus Torvalds { 3441da177e4SLinus Torvalds BUG_TRAP(x->km.state == XFRM_STATE_DEAD); 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 3478f126e37SDavid S. Miller hlist_add_head(&x->bydst, &xfrm_state_gc_list); 3481da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 3491da177e4SLinus Torvalds schedule_work(&xfrm_state_gc_work); 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy); 3521da177e4SLinus Torvalds 35353bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x) 3541da177e4SLinus Torvalds { 35526b15dadSJamal Hadi Salim int err = -ESRCH; 35626b15dadSJamal Hadi Salim 3571da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_DEAD) { 3581da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 3591da177e4SLinus Torvalds spin_lock(&xfrm_state_lock); 3608f126e37SDavid S. Miller hlist_del(&x->bydst); 3618f126e37SDavid S. Miller hlist_del(&x->bysrc); 362a47f0ce0SDavid S. Miller if (x->id.spi) 3638f126e37SDavid S. Miller hlist_del(&x->byspi); 364f034b5d4SDavid S. Miller xfrm_state_num--; 3651da177e4SLinus Torvalds spin_unlock(&xfrm_state_lock); 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds /* All xfrm_state objects are created by xfrm_state_alloc. 3681da177e4SLinus Torvalds * The xfrm_state_alloc call gives a reference, and that 3691da177e4SLinus Torvalds * is what we are dropping here. 3701da177e4SLinus Torvalds */ 37121380b81SHerbert Xu __xfrm_state_put(x); 37226b15dadSJamal Hadi Salim err = 0; 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds 37526b15dadSJamal Hadi Salim return err; 37626b15dadSJamal Hadi Salim } 37753bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete); 37826b15dadSJamal Hadi Salim 37926b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x) 3801da177e4SLinus Torvalds { 38126b15dadSJamal Hadi Salim int err; 38226b15dadSJamal Hadi Salim 3831da177e4SLinus Torvalds spin_lock_bh(&x->lock); 38426b15dadSJamal Hadi Salim err = __xfrm_state_delete(x); 3851da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 38626b15dadSJamal Hadi Salim 38726b15dadSJamal Hadi Salim return err; 3881da177e4SLinus Torvalds } 3891da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete); 3901da177e4SLinus Torvalds 391161a09e7SJoy Latten void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info) 3921da177e4SLinus Torvalds { 3931da177e4SLinus Torvalds int i; 394161a09e7SJoy Latten int err = 0; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 397a9917c06SMasahide NAKAMURA for (i = 0; i <= xfrm_state_hmask; i++) { 3988f126e37SDavid S. Miller struct hlist_node *entry; 3998f126e37SDavid S. Miller struct xfrm_state *x; 4001da177e4SLinus Torvalds restart: 4018f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 4021da177e4SLinus Torvalds if (!xfrm_state_kern(x) && 4035794708fSMasahide NAKAMURA xfrm_id_proto_match(x->id.proto, proto)) { 4041da177e4SLinus Torvalds xfrm_state_hold(x); 4051da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4061da177e4SLinus Torvalds 407161a09e7SJoy Latten err = xfrm_state_delete(x); 408161a09e7SJoy Latten xfrm_audit_log(audit_info->loginuid, 409161a09e7SJoy Latten audit_info->secid, 410161a09e7SJoy Latten AUDIT_MAC_IPSEC_DELSA, 411161a09e7SJoy Latten err ? 0 : 1, NULL, x); 4121da177e4SLinus Torvalds xfrm_state_put(x); 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 4151da177e4SLinus Torvalds goto restart; 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4201da177e4SLinus Torvalds wake_up(&km_waitq); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush); 4231da177e4SLinus Torvalds 4241da177e4SLinus Torvalds static int 4251da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, 4261da177e4SLinus Torvalds struct xfrm_tmpl *tmpl, 4271da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 4281da177e4SLinus Torvalds unsigned short family) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 4311da177e4SLinus Torvalds if (!afinfo) 4321da177e4SLinus Torvalds return -1; 4331da177e4SLinus Torvalds afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); 4341da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 4351da177e4SLinus Torvalds return 0; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 438a94cfd19SAl Viro static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) 439edcd5821SDavid S. Miller { 440edcd5821SDavid S. Miller unsigned int h = xfrm_spi_hash(daddr, spi, proto, family); 441edcd5821SDavid S. Miller struct xfrm_state *x; 4428f126e37SDavid S. Miller struct hlist_node *entry; 443edcd5821SDavid S. Miller 4448f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) { 445edcd5821SDavid S. Miller if (x->props.family != family || 446edcd5821SDavid S. Miller x->id.spi != spi || 447edcd5821SDavid S. Miller x->id.proto != proto) 448edcd5821SDavid S. Miller continue; 449edcd5821SDavid S. Miller 450edcd5821SDavid S. Miller switch (family) { 451edcd5821SDavid S. Miller case AF_INET: 452edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4) 453edcd5821SDavid S. Miller continue; 454edcd5821SDavid S. Miller break; 455edcd5821SDavid S. Miller case AF_INET6: 456edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 457edcd5821SDavid S. Miller (struct in6_addr *) 458edcd5821SDavid S. Miller x->id.daddr.a6)) 459edcd5821SDavid S. Miller continue; 460edcd5821SDavid S. Miller break; 461edcd5821SDavid S. Miller }; 462edcd5821SDavid S. Miller 463edcd5821SDavid S. Miller xfrm_state_hold(x); 464edcd5821SDavid S. Miller return x; 465edcd5821SDavid S. Miller } 466edcd5821SDavid S. Miller 467edcd5821SDavid S. Miller return NULL; 468edcd5821SDavid S. Miller } 469edcd5821SDavid S. Miller 470edcd5821SDavid S. Miller static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family) 471edcd5821SDavid S. Miller { 472667bbcb6SMasahide NAKAMURA unsigned int h = xfrm_src_hash(daddr, saddr, family); 473edcd5821SDavid S. Miller struct xfrm_state *x; 4748f126e37SDavid S. Miller struct hlist_node *entry; 475edcd5821SDavid S. Miller 4768f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) { 477edcd5821SDavid S. Miller if (x->props.family != family || 478edcd5821SDavid S. Miller x->id.proto != proto) 479edcd5821SDavid S. Miller continue; 480edcd5821SDavid S. Miller 481edcd5821SDavid S. Miller switch (family) { 482edcd5821SDavid S. Miller case AF_INET: 483edcd5821SDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 484edcd5821SDavid S. Miller x->props.saddr.a4 != saddr->a4) 485edcd5821SDavid S. Miller continue; 486edcd5821SDavid S. Miller break; 487edcd5821SDavid S. Miller case AF_INET6: 488edcd5821SDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)daddr, 489edcd5821SDavid S. Miller (struct in6_addr *) 490edcd5821SDavid S. Miller x->id.daddr.a6) || 491edcd5821SDavid S. Miller !ipv6_addr_equal((struct in6_addr *)saddr, 492edcd5821SDavid S. Miller (struct in6_addr *) 493edcd5821SDavid S. Miller x->props.saddr.a6)) 494edcd5821SDavid S. Miller continue; 495edcd5821SDavid S. Miller break; 496edcd5821SDavid S. Miller }; 497edcd5821SDavid S. Miller 498edcd5821SDavid S. Miller xfrm_state_hold(x); 499edcd5821SDavid S. Miller return x; 500edcd5821SDavid S. Miller } 501edcd5821SDavid S. Miller 502edcd5821SDavid S. Miller return NULL; 503edcd5821SDavid S. Miller } 504edcd5821SDavid S. Miller 505edcd5821SDavid S. Miller static inline struct xfrm_state * 506edcd5821SDavid S. Miller __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family) 507edcd5821SDavid S. Miller { 508edcd5821SDavid S. Miller if (use_spi) 509edcd5821SDavid S. Miller return __xfrm_state_lookup(&x->id.daddr, x->id.spi, 510edcd5821SDavid S. Miller x->id.proto, family); 511edcd5821SDavid S. Miller else 512edcd5821SDavid S. Miller return __xfrm_state_lookup_byaddr(&x->id.daddr, 513edcd5821SDavid S. Miller &x->props.saddr, 514edcd5821SDavid S. Miller x->id.proto, family); 515edcd5821SDavid S. Miller } 516edcd5821SDavid S. Miller 5172fab22f2SPatrick McHardy static void xfrm_hash_grow_check(int have_hash_collision) 5182fab22f2SPatrick McHardy { 5192fab22f2SPatrick McHardy if (have_hash_collision && 5202fab22f2SPatrick McHardy (xfrm_state_hmask + 1) < xfrm_state_hashmax && 5212fab22f2SPatrick McHardy xfrm_state_num > xfrm_state_hmask) 5222fab22f2SPatrick McHardy schedule_work(&xfrm_hash_work); 5232fab22f2SPatrick McHardy } 5242fab22f2SPatrick McHardy 5251da177e4SLinus Torvalds struct xfrm_state * 5261da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 5271da177e4SLinus Torvalds struct flowi *fl, struct xfrm_tmpl *tmpl, 5281da177e4SLinus Torvalds struct xfrm_policy *pol, int *err, 5291da177e4SLinus Torvalds unsigned short family) 5301da177e4SLinus Torvalds { 531c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); 5328f126e37SDavid S. Miller struct hlist_node *entry; 5331da177e4SLinus Torvalds struct xfrm_state *x, *x0; 5341da177e4SLinus Torvalds int acquire_in_progress = 0; 5351da177e4SLinus Torvalds int error = 0; 5361da177e4SLinus Torvalds struct xfrm_state *best = NULL; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 5398f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 5401da177e4SLinus Torvalds if (x->props.family == family && 5411da177e4SLinus Torvalds x->props.reqid == tmpl->reqid && 542fbd9a5b4SMasahide NAKAMURA !(x->props.flags & XFRM_STATE_WILDRECV) && 5431da177e4SLinus Torvalds xfrm_state_addr_check(x, daddr, saddr, family) && 5441da177e4SLinus Torvalds tmpl->mode == x->props.mode && 5451da177e4SLinus Torvalds tmpl->id.proto == x->id.proto && 5461da177e4SLinus Torvalds (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { 5471da177e4SLinus Torvalds /* Resolution logic: 5481da177e4SLinus Torvalds 1. There is a valid state with matching selector. 5491da177e4SLinus Torvalds Done. 5501da177e4SLinus Torvalds 2. Valid state with inappropriate selector. Skip. 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds Entering area of "sysdeps". 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds 3. If state is not valid, selector is temporary, 5551da177e4SLinus Torvalds it selects only session which triggered 5561da177e4SLinus Torvalds previous resolution. Key manager will do 5571da177e4SLinus Torvalds something to install a state with proper 5581da177e4SLinus Torvalds selector. 5591da177e4SLinus Torvalds */ 5601da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID) { 561df71837dSTrent Jaeger if (!xfrm_selector_match(&x->sel, fl, family) || 562e0d1caa7SVenkat Yekkirala !security_xfrm_state_pol_flow_match(x, pol, fl)) 5631da177e4SLinus Torvalds continue; 5641da177e4SLinus Torvalds if (!best || 5651da177e4SLinus Torvalds best->km.dying > x->km.dying || 5661da177e4SLinus Torvalds (best->km.dying == x->km.dying && 5671da177e4SLinus Torvalds best->curlft.add_time < x->curlft.add_time)) 5681da177e4SLinus Torvalds best = x; 5691da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ACQ) { 5701da177e4SLinus Torvalds acquire_in_progress = 1; 5711da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ERROR || 5721da177e4SLinus Torvalds x->km.state == XFRM_STATE_EXPIRED) { 573df71837dSTrent Jaeger if (xfrm_selector_match(&x->sel, fl, family) && 574e0d1caa7SVenkat Yekkirala security_xfrm_state_pol_flow_match(x, pol, fl)) 5751da177e4SLinus Torvalds error = -ESRCH; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds x = best; 5811da177e4SLinus Torvalds if (!x && !error && !acquire_in_progress) { 5825c5d281aSPatrick McHardy if (tmpl->id.spi && 583edcd5821SDavid S. Miller (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi, 584edcd5821SDavid S. Miller tmpl->id.proto, family)) != NULL) { 5851da177e4SLinus Torvalds xfrm_state_put(x0); 5861da177e4SLinus Torvalds error = -EEXIST; 5871da177e4SLinus Torvalds goto out; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds x = xfrm_state_alloc(); 5901da177e4SLinus Torvalds if (x == NULL) { 5911da177e4SLinus Torvalds error = -ENOMEM; 5921da177e4SLinus Torvalds goto out; 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds /* Initialize temporary selector matching only 5951da177e4SLinus Torvalds * to current session. */ 5961da177e4SLinus Torvalds xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); 5971da177e4SLinus Torvalds 598e0d1caa7SVenkat Yekkirala error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); 599e0d1caa7SVenkat Yekkirala if (error) { 600e0d1caa7SVenkat Yekkirala x->km.state = XFRM_STATE_DEAD; 601e0d1caa7SVenkat Yekkirala xfrm_state_put(x); 602e0d1caa7SVenkat Yekkirala x = NULL; 603e0d1caa7SVenkat Yekkirala goto out; 604e0d1caa7SVenkat Yekkirala } 605e0d1caa7SVenkat Yekkirala 6061da177e4SLinus Torvalds if (km_query(x, tmpl, pol) == 0) { 6071da177e4SLinus Torvalds x->km.state = XFRM_STATE_ACQ; 6088f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 609667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(daddr, saddr, family); 6108f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 6111da177e4SLinus Torvalds if (x->id.spi) { 6121da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); 6138f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 6161da177e4SLinus Torvalds x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 6171da177e4SLinus Torvalds add_timer(&x->timer); 6182fab22f2SPatrick McHardy xfrm_state_num++; 6192fab22f2SPatrick McHardy xfrm_hash_grow_check(x->bydst.next != NULL); 6201da177e4SLinus Torvalds } else { 6211da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 6221da177e4SLinus Torvalds xfrm_state_put(x); 6231da177e4SLinus Torvalds x = NULL; 6241da177e4SLinus Torvalds error = -ESRCH; 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds out: 6281da177e4SLinus Torvalds if (x) 6291da177e4SLinus Torvalds xfrm_state_hold(x); 6301da177e4SLinus Torvalds else 6311da177e4SLinus Torvalds *err = acquire_in_progress ? -EAGAIN : error; 6321da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6331da177e4SLinus Torvalds return x; 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x) 6371da177e4SLinus Torvalds { 638a624c108SDavid S. Miller unsigned int h; 6391da177e4SLinus Torvalds 6409d4a706dSDavid S. Miller x->genid = ++xfrm_state_genid; 6419d4a706dSDavid S. Miller 642c1969f29SDavid S. Miller h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 643c1969f29SDavid S. Miller x->props.reqid, x->props.family); 6448f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 6451da177e4SLinus Torvalds 646667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family); 6478f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 6486c44e6b7SMasahide NAKAMURA 6497b4dc360SMasahide NAKAMURA if (x->id.spi) { 6506c44e6b7SMasahide NAKAMURA h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, 6516c44e6b7SMasahide NAKAMURA x->props.family); 6521da177e4SLinus Torvalds 6538f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 6546c44e6b7SMasahide NAKAMURA } 6551da177e4SLinus Torvalds 656a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies + HZ); 657a47f0ce0SDavid S. Miller if (x->replay_maxage) 658a47f0ce0SDavid S. Miller mod_timer(&x->rtimer, jiffies + x->replay_maxage); 659f8cd5488SJamal Hadi Salim 6601da177e4SLinus Torvalds wake_up(&km_waitq); 661f034b5d4SDavid S. Miller 662f034b5d4SDavid S. Miller xfrm_state_num++; 663f034b5d4SDavid S. Miller 664918049f0SDavid S. Miller xfrm_hash_grow_check(x->bydst.next != NULL); 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 667c7f5ea3aSDavid S. Miller /* xfrm_state_lock is held */ 668c7f5ea3aSDavid S. Miller static void __xfrm_state_bump_genids(struct xfrm_state *xnew) 669c7f5ea3aSDavid S. Miller { 670c7f5ea3aSDavid S. Miller unsigned short family = xnew->props.family; 671c7f5ea3aSDavid S. Miller u32 reqid = xnew->props.reqid; 672c7f5ea3aSDavid S. Miller struct xfrm_state *x; 673c7f5ea3aSDavid S. Miller struct hlist_node *entry; 674c7f5ea3aSDavid S. Miller unsigned int h; 675c7f5ea3aSDavid S. Miller 676c1969f29SDavid S. Miller h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family); 677c7f5ea3aSDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 678c7f5ea3aSDavid S. Miller if (x->props.family == family && 679c7f5ea3aSDavid S. Miller x->props.reqid == reqid && 680c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) && 681c1969f29SDavid S. Miller !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family)) 682c7f5ea3aSDavid S. Miller x->genid = xfrm_state_genid; 683c7f5ea3aSDavid S. Miller } 684c7f5ea3aSDavid S. Miller } 685c7f5ea3aSDavid S. Miller 6861da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x) 6871da177e4SLinus Torvalds { 6881da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 689c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 6901da177e4SLinus Torvalds __xfrm_state_insert(x); 6911da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert); 6941da177e4SLinus Torvalds 6952770834cSDavid S. Miller /* xfrm_state_lock is held */ 6962770834cSDavid 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) 6972770834cSDavid S. Miller { 698c1969f29SDavid S. Miller unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); 6998f126e37SDavid S. Miller struct hlist_node *entry; 7002770834cSDavid S. Miller struct xfrm_state *x; 7012770834cSDavid S. Miller 7028f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 7032770834cSDavid S. Miller if (x->props.reqid != reqid || 7042770834cSDavid S. Miller x->props.mode != mode || 7052770834cSDavid S. Miller x->props.family != family || 7062770834cSDavid S. Miller x->km.state != XFRM_STATE_ACQ || 7072770834cSDavid S. Miller x->id.spi != 0) 7082770834cSDavid S. Miller continue; 7092770834cSDavid S. Miller 7102770834cSDavid S. Miller switch (family) { 7112770834cSDavid S. Miller case AF_INET: 7122770834cSDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 7132770834cSDavid S. Miller x->props.saddr.a4 != saddr->a4) 7142770834cSDavid S. Miller continue; 7152770834cSDavid S. Miller break; 7162770834cSDavid S. Miller case AF_INET6: 7172770834cSDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6, 7182770834cSDavid S. Miller (struct in6_addr *)daddr) || 7192770834cSDavid S. Miller !ipv6_addr_equal((struct in6_addr *) 7202770834cSDavid S. Miller x->props.saddr.a6, 7212770834cSDavid S. Miller (struct in6_addr *)saddr)) 7222770834cSDavid S. Miller continue; 7232770834cSDavid S. Miller break; 7242770834cSDavid S. Miller }; 7252770834cSDavid S. Miller 7262770834cSDavid S. Miller xfrm_state_hold(x); 7272770834cSDavid S. Miller return x; 7282770834cSDavid S. Miller } 7292770834cSDavid S. Miller 7302770834cSDavid S. Miller if (!create) 7312770834cSDavid S. Miller return NULL; 7322770834cSDavid S. Miller 7332770834cSDavid S. Miller x = xfrm_state_alloc(); 7342770834cSDavid S. Miller if (likely(x)) { 7352770834cSDavid S. Miller switch (family) { 7362770834cSDavid S. Miller case AF_INET: 7372770834cSDavid S. Miller x->sel.daddr.a4 = daddr->a4; 7382770834cSDavid S. Miller x->sel.saddr.a4 = saddr->a4; 7392770834cSDavid S. Miller x->sel.prefixlen_d = 32; 7402770834cSDavid S. Miller x->sel.prefixlen_s = 32; 7412770834cSDavid S. Miller x->props.saddr.a4 = saddr->a4; 7422770834cSDavid S. Miller x->id.daddr.a4 = daddr->a4; 7432770834cSDavid S. Miller break; 7442770834cSDavid S. Miller 7452770834cSDavid S. Miller case AF_INET6: 7462770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, 7472770834cSDavid S. Miller (struct in6_addr *)daddr); 7482770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, 7492770834cSDavid S. Miller (struct in6_addr *)saddr); 7502770834cSDavid S. Miller x->sel.prefixlen_d = 128; 7512770834cSDavid S. Miller x->sel.prefixlen_s = 128; 7522770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, 7532770834cSDavid S. Miller (struct in6_addr *)saddr); 7542770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, 7552770834cSDavid S. Miller (struct in6_addr *)daddr); 7562770834cSDavid S. Miller break; 7572770834cSDavid S. Miller }; 7582770834cSDavid S. Miller 7592770834cSDavid S. Miller x->km.state = XFRM_STATE_ACQ; 7602770834cSDavid S. Miller x->id.proto = proto; 7612770834cSDavid S. Miller x->props.family = family; 7622770834cSDavid S. Miller x->props.mode = mode; 7632770834cSDavid S. Miller x->props.reqid = reqid; 7642770834cSDavid S. Miller x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 7652770834cSDavid S. Miller xfrm_state_hold(x); 7662770834cSDavid S. Miller x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 7672770834cSDavid S. Miller add_timer(&x->timer); 7688f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 769667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(daddr, saddr, family); 7708f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 7712770834cSDavid S. Miller wake_up(&km_waitq); 772918049f0SDavid S. Miller 773918049f0SDavid S. Miller xfrm_state_num++; 774918049f0SDavid S. Miller 775918049f0SDavid S. Miller xfrm_hash_grow_check(x->bydst.next != NULL); 7762770834cSDavid S. Miller } 7772770834cSDavid S. Miller 7782770834cSDavid S. Miller return x; 7792770834cSDavid S. Miller } 7802770834cSDavid S. Miller 7811da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x) 7841da177e4SLinus Torvalds { 7851da177e4SLinus Torvalds struct xfrm_state *x1; 7861da177e4SLinus Torvalds int family; 7871da177e4SLinus Torvalds int err; 788eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds family = x->props.family; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7931da177e4SLinus Torvalds 794edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, family); 7951da177e4SLinus Torvalds if (x1) { 7961da177e4SLinus Torvalds xfrm_state_put(x1); 7971da177e4SLinus Torvalds x1 = NULL; 7981da177e4SLinus Torvalds err = -EEXIST; 7991da177e4SLinus Torvalds goto out; 8001da177e4SLinus Torvalds } 8011da177e4SLinus Torvalds 802eb2971b6SMasahide NAKAMURA if (use_spi && x->km.seq) { 8031da177e4SLinus Torvalds x1 = __xfrm_find_acq_byseq(x->km.seq); 8041da177e4SLinus Torvalds if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) { 8051da177e4SLinus Torvalds xfrm_state_put(x1); 8061da177e4SLinus Torvalds x1 = NULL; 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds 810eb2971b6SMasahide NAKAMURA if (use_spi && !x1) 8112770834cSDavid S. Miller x1 = __find_acq_core(family, x->props.mode, x->props.reqid, 8122770834cSDavid S. Miller x->id.proto, 8131da177e4SLinus Torvalds &x->id.daddr, &x->props.saddr, 0); 8141da177e4SLinus Torvalds 815c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 8161da177e4SLinus Torvalds __xfrm_state_insert(x); 8171da177e4SLinus Torvalds err = 0; 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds out: 8201da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds if (x1) { 8231da177e4SLinus Torvalds xfrm_state_delete(x1); 8241da177e4SLinus Torvalds xfrm_state_put(x1); 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds return err; 8281da177e4SLinus Torvalds } 8291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add); 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x) 8321da177e4SLinus Torvalds { 8331da177e4SLinus Torvalds struct xfrm_state *x1; 8341da177e4SLinus Torvalds int err; 835eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 838edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, x->props.family); 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds err = -ESRCH; 8411da177e4SLinus Torvalds if (!x1) 8421da177e4SLinus Torvalds goto out; 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds if (xfrm_state_kern(x1)) { 8451da177e4SLinus Torvalds xfrm_state_put(x1); 8461da177e4SLinus Torvalds err = -EEXIST; 8471da177e4SLinus Torvalds goto out; 8481da177e4SLinus Torvalds } 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds if (x1->km.state == XFRM_STATE_ACQ) { 8511da177e4SLinus Torvalds __xfrm_state_insert(x); 8521da177e4SLinus Torvalds x = NULL; 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds err = 0; 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds out: 8571da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds if (err) 8601da177e4SLinus Torvalds return err; 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds if (!x) { 8631da177e4SLinus Torvalds xfrm_state_delete(x1); 8641da177e4SLinus Torvalds xfrm_state_put(x1); 8651da177e4SLinus Torvalds return 0; 8661da177e4SLinus Torvalds } 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds err = -EINVAL; 8691da177e4SLinus Torvalds spin_lock_bh(&x1->lock); 8701da177e4SLinus Torvalds if (likely(x1->km.state == XFRM_STATE_VALID)) { 8711da177e4SLinus Torvalds if (x->encap && x1->encap) 8721da177e4SLinus Torvalds memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 873060f02a3SNoriaki TAKAMIYA if (x->coaddr && x1->coaddr) { 874060f02a3SNoriaki TAKAMIYA memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr)); 875060f02a3SNoriaki TAKAMIYA } 876060f02a3SNoriaki TAKAMIYA if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel))) 877060f02a3SNoriaki TAKAMIYA memcpy(&x1->sel, &x->sel, sizeof(x1->sel)); 8781da177e4SLinus Torvalds memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 8791da177e4SLinus Torvalds x1->km.dying = 0; 8801da177e4SLinus Torvalds 881a47f0ce0SDavid S. Miller mod_timer(&x1->timer, jiffies + HZ); 8821da177e4SLinus Torvalds if (x1->curlft.use_time) 8831da177e4SLinus Torvalds xfrm_state_check_expire(x1); 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds err = 0; 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds spin_unlock_bh(&x1->lock); 8881da177e4SLinus Torvalds 8891da177e4SLinus Torvalds xfrm_state_put(x1); 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds return err; 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update); 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x) 8961da177e4SLinus Torvalds { 8971da177e4SLinus Torvalds if (!x->curlft.use_time) 8981da177e4SLinus Torvalds x->curlft.use_time = (unsigned long)xtime.tv_sec; 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_VALID) 9011da177e4SLinus Torvalds return -EINVAL; 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds if (x->curlft.bytes >= x->lft.hard_byte_limit || 9041da177e4SLinus Torvalds x->curlft.packets >= x->lft.hard_packet_limit) { 9054666faabSHerbert Xu x->km.state = XFRM_STATE_EXPIRED; 906a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies); 9071da177e4SLinus Torvalds return -EINVAL; 9081da177e4SLinus Torvalds } 9091da177e4SLinus Torvalds 9101da177e4SLinus Torvalds if (!x->km.dying && 9111da177e4SLinus Torvalds (x->curlft.bytes >= x->lft.soft_byte_limit || 9124666faabSHerbert Xu x->curlft.packets >= x->lft.soft_packet_limit)) { 9134666faabSHerbert Xu x->km.dying = 1; 91453bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 9154666faabSHerbert Xu } 9161da177e4SLinus Torvalds return 0; 9171da177e4SLinus Torvalds } 9181da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire); 9191da177e4SLinus Torvalds 9201da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 9211da177e4SLinus Torvalds { 9221da177e4SLinus Torvalds int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 9231da177e4SLinus Torvalds - skb_headroom(skb); 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds if (nhead > 0) 9261da177e4SLinus Torvalds return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds /* Check tail too... */ 9291da177e4SLinus Torvalds return 0; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 9331da177e4SLinus Torvalds { 9341da177e4SLinus Torvalds int err = xfrm_state_check_expire(x); 9351da177e4SLinus Torvalds if (err < 0) 9361da177e4SLinus Torvalds goto err; 9371da177e4SLinus Torvalds err = xfrm_state_check_space(x, skb); 9381da177e4SLinus Torvalds err: 9391da177e4SLinus Torvalds return err; 9401da177e4SLinus Torvalds } 9411da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check); 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds struct xfrm_state * 944a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, 9451da177e4SLinus Torvalds unsigned short family) 9461da177e4SLinus Torvalds { 9471da177e4SLinus Torvalds struct xfrm_state *x; 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 950edcd5821SDavid S. Miller x = __xfrm_state_lookup(daddr, spi, proto, family); 9511da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9521da177e4SLinus Torvalds return x; 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup); 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds struct xfrm_state * 957eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, 958eb2971b6SMasahide NAKAMURA u8 proto, unsigned short family) 959eb2971b6SMasahide NAKAMURA { 960eb2971b6SMasahide NAKAMURA struct xfrm_state *x; 961eb2971b6SMasahide NAKAMURA 962eb2971b6SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 963edcd5821SDavid S. Miller x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family); 964eb2971b6SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 965eb2971b6SMasahide NAKAMURA return x; 966eb2971b6SMasahide NAKAMURA } 967eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr); 968eb2971b6SMasahide NAKAMURA 969eb2971b6SMasahide NAKAMURA struct xfrm_state * 9701da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto, 9711da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 9721da177e4SLinus Torvalds int create, unsigned short family) 9731da177e4SLinus Torvalds { 9741da177e4SLinus Torvalds struct xfrm_state *x; 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 9772770834cSDavid S. Miller x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create); 9781da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 9792770834cSDavid S. Miller 9801da177e4SLinus Torvalds return x; 9811da177e4SLinus Torvalds } 9821da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq); 9831da177e4SLinus Torvalds 98441a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 98541a49cc3SMasahide NAKAMURA int 98641a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, 98741a49cc3SMasahide NAKAMURA unsigned short family) 98841a49cc3SMasahide NAKAMURA { 98941a49cc3SMasahide NAKAMURA int err = 0; 99041a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 99141a49cc3SMasahide NAKAMURA if (!afinfo) 99241a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 99341a49cc3SMasahide NAKAMURA 99441a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 99541a49cc3SMasahide NAKAMURA if (afinfo->tmpl_sort) 99641a49cc3SMasahide NAKAMURA err = afinfo->tmpl_sort(dst, src, n); 99741a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 99841a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 99941a49cc3SMasahide NAKAMURA return err; 100041a49cc3SMasahide NAKAMURA } 100141a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort); 100241a49cc3SMasahide NAKAMURA 100341a49cc3SMasahide NAKAMURA int 100441a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, 100541a49cc3SMasahide NAKAMURA unsigned short family) 100641a49cc3SMasahide NAKAMURA { 100741a49cc3SMasahide NAKAMURA int err = 0; 100841a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 100941a49cc3SMasahide NAKAMURA if (!afinfo) 101041a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 101141a49cc3SMasahide NAKAMURA 101241a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 101341a49cc3SMasahide NAKAMURA if (afinfo->state_sort) 101441a49cc3SMasahide NAKAMURA err = afinfo->state_sort(dst, src, n); 101541a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 101641a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 101741a49cc3SMasahide NAKAMURA return err; 101841a49cc3SMasahide NAKAMURA } 101941a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort); 102041a49cc3SMasahide NAKAMURA #endif 102141a49cc3SMasahide NAKAMURA 10221da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */ 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq) 10251da177e4SLinus Torvalds { 10261da177e4SLinus Torvalds int i; 10271da177e4SLinus Torvalds 1028f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 10298f126e37SDavid S. Miller struct hlist_node *entry; 10308f126e37SDavid S. Miller struct xfrm_state *x; 10318f126e37SDavid S. Miller 10328f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 10338f126e37SDavid S. Miller if (x->km.seq == seq && 10348f126e37SDavid S. Miller x->km.state == XFRM_STATE_ACQ) { 10351da177e4SLinus Torvalds xfrm_state_hold(x); 10361da177e4SLinus Torvalds return x; 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds return NULL; 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds 10431da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq) 10441da177e4SLinus Torvalds { 10451da177e4SLinus Torvalds struct xfrm_state *x; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10481da177e4SLinus Torvalds x = __xfrm_find_acq_byseq(seq); 10491da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10501da177e4SLinus Torvalds return x; 10511da177e4SLinus Torvalds } 10521da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq); 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds u32 xfrm_get_acqseq(void) 10551da177e4SLinus Torvalds { 10561da177e4SLinus Torvalds u32 res; 10571da177e4SLinus Torvalds static u32 acqseq; 10581da177e4SLinus Torvalds static DEFINE_SPINLOCK(acqseq_lock); 10591da177e4SLinus Torvalds 10601da177e4SLinus Torvalds spin_lock_bh(&acqseq_lock); 10611da177e4SLinus Torvalds res = (++acqseq ? : ++acqseq); 10621da177e4SLinus Torvalds spin_unlock_bh(&acqseq_lock); 10631da177e4SLinus Torvalds return res; 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq); 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds void 106826977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi) 10691da177e4SLinus Torvalds { 1070f034b5d4SDavid S. Miller unsigned int h; 10711da177e4SLinus Torvalds struct xfrm_state *x0; 10721da177e4SLinus Torvalds 10731da177e4SLinus Torvalds if (x->id.spi) 10741da177e4SLinus Torvalds return; 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds if (minspi == maxspi) { 10771da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); 10781da177e4SLinus Torvalds if (x0) { 10791da177e4SLinus Torvalds xfrm_state_put(x0); 10801da177e4SLinus Torvalds return; 10811da177e4SLinus Torvalds } 10821da177e4SLinus Torvalds x->id.spi = minspi; 10831da177e4SLinus Torvalds } else { 10841da177e4SLinus Torvalds u32 spi = 0; 108526977b4eSAl Viro u32 low = ntohl(minspi); 108626977b4eSAl Viro u32 high = ntohl(maxspi); 108726977b4eSAl Viro for (h=0; h<high-low+1; h++) { 108826977b4eSAl Viro spi = low + net_random()%(high-low+1); 10891da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family); 10901da177e4SLinus Torvalds if (x0 == NULL) { 10911da177e4SLinus Torvalds x->id.spi = htonl(spi); 10921da177e4SLinus Torvalds break; 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds xfrm_state_put(x0); 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds if (x->id.spi) { 10981da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 10991da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 11008f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 11011da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11021da177e4SLinus Torvalds wake_up(&km_waitq); 11031da177e4SLinus Torvalds } 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi); 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), 11081da177e4SLinus Torvalds void *data) 11091da177e4SLinus Torvalds { 11101da177e4SLinus Torvalds int i; 111194b9bb54SJamal Hadi Salim struct xfrm_state *x, *last = NULL; 11128f126e37SDavid S. Miller struct hlist_node *entry; 11131da177e4SLinus Torvalds int count = 0; 11141da177e4SLinus Torvalds int err = 0; 11151da177e4SLinus Torvalds 11161da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 1117f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 11188f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 111994b9bb54SJamal Hadi Salim if (!xfrm_id_proto_match(x->id.proto, proto)) 112094b9bb54SJamal Hadi Salim continue; 112194b9bb54SJamal Hadi Salim if (last) { 112294b9bb54SJamal Hadi Salim err = func(last, count, data); 112394b9bb54SJamal Hadi Salim if (err) 112494b9bb54SJamal Hadi Salim goto out; 112594b9bb54SJamal Hadi Salim } 112694b9bb54SJamal Hadi Salim last = x; 11271da177e4SLinus Torvalds count++; 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds if (count == 0) { 11311da177e4SLinus Torvalds err = -ENOENT; 11321da177e4SLinus Torvalds goto out; 11331da177e4SLinus Torvalds } 113494b9bb54SJamal Hadi Salim err = func(last, 0, data); 11351da177e4SLinus Torvalds out: 11361da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11371da177e4SLinus Torvalds return err; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk); 11401da177e4SLinus Torvalds 1141f8cd5488SJamal Hadi Salim 1142f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event) 1143f8cd5488SJamal Hadi Salim { 1144f8cd5488SJamal Hadi Salim struct km_event c; 1145f8cd5488SJamal Hadi Salim /* we send notify messages in case 1146f8cd5488SJamal Hadi Salim * 1. we updated on of the sequence numbers, and the seqno difference 1147f8cd5488SJamal Hadi Salim * is at least x->replay_maxdiff, in this case we also update the 1148f8cd5488SJamal Hadi Salim * timeout of our timer function 1149f8cd5488SJamal Hadi Salim * 2. if x->replay_maxage has elapsed since last update, 1150f8cd5488SJamal Hadi Salim * and there were changes 1151f8cd5488SJamal Hadi Salim * 1152f8cd5488SJamal Hadi Salim * The state structure must be locked! 1153f8cd5488SJamal Hadi Salim */ 1154f8cd5488SJamal Hadi Salim 1155f8cd5488SJamal Hadi Salim switch (event) { 1156f8cd5488SJamal Hadi Salim case XFRM_REPLAY_UPDATE: 1157f8cd5488SJamal Hadi Salim if (x->replay_maxdiff && 1158f8cd5488SJamal Hadi Salim (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 11592717096aSJamal Hadi Salim (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { 11602717096aSJamal Hadi Salim if (x->xflags & XFRM_TIME_DEFER) 11612717096aSJamal Hadi Salim event = XFRM_REPLAY_TIMEOUT; 11622717096aSJamal Hadi Salim else 1163f8cd5488SJamal Hadi Salim return; 11642717096aSJamal Hadi Salim } 1165f8cd5488SJamal Hadi Salim 1166f8cd5488SJamal Hadi Salim break; 1167f8cd5488SJamal Hadi Salim 1168f8cd5488SJamal Hadi Salim case XFRM_REPLAY_TIMEOUT: 1169f8cd5488SJamal Hadi Salim if ((x->replay.seq == x->preplay.seq) && 1170f8cd5488SJamal Hadi Salim (x->replay.bitmap == x->preplay.bitmap) && 11712717096aSJamal Hadi Salim (x->replay.oseq == x->preplay.oseq)) { 11722717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 1173f8cd5488SJamal Hadi Salim return; 11742717096aSJamal Hadi Salim } 1175f8cd5488SJamal Hadi Salim 1176f8cd5488SJamal Hadi Salim break; 1177f8cd5488SJamal Hadi Salim } 1178f8cd5488SJamal Hadi Salim 1179f8cd5488SJamal Hadi Salim memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 1180f8cd5488SJamal Hadi Salim c.event = XFRM_MSG_NEWAE; 1181f8cd5488SJamal Hadi Salim c.data.aevent = event; 1182f8cd5488SJamal Hadi Salim km_state_notify(x, &c); 1183f8cd5488SJamal Hadi Salim 1184f8cd5488SJamal Hadi Salim if (x->replay_maxage && 1185a47f0ce0SDavid S. Miller !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 11862717096aSJamal Hadi Salim x->xflags &= ~XFRM_TIME_DEFER; 11872717096aSJamal Hadi Salim } 1188a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify); 1189f8cd5488SJamal Hadi Salim 1190f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data) 1191f8cd5488SJamal Hadi Salim { 1192f8cd5488SJamal Hadi Salim struct xfrm_state *x = (struct xfrm_state*)data; 1193f8cd5488SJamal Hadi Salim 1194f8cd5488SJamal Hadi Salim spin_lock(&x->lock); 1195f8cd5488SJamal Hadi Salim 11962717096aSJamal Hadi Salim if (x->km.state == XFRM_STATE_VALID) { 11972717096aSJamal Hadi Salim if (xfrm_aevent_is_on()) 1198f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); 11992717096aSJamal Hadi Salim else 12002717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 12012717096aSJamal Hadi Salim } 1202f8cd5488SJamal Hadi Salim 1203f8cd5488SJamal Hadi Salim spin_unlock(&x->lock); 1204f8cd5488SJamal Hadi Salim } 1205f8cd5488SJamal Hadi Salim 1206a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) 12071da177e4SLinus Torvalds { 12081da177e4SLinus Torvalds u32 diff; 1209a252cc23SAl Viro u32 seq = ntohl(net_seq); 12101da177e4SLinus Torvalds 12111da177e4SLinus Torvalds if (unlikely(seq == 0)) 12121da177e4SLinus Torvalds return -EINVAL; 12131da177e4SLinus Torvalds 12141da177e4SLinus Torvalds if (likely(seq > x->replay.seq)) 12151da177e4SLinus Torvalds return 0; 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds diff = x->replay.seq - seq; 12181da177e4SLinus Torvalds if (diff >= x->props.replay_window) { 12191da177e4SLinus Torvalds x->stats.replay_window++; 12201da177e4SLinus Torvalds return -EINVAL; 12211da177e4SLinus Torvalds } 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds if (x->replay.bitmap & (1U << diff)) { 12241da177e4SLinus Torvalds x->stats.replay++; 12251da177e4SLinus Torvalds return -EINVAL; 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds return 0; 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check); 12301da177e4SLinus Torvalds 123161f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) 12321da177e4SLinus Torvalds { 12331da177e4SLinus Torvalds u32 diff; 123461f4627bSAl Viro u32 seq = ntohl(net_seq); 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds if (seq > x->replay.seq) { 12371da177e4SLinus Torvalds diff = seq - x->replay.seq; 12381da177e4SLinus Torvalds if (diff < x->props.replay_window) 12391da177e4SLinus Torvalds x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 12401da177e4SLinus Torvalds else 12411da177e4SLinus Torvalds x->replay.bitmap = 1; 12421da177e4SLinus Torvalds x->replay.seq = seq; 12431da177e4SLinus Torvalds } else { 12441da177e4SLinus Torvalds diff = x->replay.seq - seq; 12451da177e4SLinus Torvalds x->replay.bitmap |= (1U << diff); 12461da177e4SLinus Torvalds } 1247f8cd5488SJamal Hadi Salim 1248f8cd5488SJamal Hadi Salim if (xfrm_aevent_is_on()) 1249f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 12501da177e4SLinus Torvalds } 12511da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance); 12521da177e4SLinus Torvalds 12531da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); 12541da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock); 12551da177e4SLinus Torvalds 125626b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 12571da177e4SLinus Torvalds { 12581da177e4SLinus Torvalds struct xfrm_mgr *km; 12591da177e4SLinus Torvalds 126026b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 126126b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 126226b15dadSJamal Hadi Salim if (km->notify_policy) 126326b15dadSJamal Hadi Salim km->notify_policy(xp, dir, c); 126426b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 126526b15dadSJamal Hadi Salim } 126626b15dadSJamal Hadi Salim 126726b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c) 126826b15dadSJamal Hadi Salim { 126926b15dadSJamal Hadi Salim struct xfrm_mgr *km; 127026b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 127126b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 127226b15dadSJamal Hadi Salim if (km->notify) 127326b15dadSJamal Hadi Salim km->notify(x, c); 127426b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 127526b15dadSJamal Hadi Salim } 127626b15dadSJamal Hadi Salim 127726b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify); 127826b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify); 127926b15dadSJamal Hadi Salim 128053bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid) 128126b15dadSJamal Hadi Salim { 128226b15dadSJamal Hadi Salim struct km_event c; 128326b15dadSJamal Hadi Salim 1284bf08867fSHerbert Xu c.data.hard = hard; 128553bc6b4dSJamal Hadi Salim c.pid = pid; 1286f60f6b8fSHerbert Xu c.event = XFRM_MSG_EXPIRE; 128726b15dadSJamal Hadi Salim km_state_notify(x, &c); 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds if (hard) 12901da177e4SLinus Torvalds wake_up(&km_waitq); 12911da177e4SLinus Torvalds } 12921da177e4SLinus Torvalds 129353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired); 129426b15dadSJamal Hadi Salim /* 129526b15dadSJamal Hadi Salim * We send to all registered managers regardless of failure 129626b15dadSJamal Hadi Salim * We are happy with one success 129726b15dadSJamal Hadi Salim */ 1298980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 12991da177e4SLinus Torvalds { 130026b15dadSJamal Hadi Salim int err = -EINVAL, acqret; 13011da177e4SLinus Torvalds struct xfrm_mgr *km; 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13041da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 130526b15dadSJamal Hadi Salim acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); 130626b15dadSJamal Hadi Salim if (!acqret) 130726b15dadSJamal Hadi Salim err = acqret; 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13101da177e4SLinus Torvalds return err; 13111da177e4SLinus Torvalds } 1312980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query); 13131da177e4SLinus Torvalds 13145d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) 13151da177e4SLinus Torvalds { 13161da177e4SLinus Torvalds int err = -EINVAL; 13171da177e4SLinus Torvalds struct xfrm_mgr *km; 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13201da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 13211da177e4SLinus Torvalds if (km->new_mapping) 13221da177e4SLinus Torvalds err = km->new_mapping(x, ipaddr, sport); 13231da177e4SLinus Torvalds if (!err) 13241da177e4SLinus Torvalds break; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13271da177e4SLinus Torvalds return err; 13281da177e4SLinus Torvalds } 13291da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping); 13301da177e4SLinus Torvalds 13316c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) 13321da177e4SLinus Torvalds { 133326b15dadSJamal Hadi Salim struct km_event c; 13341da177e4SLinus Torvalds 1335bf08867fSHerbert Xu c.data.hard = hard; 13366c5c8ca7SJamal Hadi Salim c.pid = pid; 1337f60f6b8fSHerbert Xu c.event = XFRM_MSG_POLEXPIRE; 133826b15dadSJamal Hadi Salim km_policy_notify(pol, dir, &c); 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds if (hard) 13411da177e4SLinus Torvalds wake_up(&km_waitq); 13421da177e4SLinus Torvalds } 1343a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired); 13441da177e4SLinus Torvalds 134597a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) 134697a64b45SMasahide NAKAMURA { 134797a64b45SMasahide NAKAMURA int err = -EINVAL; 134897a64b45SMasahide NAKAMURA int ret; 134997a64b45SMasahide NAKAMURA struct xfrm_mgr *km; 135097a64b45SMasahide NAKAMURA 135197a64b45SMasahide NAKAMURA read_lock(&xfrm_km_lock); 135297a64b45SMasahide NAKAMURA list_for_each_entry(km, &xfrm_km_list, list) { 135397a64b45SMasahide NAKAMURA if (km->report) { 135497a64b45SMasahide NAKAMURA ret = km->report(proto, sel, addr); 135597a64b45SMasahide NAKAMURA if (!ret) 135697a64b45SMasahide NAKAMURA err = ret; 135797a64b45SMasahide NAKAMURA } 135897a64b45SMasahide NAKAMURA } 135997a64b45SMasahide NAKAMURA read_unlock(&xfrm_km_lock); 136097a64b45SMasahide NAKAMURA return err; 136197a64b45SMasahide NAKAMURA } 136297a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report); 136397a64b45SMasahide NAKAMURA 13641da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 13651da177e4SLinus Torvalds { 13661da177e4SLinus Torvalds int err; 13671da177e4SLinus Torvalds u8 *data; 13681da177e4SLinus Torvalds struct xfrm_mgr *km; 13691da177e4SLinus Torvalds struct xfrm_policy *pol = NULL; 13701da177e4SLinus Torvalds 13711da177e4SLinus Torvalds if (optlen <= 0 || optlen > PAGE_SIZE) 13721da177e4SLinus Torvalds return -EMSGSIZE; 13731da177e4SLinus Torvalds 13741da177e4SLinus Torvalds data = kmalloc(optlen, GFP_KERNEL); 13751da177e4SLinus Torvalds if (!data) 13761da177e4SLinus Torvalds return -ENOMEM; 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds err = -EFAULT; 13791da177e4SLinus Torvalds if (copy_from_user(data, optval, optlen)) 13801da177e4SLinus Torvalds goto out; 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds err = -EINVAL; 13831da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 13841da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 1385cb969f07SVenkat Yekkirala pol = km->compile_policy(sk, optname, data, 13861da177e4SLinus Torvalds optlen, &err); 13871da177e4SLinus Torvalds if (err >= 0) 13881da177e4SLinus Torvalds break; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 13911da177e4SLinus Torvalds 13921da177e4SLinus Torvalds if (err >= 0) { 13931da177e4SLinus Torvalds xfrm_sk_policy_insert(sk, err, pol); 13941da177e4SLinus Torvalds xfrm_pol_put(pol); 13951da177e4SLinus Torvalds err = 0; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds 13981da177e4SLinus Torvalds out: 13991da177e4SLinus Torvalds kfree(data); 14001da177e4SLinus Torvalds return err; 14011da177e4SLinus Torvalds } 14021da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy); 14031da177e4SLinus Torvalds 14041da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km) 14051da177e4SLinus Torvalds { 14061da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 14071da177e4SLinus Torvalds list_add_tail(&km->list, &xfrm_km_list); 14081da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 14091da177e4SLinus Torvalds return 0; 14101da177e4SLinus Torvalds } 14111da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km); 14121da177e4SLinus Torvalds 14131da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km) 14141da177e4SLinus Torvalds { 14151da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 14161da177e4SLinus Torvalds list_del(&km->list); 14171da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 14181da177e4SLinus Torvalds return 0; 14191da177e4SLinus Torvalds } 14201da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km); 14211da177e4SLinus Torvalds 14221da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 14231da177e4SLinus Torvalds { 14241da177e4SLinus Torvalds int err = 0; 14251da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14261da177e4SLinus Torvalds return -EINVAL; 14271da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14281da177e4SLinus Torvalds return -EAFNOSUPPORT; 1429f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14301da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 14311da177e4SLinus Torvalds err = -ENOBUFS; 1432edcd5821SDavid S. Miller else 14331da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = afinfo; 1434f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14351da177e4SLinus Torvalds return err; 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo); 14381da177e4SLinus Torvalds 14391da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 14401da177e4SLinus Torvalds { 14411da177e4SLinus Torvalds int err = 0; 14421da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 14431da177e4SLinus Torvalds return -EINVAL; 14441da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 14451da177e4SLinus Torvalds return -EAFNOSUPPORT; 1446f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 14471da177e4SLinus Torvalds if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 14481da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) 14491da177e4SLinus Torvalds err = -EINVAL; 1450edcd5821SDavid S. Miller else 14511da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = NULL; 14521da177e4SLinus Torvalds } 1453f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 14541da177e4SLinus Torvalds return err; 14551da177e4SLinus Torvalds } 14561da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo); 14571da177e4SLinus Torvalds 1458cdca7265SMiika Komu struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) 14591da177e4SLinus Torvalds { 14601da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 14611da177e4SLinus Torvalds if (unlikely(family >= NPROTO)) 14621da177e4SLinus Torvalds return NULL; 14631da177e4SLinus Torvalds read_lock(&xfrm_state_afinfo_lock); 14641da177e4SLinus Torvalds afinfo = xfrm_state_afinfo[family]; 1465546be240SHerbert Xu if (unlikely(!afinfo)) 14661da177e4SLinus Torvalds read_unlock(&xfrm_state_afinfo_lock); 14671da177e4SLinus Torvalds return afinfo; 14681da177e4SLinus Torvalds } 14691da177e4SLinus Torvalds 1470cdca7265SMiika Komu void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) 14711da177e4SLinus Torvalds { 1472546be240SHerbert Xu read_unlock(&xfrm_state_afinfo_lock); 14731da177e4SLinus Torvalds } 14741da177e4SLinus Torvalds 1475cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_get_afinfo); 1476cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_put_afinfo); 1477cdca7265SMiika Komu 14781da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 14791da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x) 14801da177e4SLinus Torvalds { 14811da177e4SLinus Torvalds if (x->tunnel) { 14821da177e4SLinus Torvalds struct xfrm_state *t = x->tunnel; 14831da177e4SLinus Torvalds 14841da177e4SLinus Torvalds if (atomic_read(&t->tunnel_users) == 2) 14851da177e4SLinus Torvalds xfrm_state_delete(t); 14861da177e4SLinus Torvalds atomic_dec(&t->tunnel_users); 14871da177e4SLinus Torvalds xfrm_state_put(t); 14881da177e4SLinus Torvalds x->tunnel = NULL; 14891da177e4SLinus Torvalds } 14901da177e4SLinus Torvalds } 14911da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel); 14921da177e4SLinus Torvalds 149380b30c10SHerbert Xu /* 149480b30c10SHerbert Xu * This function is NOT optimal. For example, with ESP it will give an 149580b30c10SHerbert Xu * MTU that's usually two bytes short of being optimal. However, it will 149680b30c10SHerbert Xu * usually give an answer that's a multiple of 4 provided the input is 149780b30c10SHerbert Xu * also a multiple of 4. 149880b30c10SHerbert Xu */ 14991da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu) 15001da177e4SLinus Torvalds { 15011da177e4SLinus Torvalds int res = mtu; 15021da177e4SLinus Torvalds 15031da177e4SLinus Torvalds res -= x->props.header_len; 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds for (;;) { 15061da177e4SLinus Torvalds int m = res; 15071da177e4SLinus Torvalds 15081da177e4SLinus Torvalds if (m < 68) 15091da177e4SLinus Torvalds return 68; 15101da177e4SLinus Torvalds 15111da177e4SLinus Torvalds spin_lock_bh(&x->lock); 15121da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID && 15131da177e4SLinus Torvalds x->type && x->type->get_max_size) 15141da177e4SLinus Torvalds m = x->type->get_max_size(x, m); 15151da177e4SLinus Torvalds else 15161da177e4SLinus Torvalds m += x->props.header_len; 15171da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 15181da177e4SLinus Torvalds 15191da177e4SLinus Torvalds if (m <= mtu) 15201da177e4SLinus Torvalds break; 15211da177e4SLinus Torvalds res -= (m - mtu); 15221da177e4SLinus Torvalds } 15231da177e4SLinus Torvalds 15241da177e4SLinus Torvalds return res; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds 152772cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x) 152872cb6962SHerbert Xu { 1529d094cd83SHerbert Xu struct xfrm_state_afinfo *afinfo; 1530d094cd83SHerbert Xu int family = x->props.family; 153172cb6962SHerbert Xu int err; 153272cb6962SHerbert Xu 1533d094cd83SHerbert Xu err = -EAFNOSUPPORT; 1534d094cd83SHerbert Xu afinfo = xfrm_state_get_afinfo(family); 1535d094cd83SHerbert Xu if (!afinfo) 1536d094cd83SHerbert Xu goto error; 1537d094cd83SHerbert Xu 1538d094cd83SHerbert Xu err = 0; 1539d094cd83SHerbert Xu if (afinfo->init_flags) 1540d094cd83SHerbert Xu err = afinfo->init_flags(x); 1541d094cd83SHerbert Xu 1542d094cd83SHerbert Xu xfrm_state_put_afinfo(afinfo); 1543d094cd83SHerbert Xu 1544d094cd83SHerbert Xu if (err) 1545d094cd83SHerbert Xu goto error; 1546d094cd83SHerbert Xu 1547d094cd83SHerbert Xu err = -EPROTONOSUPPORT; 1548d094cd83SHerbert Xu x->type = xfrm_get_type(x->id.proto, family); 154972cb6962SHerbert Xu if (x->type == NULL) 155072cb6962SHerbert Xu goto error; 155172cb6962SHerbert Xu 155272cb6962SHerbert Xu err = x->type->init_state(x); 155372cb6962SHerbert Xu if (err) 155472cb6962SHerbert Xu goto error; 155572cb6962SHerbert Xu 1556b59f45d0SHerbert Xu x->mode = xfrm_get_mode(x->props.mode, family); 1557b59f45d0SHerbert Xu if (x->mode == NULL) 1558b59f45d0SHerbert Xu goto error; 1559b59f45d0SHerbert Xu 156072cb6962SHerbert Xu x->km.state = XFRM_STATE_VALID; 156172cb6962SHerbert Xu 156272cb6962SHerbert Xu error: 156372cb6962SHerbert Xu return err; 156472cb6962SHerbert Xu } 156572cb6962SHerbert Xu 156672cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state); 15671da177e4SLinus Torvalds 15681da177e4SLinus Torvalds void __init xfrm_state_init(void) 15691da177e4SLinus Torvalds { 1570f034b5d4SDavid S. Miller unsigned int sz; 15711da177e4SLinus Torvalds 1572f034b5d4SDavid S. Miller sz = sizeof(struct hlist_head) * 8; 1573f034b5d4SDavid S. Miller 157444e36b42SDavid S. Miller xfrm_state_bydst = xfrm_hash_alloc(sz); 157544e36b42SDavid S. Miller xfrm_state_bysrc = xfrm_hash_alloc(sz); 157644e36b42SDavid S. Miller xfrm_state_byspi = xfrm_hash_alloc(sz); 1577f034b5d4SDavid S. Miller if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi) 1578f034b5d4SDavid S. Miller panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes."); 1579f034b5d4SDavid S. Miller xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1); 1580f034b5d4SDavid S. Miller 1581c4028958SDavid Howells INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task); 15821da177e4SLinus Torvalds } 15831da177e4SLinus Torvalds 1584