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 || 70775e252d9SJoy Latten x->id.spi != 0 || 70875e252d9SJoy Latten x->id.proto != proto) 7092770834cSDavid S. Miller continue; 7102770834cSDavid S. Miller 7112770834cSDavid S. Miller switch (family) { 7122770834cSDavid S. Miller case AF_INET: 7132770834cSDavid S. Miller if (x->id.daddr.a4 != daddr->a4 || 7142770834cSDavid S. Miller x->props.saddr.a4 != saddr->a4) 7152770834cSDavid S. Miller continue; 7162770834cSDavid S. Miller break; 7172770834cSDavid S. Miller case AF_INET6: 7182770834cSDavid S. Miller if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6, 7192770834cSDavid S. Miller (struct in6_addr *)daddr) || 7202770834cSDavid S. Miller !ipv6_addr_equal((struct in6_addr *) 7212770834cSDavid S. Miller x->props.saddr.a6, 7222770834cSDavid S. Miller (struct in6_addr *)saddr)) 7232770834cSDavid S. Miller continue; 7242770834cSDavid S. Miller break; 7252770834cSDavid S. Miller }; 7262770834cSDavid S. Miller 7272770834cSDavid S. Miller xfrm_state_hold(x); 7282770834cSDavid S. Miller return x; 7292770834cSDavid S. Miller } 7302770834cSDavid S. Miller 7312770834cSDavid S. Miller if (!create) 7322770834cSDavid S. Miller return NULL; 7332770834cSDavid S. Miller 7342770834cSDavid S. Miller x = xfrm_state_alloc(); 7352770834cSDavid S. Miller if (likely(x)) { 7362770834cSDavid S. Miller switch (family) { 7372770834cSDavid S. Miller case AF_INET: 7382770834cSDavid S. Miller x->sel.daddr.a4 = daddr->a4; 7392770834cSDavid S. Miller x->sel.saddr.a4 = saddr->a4; 7402770834cSDavid S. Miller x->sel.prefixlen_d = 32; 7412770834cSDavid S. Miller x->sel.prefixlen_s = 32; 7422770834cSDavid S. Miller x->props.saddr.a4 = saddr->a4; 7432770834cSDavid S. Miller x->id.daddr.a4 = daddr->a4; 7442770834cSDavid S. Miller break; 7452770834cSDavid S. Miller 7462770834cSDavid S. Miller case AF_INET6: 7472770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, 7482770834cSDavid S. Miller (struct in6_addr *)daddr); 7492770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, 7502770834cSDavid S. Miller (struct in6_addr *)saddr); 7512770834cSDavid S. Miller x->sel.prefixlen_d = 128; 7522770834cSDavid S. Miller x->sel.prefixlen_s = 128; 7532770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, 7542770834cSDavid S. Miller (struct in6_addr *)saddr); 7552770834cSDavid S. Miller ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, 7562770834cSDavid S. Miller (struct in6_addr *)daddr); 7572770834cSDavid S. Miller break; 7582770834cSDavid S. Miller }; 7592770834cSDavid S. Miller 7602770834cSDavid S. Miller x->km.state = XFRM_STATE_ACQ; 7612770834cSDavid S. Miller x->id.proto = proto; 7622770834cSDavid S. Miller x->props.family = family; 7632770834cSDavid S. Miller x->props.mode = mode; 7642770834cSDavid S. Miller x->props.reqid = reqid; 7652770834cSDavid S. Miller x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 7662770834cSDavid S. Miller xfrm_state_hold(x); 7672770834cSDavid S. Miller x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 7682770834cSDavid S. Miller add_timer(&x->timer); 7698f126e37SDavid S. Miller hlist_add_head(&x->bydst, xfrm_state_bydst+h); 770667bbcb6SMasahide NAKAMURA h = xfrm_src_hash(daddr, saddr, family); 7718f126e37SDavid S. Miller hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 7722770834cSDavid S. Miller wake_up(&km_waitq); 773918049f0SDavid S. Miller 774918049f0SDavid S. Miller xfrm_state_num++; 775918049f0SDavid S. Miller 776918049f0SDavid S. Miller xfrm_hash_grow_check(x->bydst.next != NULL); 7772770834cSDavid S. Miller } 7782770834cSDavid S. Miller 7792770834cSDavid S. Miller return x; 7802770834cSDavid S. Miller } 7812770834cSDavid S. Miller 7821da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x) 7851da177e4SLinus Torvalds { 7861da177e4SLinus Torvalds struct xfrm_state *x1; 7871da177e4SLinus Torvalds int family; 7881da177e4SLinus Torvalds int err; 789eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 7901da177e4SLinus Torvalds 7911da177e4SLinus Torvalds family = x->props.family; 7921da177e4SLinus Torvalds 7931da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7941da177e4SLinus Torvalds 795edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, family); 7961da177e4SLinus Torvalds if (x1) { 7971da177e4SLinus Torvalds xfrm_state_put(x1); 7981da177e4SLinus Torvalds x1 = NULL; 7991da177e4SLinus Torvalds err = -EEXIST; 8001da177e4SLinus Torvalds goto out; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 803eb2971b6SMasahide NAKAMURA if (use_spi && x->km.seq) { 8041da177e4SLinus Torvalds x1 = __xfrm_find_acq_byseq(x->km.seq); 80575e252d9SJoy Latten if (x1 && ((x1->id.proto != x->id.proto) || 80675e252d9SJoy Latten xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) { 8071da177e4SLinus Torvalds xfrm_state_put(x1); 8081da177e4SLinus Torvalds x1 = NULL; 8091da177e4SLinus Torvalds } 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 812eb2971b6SMasahide NAKAMURA if (use_spi && !x1) 8132770834cSDavid S. Miller x1 = __find_acq_core(family, x->props.mode, x->props.reqid, 8142770834cSDavid S. Miller x->id.proto, 8151da177e4SLinus Torvalds &x->id.daddr, &x->props.saddr, 0); 8161da177e4SLinus Torvalds 817c7f5ea3aSDavid S. Miller __xfrm_state_bump_genids(x); 8181da177e4SLinus Torvalds __xfrm_state_insert(x); 8191da177e4SLinus Torvalds err = 0; 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds out: 8221da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 8231da177e4SLinus Torvalds 8241da177e4SLinus Torvalds if (x1) { 8251da177e4SLinus Torvalds xfrm_state_delete(x1); 8261da177e4SLinus Torvalds xfrm_state_put(x1); 8271da177e4SLinus Torvalds } 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds return err; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add); 8321da177e4SLinus Torvalds 83380c9abaaSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 83480c9abaaSShinta Sugimoto struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) 83580c9abaaSShinta Sugimoto { 83680c9abaaSShinta Sugimoto int err = -ENOMEM; 83780c9abaaSShinta Sugimoto struct xfrm_state *x = xfrm_state_alloc(); 83880c9abaaSShinta Sugimoto if (!x) 83980c9abaaSShinta Sugimoto goto error; 84080c9abaaSShinta Sugimoto 84180c9abaaSShinta Sugimoto memcpy(&x->id, &orig->id, sizeof(x->id)); 84280c9abaaSShinta Sugimoto memcpy(&x->sel, &orig->sel, sizeof(x->sel)); 84380c9abaaSShinta Sugimoto memcpy(&x->lft, &orig->lft, sizeof(x->lft)); 84480c9abaaSShinta Sugimoto x->props.mode = orig->props.mode; 84580c9abaaSShinta Sugimoto x->props.replay_window = orig->props.replay_window; 84680c9abaaSShinta Sugimoto x->props.reqid = orig->props.reqid; 84780c9abaaSShinta Sugimoto x->props.family = orig->props.family; 84880c9abaaSShinta Sugimoto x->props.saddr = orig->props.saddr; 84980c9abaaSShinta Sugimoto 85080c9abaaSShinta Sugimoto if (orig->aalg) { 85180c9abaaSShinta Sugimoto x->aalg = xfrm_algo_clone(orig->aalg); 85280c9abaaSShinta Sugimoto if (!x->aalg) 85380c9abaaSShinta Sugimoto goto error; 85480c9abaaSShinta Sugimoto } 85580c9abaaSShinta Sugimoto x->props.aalgo = orig->props.aalgo; 85680c9abaaSShinta Sugimoto 85780c9abaaSShinta Sugimoto if (orig->ealg) { 85880c9abaaSShinta Sugimoto x->ealg = xfrm_algo_clone(orig->ealg); 85980c9abaaSShinta Sugimoto if (!x->ealg) 86080c9abaaSShinta Sugimoto goto error; 86180c9abaaSShinta Sugimoto } 86280c9abaaSShinta Sugimoto x->props.ealgo = orig->props.ealgo; 86380c9abaaSShinta Sugimoto 86480c9abaaSShinta Sugimoto if (orig->calg) { 86580c9abaaSShinta Sugimoto x->calg = xfrm_algo_clone(orig->calg); 86680c9abaaSShinta Sugimoto if (!x->calg) 86780c9abaaSShinta Sugimoto goto error; 86880c9abaaSShinta Sugimoto } 86980c9abaaSShinta Sugimoto x->props.calgo = orig->props.calgo; 87080c9abaaSShinta Sugimoto 87180c9abaaSShinta Sugimoto if (orig->encap) { 87280c9abaaSShinta Sugimoto x->encap = kmemdup(orig->encap, sizeof(*x->encap), GFP_KERNEL); 87380c9abaaSShinta Sugimoto if (!x->encap) 87480c9abaaSShinta Sugimoto goto error; 87580c9abaaSShinta Sugimoto } 87680c9abaaSShinta Sugimoto 87780c9abaaSShinta Sugimoto if (orig->coaddr) { 87880c9abaaSShinta Sugimoto x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr), 87980c9abaaSShinta Sugimoto GFP_KERNEL); 88080c9abaaSShinta Sugimoto if (!x->coaddr) 88180c9abaaSShinta Sugimoto goto error; 88280c9abaaSShinta Sugimoto } 88380c9abaaSShinta Sugimoto 88480c9abaaSShinta Sugimoto err = xfrm_init_state(x); 88580c9abaaSShinta Sugimoto if (err) 88680c9abaaSShinta Sugimoto goto error; 88780c9abaaSShinta Sugimoto 88880c9abaaSShinta Sugimoto x->props.flags = orig->props.flags; 88980c9abaaSShinta Sugimoto 89080c9abaaSShinta Sugimoto x->curlft.add_time = orig->curlft.add_time; 89180c9abaaSShinta Sugimoto x->km.state = orig->km.state; 89280c9abaaSShinta Sugimoto x->km.seq = orig->km.seq; 89380c9abaaSShinta Sugimoto 89480c9abaaSShinta Sugimoto return x; 89580c9abaaSShinta Sugimoto 89680c9abaaSShinta Sugimoto error: 89780c9abaaSShinta Sugimoto if (errp) 89880c9abaaSShinta Sugimoto *errp = err; 89980c9abaaSShinta Sugimoto if (x) { 90080c9abaaSShinta Sugimoto kfree(x->aalg); 90180c9abaaSShinta Sugimoto kfree(x->ealg); 90280c9abaaSShinta Sugimoto kfree(x->calg); 90380c9abaaSShinta Sugimoto kfree(x->encap); 90480c9abaaSShinta Sugimoto kfree(x->coaddr); 90580c9abaaSShinta Sugimoto } 90680c9abaaSShinta Sugimoto kfree(x); 90780c9abaaSShinta Sugimoto return NULL; 90880c9abaaSShinta Sugimoto } 90980c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_clone); 91080c9abaaSShinta Sugimoto 91180c9abaaSShinta Sugimoto /* xfrm_state_lock is held */ 91280c9abaaSShinta Sugimoto struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) 91380c9abaaSShinta Sugimoto { 91480c9abaaSShinta Sugimoto unsigned int h; 91580c9abaaSShinta Sugimoto struct xfrm_state *x; 91680c9abaaSShinta Sugimoto struct hlist_node *entry; 91780c9abaaSShinta Sugimoto 91880c9abaaSShinta Sugimoto if (m->reqid) { 91980c9abaaSShinta Sugimoto h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr, 92080c9abaaSShinta Sugimoto m->reqid, m->old_family); 92180c9abaaSShinta Sugimoto hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { 92280c9abaaSShinta Sugimoto if (x->props.mode != m->mode || 92380c9abaaSShinta Sugimoto x->id.proto != m->proto) 92480c9abaaSShinta Sugimoto continue; 92580c9abaaSShinta Sugimoto if (m->reqid && x->props.reqid != m->reqid) 92680c9abaaSShinta Sugimoto continue; 92780c9abaaSShinta Sugimoto if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr, 92880c9abaaSShinta Sugimoto m->old_family) || 92980c9abaaSShinta Sugimoto xfrm_addr_cmp(&x->props.saddr, &m->old_saddr, 93080c9abaaSShinta Sugimoto m->old_family)) 93180c9abaaSShinta Sugimoto continue; 93280c9abaaSShinta Sugimoto xfrm_state_hold(x); 93380c9abaaSShinta Sugimoto return x; 93480c9abaaSShinta Sugimoto } 93580c9abaaSShinta Sugimoto } else { 93680c9abaaSShinta Sugimoto h = xfrm_src_hash(&m->old_daddr, &m->old_saddr, 93780c9abaaSShinta Sugimoto m->old_family); 93880c9abaaSShinta Sugimoto hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) { 93980c9abaaSShinta Sugimoto if (x->props.mode != m->mode || 94080c9abaaSShinta Sugimoto x->id.proto != m->proto) 94180c9abaaSShinta Sugimoto continue; 94280c9abaaSShinta Sugimoto if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr, 94380c9abaaSShinta Sugimoto m->old_family) || 94480c9abaaSShinta Sugimoto xfrm_addr_cmp(&x->props.saddr, &m->old_saddr, 94580c9abaaSShinta Sugimoto m->old_family)) 94680c9abaaSShinta Sugimoto continue; 94780c9abaaSShinta Sugimoto xfrm_state_hold(x); 94880c9abaaSShinta Sugimoto return x; 94980c9abaaSShinta Sugimoto } 95080c9abaaSShinta Sugimoto } 95180c9abaaSShinta Sugimoto 95280c9abaaSShinta Sugimoto return NULL; 95380c9abaaSShinta Sugimoto } 95480c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_migrate_state_find); 95580c9abaaSShinta Sugimoto 95680c9abaaSShinta Sugimoto struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, 95780c9abaaSShinta Sugimoto struct xfrm_migrate *m) 95880c9abaaSShinta Sugimoto { 95980c9abaaSShinta Sugimoto struct xfrm_state *xc; 96080c9abaaSShinta Sugimoto int err; 96180c9abaaSShinta Sugimoto 96280c9abaaSShinta Sugimoto xc = xfrm_state_clone(x, &err); 96380c9abaaSShinta Sugimoto if (!xc) 96480c9abaaSShinta Sugimoto return NULL; 96580c9abaaSShinta Sugimoto 96680c9abaaSShinta Sugimoto memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr)); 96780c9abaaSShinta Sugimoto memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr)); 96880c9abaaSShinta Sugimoto 96980c9abaaSShinta Sugimoto /* add state */ 97080c9abaaSShinta Sugimoto if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) { 97180c9abaaSShinta Sugimoto /* a care is needed when the destination address of the 97280c9abaaSShinta Sugimoto state is to be updated as it is a part of triplet */ 97380c9abaaSShinta Sugimoto xfrm_state_insert(xc); 97480c9abaaSShinta Sugimoto } else { 97580c9abaaSShinta Sugimoto if ((err = xfrm_state_add(xc)) < 0) 97680c9abaaSShinta Sugimoto goto error; 97780c9abaaSShinta Sugimoto } 97880c9abaaSShinta Sugimoto 97980c9abaaSShinta Sugimoto return xc; 98080c9abaaSShinta Sugimoto error: 98180c9abaaSShinta Sugimoto kfree(xc); 98280c9abaaSShinta Sugimoto return NULL; 98380c9abaaSShinta Sugimoto } 98480c9abaaSShinta Sugimoto EXPORT_SYMBOL(xfrm_state_migrate); 98580c9abaaSShinta Sugimoto #endif 98680c9abaaSShinta Sugimoto 9871da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x) 9881da177e4SLinus Torvalds { 9891da177e4SLinus Torvalds struct xfrm_state *x1; 9901da177e4SLinus Torvalds int err; 991eb2971b6SMasahide NAKAMURA int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 994edcd5821SDavid S. Miller x1 = __xfrm_state_locate(x, use_spi, x->props.family); 9951da177e4SLinus Torvalds 9961da177e4SLinus Torvalds err = -ESRCH; 9971da177e4SLinus Torvalds if (!x1) 9981da177e4SLinus Torvalds goto out; 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds if (xfrm_state_kern(x1)) { 10011da177e4SLinus Torvalds xfrm_state_put(x1); 10021da177e4SLinus Torvalds err = -EEXIST; 10031da177e4SLinus Torvalds goto out; 10041da177e4SLinus Torvalds } 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds if (x1->km.state == XFRM_STATE_ACQ) { 10071da177e4SLinus Torvalds __xfrm_state_insert(x); 10081da177e4SLinus Torvalds x = NULL; 10091da177e4SLinus Torvalds } 10101da177e4SLinus Torvalds err = 0; 10111da177e4SLinus Torvalds 10121da177e4SLinus Torvalds out: 10131da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds if (err) 10161da177e4SLinus Torvalds return err; 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds if (!x) { 10191da177e4SLinus Torvalds xfrm_state_delete(x1); 10201da177e4SLinus Torvalds xfrm_state_put(x1); 10211da177e4SLinus Torvalds return 0; 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds err = -EINVAL; 10251da177e4SLinus Torvalds spin_lock_bh(&x1->lock); 10261da177e4SLinus Torvalds if (likely(x1->km.state == XFRM_STATE_VALID)) { 10271da177e4SLinus Torvalds if (x->encap && x1->encap) 10281da177e4SLinus Torvalds memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 1029060f02a3SNoriaki TAKAMIYA if (x->coaddr && x1->coaddr) { 1030060f02a3SNoriaki TAKAMIYA memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr)); 1031060f02a3SNoriaki TAKAMIYA } 1032060f02a3SNoriaki TAKAMIYA if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel))) 1033060f02a3SNoriaki TAKAMIYA memcpy(&x1->sel, &x->sel, sizeof(x1->sel)); 10341da177e4SLinus Torvalds memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 10351da177e4SLinus Torvalds x1->km.dying = 0; 10361da177e4SLinus Torvalds 1037a47f0ce0SDavid S. Miller mod_timer(&x1->timer, jiffies + HZ); 10381da177e4SLinus Torvalds if (x1->curlft.use_time) 10391da177e4SLinus Torvalds xfrm_state_check_expire(x1); 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds err = 0; 10421da177e4SLinus Torvalds } 10431da177e4SLinus Torvalds spin_unlock_bh(&x1->lock); 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds xfrm_state_put(x1); 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds return err; 10481da177e4SLinus Torvalds } 10491da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update); 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x) 10521da177e4SLinus Torvalds { 10531da177e4SLinus Torvalds if (!x->curlft.use_time) 10541da177e4SLinus Torvalds x->curlft.use_time = (unsigned long)xtime.tv_sec; 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_VALID) 10571da177e4SLinus Torvalds return -EINVAL; 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds if (x->curlft.bytes >= x->lft.hard_byte_limit || 10601da177e4SLinus Torvalds x->curlft.packets >= x->lft.hard_packet_limit) { 10614666faabSHerbert Xu x->km.state = XFRM_STATE_EXPIRED; 1062a47f0ce0SDavid S. Miller mod_timer(&x->timer, jiffies); 10631da177e4SLinus Torvalds return -EINVAL; 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds if (!x->km.dying && 10671da177e4SLinus Torvalds (x->curlft.bytes >= x->lft.soft_byte_limit || 10684666faabSHerbert Xu x->curlft.packets >= x->lft.soft_packet_limit)) { 10694666faabSHerbert Xu x->km.dying = 1; 107053bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 10714666faabSHerbert Xu } 10721da177e4SLinus Torvalds return 0; 10731da177e4SLinus Torvalds } 10741da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire); 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 10771da177e4SLinus Torvalds { 10781da177e4SLinus Torvalds int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 10791da177e4SLinus Torvalds - skb_headroom(skb); 10801da177e4SLinus Torvalds 10811da177e4SLinus Torvalds if (nhead > 0) 10821da177e4SLinus Torvalds return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds /* Check tail too... */ 10851da177e4SLinus Torvalds return 0; 10861da177e4SLinus Torvalds } 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 10891da177e4SLinus Torvalds { 10901da177e4SLinus Torvalds int err = xfrm_state_check_expire(x); 10911da177e4SLinus Torvalds if (err < 0) 10921da177e4SLinus Torvalds goto err; 10931da177e4SLinus Torvalds err = xfrm_state_check_space(x, skb); 10941da177e4SLinus Torvalds err: 10951da177e4SLinus Torvalds return err; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check); 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds struct xfrm_state * 1100a94cfd19SAl Viro xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, 11011da177e4SLinus Torvalds unsigned short family) 11021da177e4SLinus Torvalds { 11031da177e4SLinus Torvalds struct xfrm_state *x; 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 1106edcd5821SDavid S. Miller x = __xfrm_state_lookup(daddr, spi, proto, family); 11071da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11081da177e4SLinus Torvalds return x; 11091da177e4SLinus Torvalds } 11101da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup); 11111da177e4SLinus Torvalds 11121da177e4SLinus Torvalds struct xfrm_state * 1113eb2971b6SMasahide NAKAMURA xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, 1114eb2971b6SMasahide NAKAMURA u8 proto, unsigned short family) 1115eb2971b6SMasahide NAKAMURA { 1116eb2971b6SMasahide NAKAMURA struct xfrm_state *x; 1117eb2971b6SMasahide NAKAMURA 1118eb2971b6SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 1119edcd5821SDavid S. Miller x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family); 1120eb2971b6SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 1121eb2971b6SMasahide NAKAMURA return x; 1122eb2971b6SMasahide NAKAMURA } 1123eb2971b6SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_lookup_byaddr); 1124eb2971b6SMasahide NAKAMURA 1125eb2971b6SMasahide NAKAMURA struct xfrm_state * 11261da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto, 11271da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 11281da177e4SLinus Torvalds int create, unsigned short family) 11291da177e4SLinus Torvalds { 11301da177e4SLinus Torvalds struct xfrm_state *x; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 11332770834cSDavid S. Miller x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create); 11341da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 11352770834cSDavid S. Miller 11361da177e4SLinus Torvalds return x; 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq); 11391da177e4SLinus Torvalds 114041a49cc3SMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 114141a49cc3SMasahide NAKAMURA int 114241a49cc3SMasahide NAKAMURA xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, 114341a49cc3SMasahide NAKAMURA unsigned short family) 114441a49cc3SMasahide NAKAMURA { 114541a49cc3SMasahide NAKAMURA int err = 0; 114641a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 114741a49cc3SMasahide NAKAMURA if (!afinfo) 114841a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 114941a49cc3SMasahide NAKAMURA 115041a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 115141a49cc3SMasahide NAKAMURA if (afinfo->tmpl_sort) 115241a49cc3SMasahide NAKAMURA err = afinfo->tmpl_sort(dst, src, n); 115341a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 115441a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 115541a49cc3SMasahide NAKAMURA return err; 115641a49cc3SMasahide NAKAMURA } 115741a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_tmpl_sort); 115841a49cc3SMasahide NAKAMURA 115941a49cc3SMasahide NAKAMURA int 116041a49cc3SMasahide NAKAMURA xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, 116141a49cc3SMasahide NAKAMURA unsigned short family) 116241a49cc3SMasahide NAKAMURA { 116341a49cc3SMasahide NAKAMURA int err = 0; 116441a49cc3SMasahide NAKAMURA struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 116541a49cc3SMasahide NAKAMURA if (!afinfo) 116641a49cc3SMasahide NAKAMURA return -EAFNOSUPPORT; 116741a49cc3SMasahide NAKAMURA 116841a49cc3SMasahide NAKAMURA spin_lock_bh(&xfrm_state_lock); 116941a49cc3SMasahide NAKAMURA if (afinfo->state_sort) 117041a49cc3SMasahide NAKAMURA err = afinfo->state_sort(dst, src, n); 117141a49cc3SMasahide NAKAMURA spin_unlock_bh(&xfrm_state_lock); 117241a49cc3SMasahide NAKAMURA xfrm_state_put_afinfo(afinfo); 117341a49cc3SMasahide NAKAMURA return err; 117441a49cc3SMasahide NAKAMURA } 117541a49cc3SMasahide NAKAMURA EXPORT_SYMBOL(xfrm_state_sort); 117641a49cc3SMasahide NAKAMURA #endif 117741a49cc3SMasahide NAKAMURA 11781da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */ 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq) 11811da177e4SLinus Torvalds { 11821da177e4SLinus Torvalds int i; 11831da177e4SLinus Torvalds 1184f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 11858f126e37SDavid S. Miller struct hlist_node *entry; 11868f126e37SDavid S. Miller struct xfrm_state *x; 11878f126e37SDavid S. Miller 11888f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 11898f126e37SDavid S. Miller if (x->km.seq == seq && 11908f126e37SDavid S. Miller x->km.state == XFRM_STATE_ACQ) { 11911da177e4SLinus Torvalds xfrm_state_hold(x); 11921da177e4SLinus Torvalds return x; 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds } 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds return NULL; 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds struct xfrm_state *x; 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 12041da177e4SLinus Torvalds x = __xfrm_find_acq_byseq(seq); 12051da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 12061da177e4SLinus Torvalds return x; 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq); 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds u32 xfrm_get_acqseq(void) 12111da177e4SLinus Torvalds { 12121da177e4SLinus Torvalds u32 res; 12131da177e4SLinus Torvalds static u32 acqseq; 12141da177e4SLinus Torvalds static DEFINE_SPINLOCK(acqseq_lock); 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds spin_lock_bh(&acqseq_lock); 12171da177e4SLinus Torvalds res = (++acqseq ? : ++acqseq); 12181da177e4SLinus Torvalds spin_unlock_bh(&acqseq_lock); 12191da177e4SLinus Torvalds return res; 12201da177e4SLinus Torvalds } 12211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq); 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds void 122426977b4eSAl Viro xfrm_alloc_spi(struct xfrm_state *x, __be32 minspi, __be32 maxspi) 12251da177e4SLinus Torvalds { 1226f034b5d4SDavid S. Miller unsigned int h; 12271da177e4SLinus Torvalds struct xfrm_state *x0; 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds if (x->id.spi) 12301da177e4SLinus Torvalds return; 12311da177e4SLinus Torvalds 12321da177e4SLinus Torvalds if (minspi == maxspi) { 12331da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); 12341da177e4SLinus Torvalds if (x0) { 12351da177e4SLinus Torvalds xfrm_state_put(x0); 12361da177e4SLinus Torvalds return; 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds x->id.spi = minspi; 12391da177e4SLinus Torvalds } else { 12401da177e4SLinus Torvalds u32 spi = 0; 124126977b4eSAl Viro u32 low = ntohl(minspi); 124226977b4eSAl Viro u32 high = ntohl(maxspi); 124326977b4eSAl Viro for (h=0; h<high-low+1; h++) { 124426977b4eSAl Viro spi = low + net_random()%(high-low+1); 12451da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family); 12461da177e4SLinus Torvalds if (x0 == NULL) { 12471da177e4SLinus Torvalds x->id.spi = htonl(spi); 12481da177e4SLinus Torvalds break; 12491da177e4SLinus Torvalds } 12501da177e4SLinus Torvalds xfrm_state_put(x0); 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds if (x->id.spi) { 12541da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 12551da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 12568f126e37SDavid S. Miller hlist_add_head(&x->byspi, xfrm_state_byspi+h); 12571da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 12581da177e4SLinus Torvalds wake_up(&km_waitq); 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds } 12611da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi); 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), 12641da177e4SLinus Torvalds void *data) 12651da177e4SLinus Torvalds { 12661da177e4SLinus Torvalds int i; 126794b9bb54SJamal Hadi Salim struct xfrm_state *x, *last = NULL; 12688f126e37SDavid S. Miller struct hlist_node *entry; 12691da177e4SLinus Torvalds int count = 0; 12701da177e4SLinus Torvalds int err = 0; 12711da177e4SLinus Torvalds 12721da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 1273f034b5d4SDavid S. Miller for (i = 0; i <= xfrm_state_hmask; i++) { 12748f126e37SDavid S. Miller hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { 127594b9bb54SJamal Hadi Salim if (!xfrm_id_proto_match(x->id.proto, proto)) 127694b9bb54SJamal Hadi Salim continue; 127794b9bb54SJamal Hadi Salim if (last) { 127894b9bb54SJamal Hadi Salim err = func(last, count, data); 127994b9bb54SJamal Hadi Salim if (err) 128094b9bb54SJamal Hadi Salim goto out; 128194b9bb54SJamal Hadi Salim } 128294b9bb54SJamal Hadi Salim last = x; 12831da177e4SLinus Torvalds count++; 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds if (count == 0) { 12871da177e4SLinus Torvalds err = -ENOENT; 12881da177e4SLinus Torvalds goto out; 12891da177e4SLinus Torvalds } 129094b9bb54SJamal Hadi Salim err = func(last, 0, data); 12911da177e4SLinus Torvalds out: 12921da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 12931da177e4SLinus Torvalds return err; 12941da177e4SLinus Torvalds } 12951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk); 12961da177e4SLinus Torvalds 1297f8cd5488SJamal Hadi Salim 1298f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event) 1299f8cd5488SJamal Hadi Salim { 1300f8cd5488SJamal Hadi Salim struct km_event c; 1301f8cd5488SJamal Hadi Salim /* we send notify messages in case 1302f8cd5488SJamal Hadi Salim * 1. we updated on of the sequence numbers, and the seqno difference 1303f8cd5488SJamal Hadi Salim * is at least x->replay_maxdiff, in this case we also update the 1304f8cd5488SJamal Hadi Salim * timeout of our timer function 1305f8cd5488SJamal Hadi Salim * 2. if x->replay_maxage has elapsed since last update, 1306f8cd5488SJamal Hadi Salim * and there were changes 1307f8cd5488SJamal Hadi Salim * 1308f8cd5488SJamal Hadi Salim * The state structure must be locked! 1309f8cd5488SJamal Hadi Salim */ 1310f8cd5488SJamal Hadi Salim 1311f8cd5488SJamal Hadi Salim switch (event) { 1312f8cd5488SJamal Hadi Salim case XFRM_REPLAY_UPDATE: 1313f8cd5488SJamal Hadi Salim if (x->replay_maxdiff && 1314f8cd5488SJamal Hadi Salim (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 13152717096aSJamal Hadi Salim (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { 13162717096aSJamal Hadi Salim if (x->xflags & XFRM_TIME_DEFER) 13172717096aSJamal Hadi Salim event = XFRM_REPLAY_TIMEOUT; 13182717096aSJamal Hadi Salim else 1319f8cd5488SJamal Hadi Salim return; 13202717096aSJamal Hadi Salim } 1321f8cd5488SJamal Hadi Salim 1322f8cd5488SJamal Hadi Salim break; 1323f8cd5488SJamal Hadi Salim 1324f8cd5488SJamal Hadi Salim case XFRM_REPLAY_TIMEOUT: 1325f8cd5488SJamal Hadi Salim if ((x->replay.seq == x->preplay.seq) && 1326f8cd5488SJamal Hadi Salim (x->replay.bitmap == x->preplay.bitmap) && 13272717096aSJamal Hadi Salim (x->replay.oseq == x->preplay.oseq)) { 13282717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 1329f8cd5488SJamal Hadi Salim return; 13302717096aSJamal Hadi Salim } 1331f8cd5488SJamal Hadi Salim 1332f8cd5488SJamal Hadi Salim break; 1333f8cd5488SJamal Hadi Salim } 1334f8cd5488SJamal Hadi Salim 1335f8cd5488SJamal Hadi Salim memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 1336f8cd5488SJamal Hadi Salim c.event = XFRM_MSG_NEWAE; 1337f8cd5488SJamal Hadi Salim c.data.aevent = event; 1338f8cd5488SJamal Hadi Salim km_state_notify(x, &c); 1339f8cd5488SJamal Hadi Salim 1340f8cd5488SJamal Hadi Salim if (x->replay_maxage && 1341a47f0ce0SDavid S. Miller !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 13422717096aSJamal Hadi Salim x->xflags &= ~XFRM_TIME_DEFER; 13432717096aSJamal Hadi Salim } 1344a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify); 1345f8cd5488SJamal Hadi Salim 1346f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data) 1347f8cd5488SJamal Hadi Salim { 1348f8cd5488SJamal Hadi Salim struct xfrm_state *x = (struct xfrm_state*)data; 1349f8cd5488SJamal Hadi Salim 1350f8cd5488SJamal Hadi Salim spin_lock(&x->lock); 1351f8cd5488SJamal Hadi Salim 13522717096aSJamal Hadi Salim if (x->km.state == XFRM_STATE_VALID) { 13532717096aSJamal Hadi Salim if (xfrm_aevent_is_on()) 1354f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); 13552717096aSJamal Hadi Salim else 13562717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 13572717096aSJamal Hadi Salim } 1358f8cd5488SJamal Hadi Salim 1359f8cd5488SJamal Hadi Salim spin_unlock(&x->lock); 1360f8cd5488SJamal Hadi Salim } 1361f8cd5488SJamal Hadi Salim 1362a252cc23SAl Viro int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) 13631da177e4SLinus Torvalds { 13641da177e4SLinus Torvalds u32 diff; 1365a252cc23SAl Viro u32 seq = ntohl(net_seq); 13661da177e4SLinus Torvalds 13671da177e4SLinus Torvalds if (unlikely(seq == 0)) 13681da177e4SLinus Torvalds return -EINVAL; 13691da177e4SLinus Torvalds 13701da177e4SLinus Torvalds if (likely(seq > x->replay.seq)) 13711da177e4SLinus Torvalds return 0; 13721da177e4SLinus Torvalds 13731da177e4SLinus Torvalds diff = x->replay.seq - seq; 13744c4d51a7SHerbert Xu if (diff >= min_t(unsigned int, x->props.replay_window, 13754c4d51a7SHerbert Xu sizeof(x->replay.bitmap) * 8)) { 13761da177e4SLinus Torvalds x->stats.replay_window++; 13771da177e4SLinus Torvalds return -EINVAL; 13781da177e4SLinus Torvalds } 13791da177e4SLinus Torvalds 13801da177e4SLinus Torvalds if (x->replay.bitmap & (1U << diff)) { 13811da177e4SLinus Torvalds x->stats.replay++; 13821da177e4SLinus Torvalds return -EINVAL; 13831da177e4SLinus Torvalds } 13841da177e4SLinus Torvalds return 0; 13851da177e4SLinus Torvalds } 13861da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check); 13871da177e4SLinus Torvalds 138861f4627bSAl Viro void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) 13891da177e4SLinus Torvalds { 13901da177e4SLinus Torvalds u32 diff; 139161f4627bSAl Viro u32 seq = ntohl(net_seq); 13921da177e4SLinus Torvalds 13931da177e4SLinus Torvalds if (seq > x->replay.seq) { 13941da177e4SLinus Torvalds diff = seq - x->replay.seq; 13951da177e4SLinus Torvalds if (diff < x->props.replay_window) 13961da177e4SLinus Torvalds x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 13971da177e4SLinus Torvalds else 13981da177e4SLinus Torvalds x->replay.bitmap = 1; 13991da177e4SLinus Torvalds x->replay.seq = seq; 14001da177e4SLinus Torvalds } else { 14011da177e4SLinus Torvalds diff = x->replay.seq - seq; 14021da177e4SLinus Torvalds x->replay.bitmap |= (1U << diff); 14031da177e4SLinus Torvalds } 1404f8cd5488SJamal Hadi Salim 1405f8cd5488SJamal Hadi Salim if (xfrm_aevent_is_on()) 1406f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 14071da177e4SLinus Torvalds } 14081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance); 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); 14111da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock); 14121da177e4SLinus Torvalds 141326b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 14141da177e4SLinus Torvalds { 14151da177e4SLinus Torvalds struct xfrm_mgr *km; 14161da177e4SLinus Torvalds 141726b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 141826b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 141926b15dadSJamal Hadi Salim if (km->notify_policy) 142026b15dadSJamal Hadi Salim km->notify_policy(xp, dir, c); 142126b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 142226b15dadSJamal Hadi Salim } 142326b15dadSJamal Hadi Salim 142426b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c) 142526b15dadSJamal Hadi Salim { 142626b15dadSJamal Hadi Salim struct xfrm_mgr *km; 142726b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 142826b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 142926b15dadSJamal Hadi Salim if (km->notify) 143026b15dadSJamal Hadi Salim km->notify(x, c); 143126b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 143226b15dadSJamal Hadi Salim } 143326b15dadSJamal Hadi Salim 143426b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify); 143526b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify); 143626b15dadSJamal Hadi Salim 143753bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid) 143826b15dadSJamal Hadi Salim { 143926b15dadSJamal Hadi Salim struct km_event c; 144026b15dadSJamal Hadi Salim 1441bf08867fSHerbert Xu c.data.hard = hard; 144253bc6b4dSJamal Hadi Salim c.pid = pid; 1443f60f6b8fSHerbert Xu c.event = XFRM_MSG_EXPIRE; 144426b15dadSJamal Hadi Salim km_state_notify(x, &c); 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds if (hard) 14471da177e4SLinus Torvalds wake_up(&km_waitq); 14481da177e4SLinus Torvalds } 14491da177e4SLinus Torvalds 145053bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired); 145126b15dadSJamal Hadi Salim /* 145226b15dadSJamal Hadi Salim * We send to all registered managers regardless of failure 145326b15dadSJamal Hadi Salim * We are happy with one success 145426b15dadSJamal Hadi Salim */ 1455980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 14561da177e4SLinus Torvalds { 145726b15dadSJamal Hadi Salim int err = -EINVAL, acqret; 14581da177e4SLinus Torvalds struct xfrm_mgr *km; 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 14611da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 146226b15dadSJamal Hadi Salim acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); 146326b15dadSJamal Hadi Salim if (!acqret) 146426b15dadSJamal Hadi Salim err = acqret; 14651da177e4SLinus Torvalds } 14661da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 14671da177e4SLinus Torvalds return err; 14681da177e4SLinus Torvalds } 1469980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query); 14701da177e4SLinus Torvalds 14715d36b180SAl Viro int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) 14721da177e4SLinus Torvalds { 14731da177e4SLinus Torvalds int err = -EINVAL; 14741da177e4SLinus Torvalds struct xfrm_mgr *km; 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 14771da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 14781da177e4SLinus Torvalds if (km->new_mapping) 14791da177e4SLinus Torvalds err = km->new_mapping(x, ipaddr, sport); 14801da177e4SLinus Torvalds if (!err) 14811da177e4SLinus Torvalds break; 14821da177e4SLinus Torvalds } 14831da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 14841da177e4SLinus Torvalds return err; 14851da177e4SLinus Torvalds } 14861da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping); 14871da177e4SLinus Torvalds 14886c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) 14891da177e4SLinus Torvalds { 149026b15dadSJamal Hadi Salim struct km_event c; 14911da177e4SLinus Torvalds 1492bf08867fSHerbert Xu c.data.hard = hard; 14936c5c8ca7SJamal Hadi Salim c.pid = pid; 1494f60f6b8fSHerbert Xu c.event = XFRM_MSG_POLEXPIRE; 149526b15dadSJamal Hadi Salim km_policy_notify(pol, dir, &c); 14961da177e4SLinus Torvalds 14971da177e4SLinus Torvalds if (hard) 14981da177e4SLinus Torvalds wake_up(&km_waitq); 14991da177e4SLinus Torvalds } 1500a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired); 15011da177e4SLinus Torvalds 150280c9abaaSShinta Sugimoto int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 150380c9abaaSShinta Sugimoto struct xfrm_migrate *m, int num_migrate) 150480c9abaaSShinta Sugimoto { 150580c9abaaSShinta Sugimoto int err = -EINVAL; 150680c9abaaSShinta Sugimoto int ret; 150780c9abaaSShinta Sugimoto struct xfrm_mgr *km; 150880c9abaaSShinta Sugimoto 150980c9abaaSShinta Sugimoto read_lock(&xfrm_km_lock); 151080c9abaaSShinta Sugimoto list_for_each_entry(km, &xfrm_km_list, list) { 151180c9abaaSShinta Sugimoto if (km->migrate) { 151280c9abaaSShinta Sugimoto ret = km->migrate(sel, dir, type, m, num_migrate); 151380c9abaaSShinta Sugimoto if (!ret) 151480c9abaaSShinta Sugimoto err = ret; 151580c9abaaSShinta Sugimoto } 151680c9abaaSShinta Sugimoto } 151780c9abaaSShinta Sugimoto read_unlock(&xfrm_km_lock); 151880c9abaaSShinta Sugimoto return err; 151980c9abaaSShinta Sugimoto } 152080c9abaaSShinta Sugimoto EXPORT_SYMBOL(km_migrate); 152180c9abaaSShinta Sugimoto 152297a64b45SMasahide NAKAMURA int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) 152397a64b45SMasahide NAKAMURA { 152497a64b45SMasahide NAKAMURA int err = -EINVAL; 152597a64b45SMasahide NAKAMURA int ret; 152697a64b45SMasahide NAKAMURA struct xfrm_mgr *km; 152797a64b45SMasahide NAKAMURA 152897a64b45SMasahide NAKAMURA read_lock(&xfrm_km_lock); 152997a64b45SMasahide NAKAMURA list_for_each_entry(km, &xfrm_km_list, list) { 153097a64b45SMasahide NAKAMURA if (km->report) { 153197a64b45SMasahide NAKAMURA ret = km->report(proto, sel, addr); 153297a64b45SMasahide NAKAMURA if (!ret) 153397a64b45SMasahide NAKAMURA err = ret; 153497a64b45SMasahide NAKAMURA } 153597a64b45SMasahide NAKAMURA } 153697a64b45SMasahide NAKAMURA read_unlock(&xfrm_km_lock); 153797a64b45SMasahide NAKAMURA return err; 153897a64b45SMasahide NAKAMURA } 153997a64b45SMasahide NAKAMURA EXPORT_SYMBOL(km_report); 154097a64b45SMasahide NAKAMURA 15411da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 15421da177e4SLinus Torvalds { 15431da177e4SLinus Torvalds int err; 15441da177e4SLinus Torvalds u8 *data; 15451da177e4SLinus Torvalds struct xfrm_mgr *km; 15461da177e4SLinus Torvalds struct xfrm_policy *pol = NULL; 15471da177e4SLinus Torvalds 15481da177e4SLinus Torvalds if (optlen <= 0 || optlen > PAGE_SIZE) 15491da177e4SLinus Torvalds return -EMSGSIZE; 15501da177e4SLinus Torvalds 15511da177e4SLinus Torvalds data = kmalloc(optlen, GFP_KERNEL); 15521da177e4SLinus Torvalds if (!data) 15531da177e4SLinus Torvalds return -ENOMEM; 15541da177e4SLinus Torvalds 15551da177e4SLinus Torvalds err = -EFAULT; 15561da177e4SLinus Torvalds if (copy_from_user(data, optval, optlen)) 15571da177e4SLinus Torvalds goto out; 15581da177e4SLinus Torvalds 15591da177e4SLinus Torvalds err = -EINVAL; 15601da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 15611da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 1562cb969f07SVenkat Yekkirala pol = km->compile_policy(sk, optname, data, 15631da177e4SLinus Torvalds optlen, &err); 15641da177e4SLinus Torvalds if (err >= 0) 15651da177e4SLinus Torvalds break; 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 15681da177e4SLinus Torvalds 15691da177e4SLinus Torvalds if (err >= 0) { 15701da177e4SLinus Torvalds xfrm_sk_policy_insert(sk, err, pol); 15711da177e4SLinus Torvalds xfrm_pol_put(pol); 15721da177e4SLinus Torvalds err = 0; 15731da177e4SLinus Torvalds } 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds out: 15761da177e4SLinus Torvalds kfree(data); 15771da177e4SLinus Torvalds return err; 15781da177e4SLinus Torvalds } 15791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy); 15801da177e4SLinus Torvalds 15811da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km) 15821da177e4SLinus Torvalds { 15831da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 15841da177e4SLinus Torvalds list_add_tail(&km->list, &xfrm_km_list); 15851da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 15861da177e4SLinus Torvalds return 0; 15871da177e4SLinus Torvalds } 15881da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km); 15891da177e4SLinus Torvalds 15901da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km) 15911da177e4SLinus Torvalds { 15921da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 15931da177e4SLinus Torvalds list_del(&km->list); 15941da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 15951da177e4SLinus Torvalds return 0; 15961da177e4SLinus Torvalds } 15971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km); 15981da177e4SLinus Torvalds 15991da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 16001da177e4SLinus Torvalds { 16011da177e4SLinus Torvalds int err = 0; 16021da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 16031da177e4SLinus Torvalds return -EINVAL; 16041da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 16051da177e4SLinus Torvalds return -EAFNOSUPPORT; 1606f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 16071da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 16081da177e4SLinus Torvalds err = -ENOBUFS; 1609edcd5821SDavid S. Miller else 16101da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = afinfo; 1611f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 16121da177e4SLinus Torvalds return err; 16131da177e4SLinus Torvalds } 16141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo); 16151da177e4SLinus Torvalds 16161da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 16171da177e4SLinus Torvalds { 16181da177e4SLinus Torvalds int err = 0; 16191da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 16201da177e4SLinus Torvalds return -EINVAL; 16211da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 16221da177e4SLinus Torvalds return -EAFNOSUPPORT; 1623f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 16241da177e4SLinus Torvalds if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 16251da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) 16261da177e4SLinus Torvalds err = -EINVAL; 1627edcd5821SDavid S. Miller else 16281da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = NULL; 16291da177e4SLinus Torvalds } 1630f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 16311da177e4SLinus Torvalds return err; 16321da177e4SLinus Torvalds } 16331da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo); 16341da177e4SLinus Torvalds 1635cdca7265SMiika Komu struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) 16361da177e4SLinus Torvalds { 16371da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 16381da177e4SLinus Torvalds if (unlikely(family >= NPROTO)) 16391da177e4SLinus Torvalds return NULL; 16401da177e4SLinus Torvalds read_lock(&xfrm_state_afinfo_lock); 16411da177e4SLinus Torvalds afinfo = xfrm_state_afinfo[family]; 1642546be240SHerbert Xu if (unlikely(!afinfo)) 16431da177e4SLinus Torvalds read_unlock(&xfrm_state_afinfo_lock); 16441da177e4SLinus Torvalds return afinfo; 16451da177e4SLinus Torvalds } 16461da177e4SLinus Torvalds 1647cdca7265SMiika Komu void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) 16481da177e4SLinus Torvalds { 1649546be240SHerbert Xu read_unlock(&xfrm_state_afinfo_lock); 16501da177e4SLinus Torvalds } 16511da177e4SLinus Torvalds 1652cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_get_afinfo); 1653cdca7265SMiika Komu EXPORT_SYMBOL(xfrm_state_put_afinfo); 1654cdca7265SMiika Komu 16551da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 16561da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x) 16571da177e4SLinus Torvalds { 16581da177e4SLinus Torvalds if (x->tunnel) { 16591da177e4SLinus Torvalds struct xfrm_state *t = x->tunnel; 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds if (atomic_read(&t->tunnel_users) == 2) 16621da177e4SLinus Torvalds xfrm_state_delete(t); 16631da177e4SLinus Torvalds atomic_dec(&t->tunnel_users); 16641da177e4SLinus Torvalds xfrm_state_put(t); 16651da177e4SLinus Torvalds x->tunnel = NULL; 16661da177e4SLinus Torvalds } 16671da177e4SLinus Torvalds } 16681da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel); 16691da177e4SLinus Torvalds 167080b30c10SHerbert Xu /* 167180b30c10SHerbert Xu * This function is NOT optimal. For example, with ESP it will give an 167280b30c10SHerbert Xu * MTU that's usually two bytes short of being optimal. However, it will 167380b30c10SHerbert Xu * usually give an answer that's a multiple of 4 provided the input is 167480b30c10SHerbert Xu * also a multiple of 4. 167580b30c10SHerbert Xu */ 16761da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu) 16771da177e4SLinus Torvalds { 16781da177e4SLinus Torvalds int res = mtu; 16791da177e4SLinus Torvalds 16801da177e4SLinus Torvalds res -= x->props.header_len; 16811da177e4SLinus Torvalds 16821da177e4SLinus Torvalds for (;;) { 16831da177e4SLinus Torvalds int m = res; 16841da177e4SLinus Torvalds 16851da177e4SLinus Torvalds if (m < 68) 16861da177e4SLinus Torvalds return 68; 16871da177e4SLinus Torvalds 16881da177e4SLinus Torvalds spin_lock_bh(&x->lock); 16891da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID && 16901da177e4SLinus Torvalds x->type && x->type->get_max_size) 16911da177e4SLinus Torvalds m = x->type->get_max_size(x, m); 16921da177e4SLinus Torvalds else 16931da177e4SLinus Torvalds m += x->props.header_len; 16941da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds if (m <= mtu) 16971da177e4SLinus Torvalds break; 16981da177e4SLinus Torvalds res -= (m - mtu); 16991da177e4SLinus Torvalds } 17001da177e4SLinus Torvalds 17011da177e4SLinus Torvalds return res; 17021da177e4SLinus Torvalds } 17031da177e4SLinus Torvalds 170472cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x) 170572cb6962SHerbert Xu { 1706d094cd83SHerbert Xu struct xfrm_state_afinfo *afinfo; 1707d094cd83SHerbert Xu int family = x->props.family; 170872cb6962SHerbert Xu int err; 170972cb6962SHerbert Xu 1710d094cd83SHerbert Xu err = -EAFNOSUPPORT; 1711d094cd83SHerbert Xu afinfo = xfrm_state_get_afinfo(family); 1712d094cd83SHerbert Xu if (!afinfo) 1713d094cd83SHerbert Xu goto error; 1714d094cd83SHerbert Xu 1715d094cd83SHerbert Xu err = 0; 1716d094cd83SHerbert Xu if (afinfo->init_flags) 1717d094cd83SHerbert Xu err = afinfo->init_flags(x); 1718d094cd83SHerbert Xu 1719d094cd83SHerbert Xu xfrm_state_put_afinfo(afinfo); 1720d094cd83SHerbert Xu 1721d094cd83SHerbert Xu if (err) 1722d094cd83SHerbert Xu goto error; 1723d094cd83SHerbert Xu 1724d094cd83SHerbert Xu err = -EPROTONOSUPPORT; 1725d094cd83SHerbert Xu x->type = xfrm_get_type(x->id.proto, family); 172672cb6962SHerbert Xu if (x->type == NULL) 172772cb6962SHerbert Xu goto error; 172872cb6962SHerbert Xu 172972cb6962SHerbert Xu err = x->type->init_state(x); 173072cb6962SHerbert Xu if (err) 173172cb6962SHerbert Xu goto error; 173272cb6962SHerbert Xu 1733b59f45d0SHerbert Xu x->mode = xfrm_get_mode(x->props.mode, family); 1734b59f45d0SHerbert Xu if (x->mode == NULL) 1735b59f45d0SHerbert Xu goto error; 1736b59f45d0SHerbert Xu 173772cb6962SHerbert Xu x->km.state = XFRM_STATE_VALID; 173872cb6962SHerbert Xu 173972cb6962SHerbert Xu error: 174072cb6962SHerbert Xu return err; 174172cb6962SHerbert Xu } 174272cb6962SHerbert Xu 174372cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state); 17441da177e4SLinus Torvalds 17451da177e4SLinus Torvalds void __init xfrm_state_init(void) 17461da177e4SLinus Torvalds { 1747f034b5d4SDavid S. Miller unsigned int sz; 17481da177e4SLinus Torvalds 1749f034b5d4SDavid S. Miller sz = sizeof(struct hlist_head) * 8; 1750f034b5d4SDavid S. Miller 175144e36b42SDavid S. Miller xfrm_state_bydst = xfrm_hash_alloc(sz); 175244e36b42SDavid S. Miller xfrm_state_bysrc = xfrm_hash_alloc(sz); 175344e36b42SDavid S. Miller xfrm_state_byspi = xfrm_hash_alloc(sz); 1754f034b5d4SDavid S. Miller if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi) 1755f034b5d4SDavid S. Miller panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes."); 1756f034b5d4SDavid S. Miller xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1); 1757f034b5d4SDavid S. Miller 1758c4028958SDavid Howells INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task); 17591da177e4SLinus Torvalds } 17601da177e4SLinus Torvalds 1761