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> 211da177e4SLinus Torvalds #include <asm/uaccess.h> 221da177e4SLinus Torvalds 23ee857a7dSDavid S. Miller struct sock *xfrm_nl; 24ee857a7dSDavid S. Miller EXPORT_SYMBOL(xfrm_nl); 25ee857a7dSDavid S. Miller 26f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME; 27a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_etime); 28a70fcb0bSDavid S. Miller 29f8cd5488SJamal Hadi Salim u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE; 30a70fcb0bSDavid S. Miller EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth); 31a70fcb0bSDavid S. Miller 321da177e4SLinus Torvalds /* Each xfrm_state may be linked to two tables: 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl) 351da177e4SLinus Torvalds 2. Hash table by daddr to find what SAs exist for given 361da177e4SLinus Torvalds destination/tunnel endpoint. (output) 371da177e4SLinus Torvalds */ 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_lock); 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds /* Hash table to find appropriate SA towards given target (endpoint 421da177e4SLinus Torvalds * of tunnel or destination of transport mode) allowed by selector. 431da177e4SLinus Torvalds * 441da177e4SLinus Torvalds * Main use is finding SA after policy selected tunnel or transport mode. 451da177e4SLinus Torvalds * Also, it can be used by ah/esp icmp error handler to find offending SA. 461da177e4SLinus Torvalds */ 471da177e4SLinus Torvalds static struct list_head xfrm_state_bydst[XFRM_DST_HSIZE]; 481da177e4SLinus Torvalds static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE]; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(km_waitq); 511da177e4SLinus Torvalds EXPORT_SYMBOL(km_waitq); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_state_afinfo_lock); 541da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds static struct work_struct xfrm_state_gc_work; 571da177e4SLinus Torvalds static struct list_head xfrm_state_gc_list = LIST_HEAD_INIT(xfrm_state_gc_list); 581da177e4SLinus Torvalds static DEFINE_SPINLOCK(xfrm_state_gc_lock); 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds static int xfrm_state_gc_flush_bundles; 611da177e4SLinus Torvalds 6253bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x); 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family); 651da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); 661da177e4SLinus Torvalds 67980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 6853bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid); 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds static void xfrm_state_gc_destroy(struct xfrm_state *x) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds if (del_timer(&x->timer)) 731da177e4SLinus Torvalds BUG(); 74f8cd5488SJamal Hadi Salim if (del_timer(&x->rtimer)) 75f8cd5488SJamal Hadi Salim BUG(); 761da177e4SLinus Torvalds kfree(x->aalg); 771da177e4SLinus Torvalds kfree(x->ealg); 781da177e4SLinus Torvalds kfree(x->calg); 791da177e4SLinus Torvalds kfree(x->encap); 80b59f45d0SHerbert Xu if (x->mode) 81b59f45d0SHerbert Xu xfrm_put_mode(x->mode); 821da177e4SLinus Torvalds if (x->type) { 831da177e4SLinus Torvalds x->type->destructor(x); 841da177e4SLinus Torvalds xfrm_put_type(x->type); 851da177e4SLinus Torvalds } 86df71837dSTrent Jaeger security_xfrm_state_free(x); 871da177e4SLinus Torvalds kfree(x); 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static void xfrm_state_gc_task(void *data) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds struct xfrm_state *x; 931da177e4SLinus Torvalds struct list_head *entry, *tmp; 941da177e4SLinus Torvalds struct list_head gc_list = LIST_HEAD_INIT(gc_list); 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds if (xfrm_state_gc_flush_bundles) { 971da177e4SLinus Torvalds xfrm_state_gc_flush_bundles = 0; 981da177e4SLinus Torvalds xfrm_flush_bundles(); 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 1021da177e4SLinus Torvalds list_splice_init(&xfrm_state_gc_list, &gc_list); 1031da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds list_for_each_safe(entry, tmp, &gc_list) { 1061da177e4SLinus Torvalds x = list_entry(entry, struct xfrm_state, bydst); 1071da177e4SLinus Torvalds xfrm_state_gc_destroy(x); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds wake_up(&km_waitq); 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static inline unsigned long make_jiffies(long secs) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 1151da177e4SLinus Torvalds return MAX_SCHEDULE_TIMEOUT-1; 1161da177e4SLinus Torvalds else 1171da177e4SLinus Torvalds return secs*HZ; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds static void xfrm_timer_handler(unsigned long data) 1211da177e4SLinus Torvalds { 1221da177e4SLinus Torvalds struct xfrm_state *x = (struct xfrm_state*)data; 1231da177e4SLinus Torvalds unsigned long now = (unsigned long)xtime.tv_sec; 1241da177e4SLinus Torvalds long next = LONG_MAX; 1251da177e4SLinus Torvalds int warn = 0; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds spin_lock(&x->lock); 1281da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_DEAD) 1291da177e4SLinus Torvalds goto out; 1301da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_EXPIRED) 1311da177e4SLinus Torvalds goto expired; 1321da177e4SLinus Torvalds if (x->lft.hard_add_expires_seconds) { 1331da177e4SLinus Torvalds long tmo = x->lft.hard_add_expires_seconds + 1341da177e4SLinus Torvalds x->curlft.add_time - now; 1351da177e4SLinus Torvalds if (tmo <= 0) 1361da177e4SLinus Torvalds goto expired; 1371da177e4SLinus Torvalds if (tmo < next) 1381da177e4SLinus Torvalds next = tmo; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds if (x->lft.hard_use_expires_seconds) { 1411da177e4SLinus Torvalds long tmo = x->lft.hard_use_expires_seconds + 1421da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 1431da177e4SLinus Torvalds if (tmo <= 0) 1441da177e4SLinus Torvalds goto expired; 1451da177e4SLinus Torvalds if (tmo < next) 1461da177e4SLinus Torvalds next = tmo; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds if (x->km.dying) 1491da177e4SLinus Torvalds goto resched; 1501da177e4SLinus Torvalds if (x->lft.soft_add_expires_seconds) { 1511da177e4SLinus Torvalds long tmo = x->lft.soft_add_expires_seconds + 1521da177e4SLinus Torvalds x->curlft.add_time - now; 1531da177e4SLinus Torvalds if (tmo <= 0) 1541da177e4SLinus Torvalds warn = 1; 1551da177e4SLinus Torvalds else if (tmo < next) 1561da177e4SLinus Torvalds next = tmo; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds if (x->lft.soft_use_expires_seconds) { 1591da177e4SLinus Torvalds long tmo = x->lft.soft_use_expires_seconds + 1601da177e4SLinus Torvalds (x->curlft.use_time ? : now) - now; 1611da177e4SLinus Torvalds if (tmo <= 0) 1621da177e4SLinus Torvalds warn = 1; 1631da177e4SLinus Torvalds else if (tmo < next) 1641da177e4SLinus Torvalds next = tmo; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1674666faabSHerbert Xu x->km.dying = warn; 1681da177e4SLinus Torvalds if (warn) 16953bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 1701da177e4SLinus Torvalds resched: 1711da177e4SLinus Torvalds if (next != LONG_MAX && 1721da177e4SLinus Torvalds !mod_timer(&x->timer, jiffies + make_jiffies(next))) 1731da177e4SLinus Torvalds xfrm_state_hold(x); 1741da177e4SLinus Torvalds goto out; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds expired: 1771da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { 1781da177e4SLinus Torvalds x->km.state = XFRM_STATE_EXPIRED; 1791da177e4SLinus Torvalds wake_up(&km_waitq); 1801da177e4SLinus Torvalds next = 2; 1811da177e4SLinus Torvalds goto resched; 1821da177e4SLinus Torvalds } 1834666faabSHerbert Xu if (!__xfrm_state_delete(x) && x->id.spi) 18453bc6b4dSJamal Hadi Salim km_state_expired(x, 1, 0); 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds out: 1871da177e4SLinus Torvalds spin_unlock(&x->lock); 1881da177e4SLinus Torvalds xfrm_state_put(x); 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1910ac84752SDavid S. Miller static void xfrm_replay_timer_handler(unsigned long data); 1920ac84752SDavid S. Miller 1931da177e4SLinus Torvalds struct xfrm_state *xfrm_state_alloc(void) 1941da177e4SLinus Torvalds { 1951da177e4SLinus Torvalds struct xfrm_state *x; 1961da177e4SLinus Torvalds 1970da974f4SPanagiotis Issaris x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC); 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (x) { 2001da177e4SLinus Torvalds atomic_set(&x->refcnt, 1); 2011da177e4SLinus Torvalds atomic_set(&x->tunnel_users, 0); 2021da177e4SLinus Torvalds INIT_LIST_HEAD(&x->bydst); 2031da177e4SLinus Torvalds INIT_LIST_HEAD(&x->byspi); 2041da177e4SLinus Torvalds init_timer(&x->timer); 2051da177e4SLinus Torvalds x->timer.function = xfrm_timer_handler; 2061da177e4SLinus Torvalds x->timer.data = (unsigned long)x; 207f8cd5488SJamal Hadi Salim init_timer(&x->rtimer); 208f8cd5488SJamal Hadi Salim x->rtimer.function = xfrm_replay_timer_handler; 209f8cd5488SJamal Hadi Salim x->rtimer.data = (unsigned long)x; 2101da177e4SLinus Torvalds x->curlft.add_time = (unsigned long)xtime.tv_sec; 2111da177e4SLinus Torvalds x->lft.soft_byte_limit = XFRM_INF; 2121da177e4SLinus Torvalds x->lft.soft_packet_limit = XFRM_INF; 2131da177e4SLinus Torvalds x->lft.hard_byte_limit = XFRM_INF; 2141da177e4SLinus Torvalds x->lft.hard_packet_limit = XFRM_INF; 215f8cd5488SJamal Hadi Salim x->replay_maxage = 0; 216f8cd5488SJamal Hadi Salim x->replay_maxdiff = 0; 2171da177e4SLinus Torvalds spin_lock_init(&x->lock); 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds return x; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_alloc); 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds void __xfrm_state_destroy(struct xfrm_state *x) 2241da177e4SLinus Torvalds { 2251da177e4SLinus Torvalds BUG_TRAP(x->km.state == XFRM_STATE_DEAD); 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_gc_lock); 2281da177e4SLinus Torvalds list_add(&x->bydst, &xfrm_state_gc_list); 2291da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_gc_lock); 2301da177e4SLinus Torvalds schedule_work(&xfrm_state_gc_work); 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds EXPORT_SYMBOL(__xfrm_state_destroy); 2331da177e4SLinus Torvalds 23453bc6b4dSJamal Hadi Salim int __xfrm_state_delete(struct xfrm_state *x) 2351da177e4SLinus Torvalds { 23626b15dadSJamal Hadi Salim int err = -ESRCH; 23726b15dadSJamal Hadi Salim 2381da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_DEAD) { 2391da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 2401da177e4SLinus Torvalds spin_lock(&xfrm_state_lock); 2411da177e4SLinus Torvalds list_del(&x->bydst); 24221380b81SHerbert Xu __xfrm_state_put(x); 2431da177e4SLinus Torvalds if (x->id.spi) { 2441da177e4SLinus Torvalds list_del(&x->byspi); 24521380b81SHerbert Xu __xfrm_state_put(x); 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds spin_unlock(&xfrm_state_lock); 2481da177e4SLinus Torvalds if (del_timer(&x->timer)) 24921380b81SHerbert Xu __xfrm_state_put(x); 250f8cd5488SJamal Hadi Salim if (del_timer(&x->rtimer)) 251f8cd5488SJamal Hadi Salim __xfrm_state_put(x); 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds /* The number two in this test is the reference 2541da177e4SLinus Torvalds * mentioned in the comment below plus the reference 2551da177e4SLinus Torvalds * our caller holds. A larger value means that 2561da177e4SLinus Torvalds * there are DSTs attached to this xfrm_state. 2571da177e4SLinus Torvalds */ 2581da177e4SLinus Torvalds if (atomic_read(&x->refcnt) > 2) { 2591da177e4SLinus Torvalds xfrm_state_gc_flush_bundles = 1; 2601da177e4SLinus Torvalds schedule_work(&xfrm_state_gc_work); 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* All xfrm_state objects are created by xfrm_state_alloc. 2641da177e4SLinus Torvalds * The xfrm_state_alloc call gives a reference, and that 2651da177e4SLinus Torvalds * is what we are dropping here. 2661da177e4SLinus Torvalds */ 26721380b81SHerbert Xu __xfrm_state_put(x); 26826b15dadSJamal Hadi Salim err = 0; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 27126b15dadSJamal Hadi Salim return err; 27226b15dadSJamal Hadi Salim } 27353bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(__xfrm_state_delete); 27426b15dadSJamal Hadi Salim 27526b15dadSJamal Hadi Salim int xfrm_state_delete(struct xfrm_state *x) 2761da177e4SLinus Torvalds { 27726b15dadSJamal Hadi Salim int err; 27826b15dadSJamal Hadi Salim 2791da177e4SLinus Torvalds spin_lock_bh(&x->lock); 28026b15dadSJamal Hadi Salim err = __xfrm_state_delete(x); 2811da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 28226b15dadSJamal Hadi Salim 28326b15dadSJamal Hadi Salim return err; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete); 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds void xfrm_state_flush(u8 proto) 2881da177e4SLinus Torvalds { 2891da177e4SLinus Torvalds int i; 2901da177e4SLinus Torvalds struct xfrm_state *x; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 2931da177e4SLinus Torvalds for (i = 0; i < XFRM_DST_HSIZE; i++) { 2941da177e4SLinus Torvalds restart: 2951da177e4SLinus Torvalds list_for_each_entry(x, xfrm_state_bydst+i, bydst) { 2961da177e4SLinus Torvalds if (!xfrm_state_kern(x) && 2971da177e4SLinus Torvalds (proto == IPSEC_PROTO_ANY || x->id.proto == proto)) { 2981da177e4SLinus Torvalds xfrm_state_hold(x); 2991da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds xfrm_state_delete(x); 3021da177e4SLinus Torvalds xfrm_state_put(x); 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 3051da177e4SLinus Torvalds goto restart; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 3101da177e4SLinus Torvalds wake_up(&km_waitq); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_flush); 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds static int 3151da177e4SLinus Torvalds xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, 3161da177e4SLinus Torvalds struct xfrm_tmpl *tmpl, 3171da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 3181da177e4SLinus Torvalds unsigned short family) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 3211da177e4SLinus Torvalds if (!afinfo) 3221da177e4SLinus Torvalds return -1; 3231da177e4SLinus Torvalds afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); 3241da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 3251da177e4SLinus Torvalds return 0; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds struct xfrm_state * 3291da177e4SLinus Torvalds xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 3301da177e4SLinus Torvalds struct flowi *fl, struct xfrm_tmpl *tmpl, 3311da177e4SLinus Torvalds struct xfrm_policy *pol, int *err, 3321da177e4SLinus Torvalds unsigned short family) 3331da177e4SLinus Torvalds { 3341da177e4SLinus Torvalds unsigned h = xfrm_dst_hash(daddr, family); 3351da177e4SLinus Torvalds struct xfrm_state *x, *x0; 3361da177e4SLinus Torvalds int acquire_in_progress = 0; 3371da177e4SLinus Torvalds int error = 0; 3381da177e4SLinus Torvalds struct xfrm_state *best = NULL; 3391da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds afinfo = xfrm_state_get_afinfo(family); 3421da177e4SLinus Torvalds if (afinfo == NULL) { 3431da177e4SLinus Torvalds *err = -EAFNOSUPPORT; 3441da177e4SLinus Torvalds return NULL; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 3481da177e4SLinus Torvalds list_for_each_entry(x, xfrm_state_bydst+h, bydst) { 3491da177e4SLinus Torvalds if (x->props.family == family && 3501da177e4SLinus Torvalds x->props.reqid == tmpl->reqid && 3511da177e4SLinus Torvalds xfrm_state_addr_check(x, daddr, saddr, family) && 3521da177e4SLinus Torvalds tmpl->mode == x->props.mode && 3531da177e4SLinus Torvalds tmpl->id.proto == x->id.proto && 3541da177e4SLinus Torvalds (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { 3551da177e4SLinus Torvalds /* Resolution logic: 3561da177e4SLinus Torvalds 1. There is a valid state with matching selector. 3571da177e4SLinus Torvalds Done. 3581da177e4SLinus Torvalds 2. Valid state with inappropriate selector. Skip. 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds Entering area of "sysdeps". 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds 3. If state is not valid, selector is temporary, 3631da177e4SLinus Torvalds it selects only session which triggered 3641da177e4SLinus Torvalds previous resolution. Key manager will do 3651da177e4SLinus Torvalds something to install a state with proper 3661da177e4SLinus Torvalds selector. 3671da177e4SLinus Torvalds */ 3681da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID) { 369df71837dSTrent Jaeger if (!xfrm_selector_match(&x->sel, fl, family) || 370e0d1caa7SVenkat Yekkirala !security_xfrm_state_pol_flow_match(x, pol, fl)) 3711da177e4SLinus Torvalds continue; 3721da177e4SLinus Torvalds if (!best || 3731da177e4SLinus Torvalds best->km.dying > x->km.dying || 3741da177e4SLinus Torvalds (best->km.dying == x->km.dying && 3751da177e4SLinus Torvalds best->curlft.add_time < x->curlft.add_time)) 3761da177e4SLinus Torvalds best = x; 3771da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ACQ) { 3781da177e4SLinus Torvalds acquire_in_progress = 1; 3791da177e4SLinus Torvalds } else if (x->km.state == XFRM_STATE_ERROR || 3801da177e4SLinus Torvalds x->km.state == XFRM_STATE_EXPIRED) { 381df71837dSTrent Jaeger if (xfrm_selector_match(&x->sel, fl, family) && 382e0d1caa7SVenkat Yekkirala security_xfrm_state_pol_flow_match(x, pol, fl)) 3831da177e4SLinus Torvalds error = -ESRCH; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds x = best; 3891da177e4SLinus Torvalds if (!x && !error && !acquire_in_progress) { 3905c5d281aSPatrick McHardy if (tmpl->id.spi && 3915c5d281aSPatrick McHardy (x0 = afinfo->state_lookup(daddr, tmpl->id.spi, 3925c5d281aSPatrick McHardy tmpl->id.proto)) != NULL) { 3931da177e4SLinus Torvalds xfrm_state_put(x0); 3941da177e4SLinus Torvalds error = -EEXIST; 3951da177e4SLinus Torvalds goto out; 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds x = xfrm_state_alloc(); 3981da177e4SLinus Torvalds if (x == NULL) { 3991da177e4SLinus Torvalds error = -ENOMEM; 4001da177e4SLinus Torvalds goto out; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds /* Initialize temporary selector matching only 4031da177e4SLinus Torvalds * to current session. */ 4041da177e4SLinus Torvalds xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); 4051da177e4SLinus Torvalds 406e0d1caa7SVenkat Yekkirala error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); 407e0d1caa7SVenkat Yekkirala if (error) { 408e0d1caa7SVenkat Yekkirala x->km.state = XFRM_STATE_DEAD; 409e0d1caa7SVenkat Yekkirala xfrm_state_put(x); 410e0d1caa7SVenkat Yekkirala x = NULL; 411e0d1caa7SVenkat Yekkirala goto out; 412e0d1caa7SVenkat Yekkirala } 413e0d1caa7SVenkat Yekkirala 4141da177e4SLinus Torvalds if (km_query(x, tmpl, pol) == 0) { 4151da177e4SLinus Torvalds x->km.state = XFRM_STATE_ACQ; 4161da177e4SLinus Torvalds list_add_tail(&x->bydst, xfrm_state_bydst+h); 4171da177e4SLinus Torvalds xfrm_state_hold(x); 4181da177e4SLinus Torvalds if (x->id.spi) { 4191da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); 4201da177e4SLinus Torvalds list_add(&x->byspi, xfrm_state_byspi+h); 4211da177e4SLinus Torvalds xfrm_state_hold(x); 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; 4241da177e4SLinus Torvalds xfrm_state_hold(x); 4251da177e4SLinus Torvalds x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; 4261da177e4SLinus Torvalds add_timer(&x->timer); 4271da177e4SLinus Torvalds } else { 4281da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 4291da177e4SLinus Torvalds xfrm_state_put(x); 4301da177e4SLinus Torvalds x = NULL; 4311da177e4SLinus Torvalds error = -ESRCH; 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds out: 4351da177e4SLinus Torvalds if (x) 4361da177e4SLinus Torvalds xfrm_state_hold(x); 4371da177e4SLinus Torvalds else 4381da177e4SLinus Torvalds *err = acquire_in_progress ? -EAGAIN : error; 4391da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 4401da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 4411da177e4SLinus Torvalds return x; 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds static void __xfrm_state_insert(struct xfrm_state *x) 4451da177e4SLinus Torvalds { 4461da177e4SLinus Torvalds unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family); 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds list_add(&x->bydst, xfrm_state_bydst+h); 4491da177e4SLinus Torvalds xfrm_state_hold(x); 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds list_add(&x->byspi, xfrm_state_byspi+h); 4541da177e4SLinus Torvalds xfrm_state_hold(x); 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds if (!mod_timer(&x->timer, jiffies + HZ)) 4571da177e4SLinus Torvalds xfrm_state_hold(x); 4581da177e4SLinus Torvalds 459f8cd5488SJamal Hadi Salim if (x->replay_maxage && 460f8cd5488SJamal Hadi Salim !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 461f8cd5488SJamal Hadi Salim xfrm_state_hold(x); 462f8cd5488SJamal Hadi Salim 4631da177e4SLinus Torvalds wake_up(&km_waitq); 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds void xfrm_state_insert(struct xfrm_state *x) 4671da177e4SLinus Torvalds { 4681da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 4691da177e4SLinus Torvalds __xfrm_state_insert(x); 4701da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 471399c180aSDavid S. Miller 472399c180aSDavid S. Miller xfrm_flush_all_bundles(); 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_insert); 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds int xfrm_state_add(struct xfrm_state *x) 4791da177e4SLinus Torvalds { 4801da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 4811da177e4SLinus Torvalds struct xfrm_state *x1; 4821da177e4SLinus Torvalds int family; 4831da177e4SLinus Torvalds int err; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds family = x->props.family; 4861da177e4SLinus Torvalds afinfo = xfrm_state_get_afinfo(family); 4871da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 4881da177e4SLinus Torvalds return -EAFNOSUPPORT; 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto); 4931da177e4SLinus Torvalds if (x1) { 4941da177e4SLinus Torvalds xfrm_state_put(x1); 4951da177e4SLinus Torvalds x1 = NULL; 4961da177e4SLinus Torvalds err = -EEXIST; 4971da177e4SLinus Torvalds goto out; 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds if (x->km.seq) { 5011da177e4SLinus Torvalds x1 = __xfrm_find_acq_byseq(x->km.seq); 5021da177e4SLinus Torvalds if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) { 5031da177e4SLinus Torvalds xfrm_state_put(x1); 5041da177e4SLinus Torvalds x1 = NULL; 5051da177e4SLinus Torvalds } 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds if (!x1) 5091da177e4SLinus Torvalds x1 = afinfo->find_acq( 5101da177e4SLinus Torvalds x->props.mode, x->props.reqid, x->id.proto, 5111da177e4SLinus Torvalds &x->id.daddr, &x->props.saddr, 0); 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds __xfrm_state_insert(x); 5141da177e4SLinus Torvalds err = 0; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds out: 5171da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 5181da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 5191da177e4SLinus Torvalds 520399c180aSDavid S. Miller if (!err) 521399c180aSDavid S. Miller xfrm_flush_all_bundles(); 522399c180aSDavid S. Miller 5231da177e4SLinus Torvalds if (x1) { 5241da177e4SLinus Torvalds xfrm_state_delete(x1); 5251da177e4SLinus Torvalds xfrm_state_put(x1); 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds return err; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_add); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds int xfrm_state_update(struct xfrm_state *x) 5331da177e4SLinus Torvalds { 5341da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 5351da177e4SLinus Torvalds struct xfrm_state *x1; 5361da177e4SLinus Torvalds int err; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds afinfo = xfrm_state_get_afinfo(x->props.family); 5391da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 5401da177e4SLinus Torvalds return -EAFNOSUPPORT; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 5431da177e4SLinus Torvalds x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto); 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds err = -ESRCH; 5461da177e4SLinus Torvalds if (!x1) 5471da177e4SLinus Torvalds goto out; 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds if (xfrm_state_kern(x1)) { 5501da177e4SLinus Torvalds xfrm_state_put(x1); 5511da177e4SLinus Torvalds err = -EEXIST; 5521da177e4SLinus Torvalds goto out; 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds if (x1->km.state == XFRM_STATE_ACQ) { 5561da177e4SLinus Torvalds __xfrm_state_insert(x); 5571da177e4SLinus Torvalds x = NULL; 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds err = 0; 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds out: 5621da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 5631da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds if (err) 5661da177e4SLinus Torvalds return err; 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds if (!x) { 5691da177e4SLinus Torvalds xfrm_state_delete(x1); 5701da177e4SLinus Torvalds xfrm_state_put(x1); 5711da177e4SLinus Torvalds return 0; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds 5741da177e4SLinus Torvalds err = -EINVAL; 5751da177e4SLinus Torvalds spin_lock_bh(&x1->lock); 5761da177e4SLinus Torvalds if (likely(x1->km.state == XFRM_STATE_VALID)) { 5771da177e4SLinus Torvalds if (x->encap && x1->encap) 5781da177e4SLinus Torvalds memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 5791da177e4SLinus Torvalds memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 5801da177e4SLinus Torvalds x1->km.dying = 0; 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds if (!mod_timer(&x1->timer, jiffies + HZ)) 5831da177e4SLinus Torvalds xfrm_state_hold(x1); 5841da177e4SLinus Torvalds if (x1->curlft.use_time) 5851da177e4SLinus Torvalds xfrm_state_check_expire(x1); 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds err = 0; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds spin_unlock_bh(&x1->lock); 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds xfrm_state_put(x1); 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds return err; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_update); 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds int xfrm_state_check_expire(struct xfrm_state *x) 5981da177e4SLinus Torvalds { 5991da177e4SLinus Torvalds if (!x->curlft.use_time) 6001da177e4SLinus Torvalds x->curlft.use_time = (unsigned long)xtime.tv_sec; 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_VALID) 6031da177e4SLinus Torvalds return -EINVAL; 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds if (x->curlft.bytes >= x->lft.hard_byte_limit || 6061da177e4SLinus Torvalds x->curlft.packets >= x->lft.hard_packet_limit) { 6074666faabSHerbert Xu x->km.state = XFRM_STATE_EXPIRED; 6084666faabSHerbert Xu if (!mod_timer(&x->timer, jiffies)) 6091da177e4SLinus Torvalds xfrm_state_hold(x); 6101da177e4SLinus Torvalds return -EINVAL; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds if (!x->km.dying && 6141da177e4SLinus Torvalds (x->curlft.bytes >= x->lft.soft_byte_limit || 6154666faabSHerbert Xu x->curlft.packets >= x->lft.soft_packet_limit)) { 6164666faabSHerbert Xu x->km.dying = 1; 61753bc6b4dSJamal Hadi Salim km_state_expired(x, 0, 0); 6184666faabSHerbert Xu } 6191da177e4SLinus Torvalds return 0; 6201da177e4SLinus Torvalds } 6211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check_expire); 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) 6241da177e4SLinus Torvalds { 6251da177e4SLinus Torvalds int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev) 6261da177e4SLinus Torvalds - skb_headroom(skb); 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds if (nhead > 0) 6291da177e4SLinus Torvalds return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds /* Check tail too... */ 6321da177e4SLinus Torvalds return 0; 6331da177e4SLinus Torvalds } 6341da177e4SLinus Torvalds 6351da177e4SLinus Torvalds int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) 6361da177e4SLinus Torvalds { 6371da177e4SLinus Torvalds int err = xfrm_state_check_expire(x); 6381da177e4SLinus Torvalds if (err < 0) 6391da177e4SLinus Torvalds goto err; 6401da177e4SLinus Torvalds err = xfrm_state_check_space(x, skb); 6411da177e4SLinus Torvalds err: 6421da177e4SLinus Torvalds return err; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_check); 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds struct xfrm_state * 6471da177e4SLinus Torvalds xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, 6481da177e4SLinus Torvalds unsigned short family) 6491da177e4SLinus Torvalds { 6501da177e4SLinus Torvalds struct xfrm_state *x; 6511da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 6521da177e4SLinus Torvalds if (!afinfo) 6531da177e4SLinus Torvalds return NULL; 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 6561da177e4SLinus Torvalds x = afinfo->state_lookup(daddr, spi, proto); 6571da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6581da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 6591da177e4SLinus Torvalds return x; 6601da177e4SLinus Torvalds } 6611da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_lookup); 6621da177e4SLinus Torvalds 6631da177e4SLinus Torvalds struct xfrm_state * 6641da177e4SLinus Torvalds xfrm_find_acq(u8 mode, u32 reqid, u8 proto, 6651da177e4SLinus Torvalds xfrm_address_t *daddr, xfrm_address_t *saddr, 6661da177e4SLinus Torvalds int create, unsigned short family) 6671da177e4SLinus Torvalds { 6681da177e4SLinus Torvalds struct xfrm_state *x; 6691da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 6701da177e4SLinus Torvalds if (!afinfo) 6711da177e4SLinus Torvalds return NULL; 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 6741da177e4SLinus Torvalds x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create); 6751da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 6761da177e4SLinus Torvalds xfrm_state_put_afinfo(afinfo); 6771da177e4SLinus Torvalds return x; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq); 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds /* Silly enough, but I'm lazy to build resolution list */ 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq) 6841da177e4SLinus Torvalds { 6851da177e4SLinus Torvalds int i; 6861da177e4SLinus Torvalds struct xfrm_state *x; 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds for (i = 0; i < XFRM_DST_HSIZE; i++) { 6891da177e4SLinus Torvalds list_for_each_entry(x, xfrm_state_bydst+i, bydst) { 6901da177e4SLinus Torvalds if (x->km.seq == seq && x->km.state == XFRM_STATE_ACQ) { 6911da177e4SLinus Torvalds xfrm_state_hold(x); 6921da177e4SLinus Torvalds return x; 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds } 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds return NULL; 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds 6991da177e4SLinus Torvalds struct xfrm_state *xfrm_find_acq_byseq(u32 seq) 7001da177e4SLinus Torvalds { 7011da177e4SLinus Torvalds struct xfrm_state *x; 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7041da177e4SLinus Torvalds x = __xfrm_find_acq_byseq(seq); 7051da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 7061da177e4SLinus Torvalds return x; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_find_acq_byseq); 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds u32 xfrm_get_acqseq(void) 7111da177e4SLinus Torvalds { 7121da177e4SLinus Torvalds u32 res; 7131da177e4SLinus Torvalds static u32 acqseq; 7141da177e4SLinus Torvalds static DEFINE_SPINLOCK(acqseq_lock); 7151da177e4SLinus Torvalds 7161da177e4SLinus Torvalds spin_lock_bh(&acqseq_lock); 7171da177e4SLinus Torvalds res = (++acqseq ? : ++acqseq); 7181da177e4SLinus Torvalds spin_unlock_bh(&acqseq_lock); 7191da177e4SLinus Torvalds return res; 7201da177e4SLinus Torvalds } 7211da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_get_acqseq); 7221da177e4SLinus Torvalds 7231da177e4SLinus Torvalds void 7241da177e4SLinus Torvalds xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) 7251da177e4SLinus Torvalds { 7261da177e4SLinus Torvalds u32 h; 7271da177e4SLinus Torvalds struct xfrm_state *x0; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds if (x->id.spi) 7301da177e4SLinus Torvalds return; 7311da177e4SLinus Torvalds 7321da177e4SLinus Torvalds if (minspi == maxspi) { 7331da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); 7341da177e4SLinus Torvalds if (x0) { 7351da177e4SLinus Torvalds xfrm_state_put(x0); 7361da177e4SLinus Torvalds return; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds x->id.spi = minspi; 7391da177e4SLinus Torvalds } else { 7401da177e4SLinus Torvalds u32 spi = 0; 7411da177e4SLinus Torvalds minspi = ntohl(minspi); 7421da177e4SLinus Torvalds maxspi = ntohl(maxspi); 7431da177e4SLinus Torvalds for (h=0; h<maxspi-minspi+1; h++) { 7441da177e4SLinus Torvalds spi = minspi + net_random()%(maxspi-minspi+1); 7451da177e4SLinus Torvalds x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family); 7461da177e4SLinus Torvalds if (x0 == NULL) { 7471da177e4SLinus Torvalds x->id.spi = htonl(spi); 7481da177e4SLinus Torvalds break; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds xfrm_state_put(x0); 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds if (x->id.spi) { 7541da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7551da177e4SLinus Torvalds h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); 7561da177e4SLinus Torvalds list_add(&x->byspi, xfrm_state_byspi+h); 7571da177e4SLinus Torvalds xfrm_state_hold(x); 7581da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 7591da177e4SLinus Torvalds wake_up(&km_waitq); 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_alloc_spi); 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), 7651da177e4SLinus Torvalds void *data) 7661da177e4SLinus Torvalds { 7671da177e4SLinus Torvalds int i; 7681da177e4SLinus Torvalds struct xfrm_state *x; 7691da177e4SLinus Torvalds int count = 0; 7701da177e4SLinus Torvalds int err = 0; 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds spin_lock_bh(&xfrm_state_lock); 7731da177e4SLinus Torvalds for (i = 0; i < XFRM_DST_HSIZE; i++) { 7741da177e4SLinus Torvalds list_for_each_entry(x, xfrm_state_bydst+i, bydst) { 7751da177e4SLinus Torvalds if (proto == IPSEC_PROTO_ANY || x->id.proto == proto) 7761da177e4SLinus Torvalds count++; 7771da177e4SLinus Torvalds } 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds if (count == 0) { 7801da177e4SLinus Torvalds err = -ENOENT; 7811da177e4SLinus Torvalds goto out; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds for (i = 0; i < XFRM_DST_HSIZE; i++) { 7851da177e4SLinus Torvalds list_for_each_entry(x, xfrm_state_bydst+i, bydst) { 7861da177e4SLinus Torvalds if (proto != IPSEC_PROTO_ANY && x->id.proto != proto) 7871da177e4SLinus Torvalds continue; 7881da177e4SLinus Torvalds err = func(x, --count, data); 7891da177e4SLinus Torvalds if (err) 7901da177e4SLinus Torvalds goto out; 7911da177e4SLinus Torvalds } 7921da177e4SLinus Torvalds } 7931da177e4SLinus Torvalds out: 7941da177e4SLinus Torvalds spin_unlock_bh(&xfrm_state_lock); 7951da177e4SLinus Torvalds return err; 7961da177e4SLinus Torvalds } 7971da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_walk); 7981da177e4SLinus Torvalds 799f8cd5488SJamal Hadi Salim 800f8cd5488SJamal Hadi Salim void xfrm_replay_notify(struct xfrm_state *x, int event) 801f8cd5488SJamal Hadi Salim { 802f8cd5488SJamal Hadi Salim struct km_event c; 803f8cd5488SJamal Hadi Salim /* we send notify messages in case 804f8cd5488SJamal Hadi Salim * 1. we updated on of the sequence numbers, and the seqno difference 805f8cd5488SJamal Hadi Salim * is at least x->replay_maxdiff, in this case we also update the 806f8cd5488SJamal Hadi Salim * timeout of our timer function 807f8cd5488SJamal Hadi Salim * 2. if x->replay_maxage has elapsed since last update, 808f8cd5488SJamal Hadi Salim * and there were changes 809f8cd5488SJamal Hadi Salim * 810f8cd5488SJamal Hadi Salim * The state structure must be locked! 811f8cd5488SJamal Hadi Salim */ 812f8cd5488SJamal Hadi Salim 813f8cd5488SJamal Hadi Salim switch (event) { 814f8cd5488SJamal Hadi Salim case XFRM_REPLAY_UPDATE: 815f8cd5488SJamal Hadi Salim if (x->replay_maxdiff && 816f8cd5488SJamal Hadi Salim (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 8172717096aSJamal Hadi Salim (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { 8182717096aSJamal Hadi Salim if (x->xflags & XFRM_TIME_DEFER) 8192717096aSJamal Hadi Salim event = XFRM_REPLAY_TIMEOUT; 8202717096aSJamal Hadi Salim else 821f8cd5488SJamal Hadi Salim return; 8222717096aSJamal Hadi Salim } 823f8cd5488SJamal Hadi Salim 824f8cd5488SJamal Hadi Salim break; 825f8cd5488SJamal Hadi Salim 826f8cd5488SJamal Hadi Salim case XFRM_REPLAY_TIMEOUT: 827f8cd5488SJamal Hadi Salim if ((x->replay.seq == x->preplay.seq) && 828f8cd5488SJamal Hadi Salim (x->replay.bitmap == x->preplay.bitmap) && 8292717096aSJamal Hadi Salim (x->replay.oseq == x->preplay.oseq)) { 8302717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 831f8cd5488SJamal Hadi Salim return; 8322717096aSJamal Hadi Salim } 833f8cd5488SJamal Hadi Salim 834f8cd5488SJamal Hadi Salim break; 835f8cd5488SJamal Hadi Salim } 836f8cd5488SJamal Hadi Salim 837f8cd5488SJamal Hadi Salim memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 838f8cd5488SJamal Hadi Salim c.event = XFRM_MSG_NEWAE; 839f8cd5488SJamal Hadi Salim c.data.aevent = event; 840f8cd5488SJamal Hadi Salim km_state_notify(x, &c); 841f8cd5488SJamal Hadi Salim 842f8cd5488SJamal Hadi Salim if (x->replay_maxage && 8432717096aSJamal Hadi Salim !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) { 844f8cd5488SJamal Hadi Salim xfrm_state_hold(x); 8452717096aSJamal Hadi Salim x->xflags &= ~XFRM_TIME_DEFER; 8462717096aSJamal Hadi Salim } 847f8cd5488SJamal Hadi Salim } 848a70fcb0bSDavid S. Miller EXPORT_SYMBOL(xfrm_replay_notify); 849f8cd5488SJamal Hadi Salim 850f8cd5488SJamal Hadi Salim static void xfrm_replay_timer_handler(unsigned long data) 851f8cd5488SJamal Hadi Salim { 852f8cd5488SJamal Hadi Salim struct xfrm_state *x = (struct xfrm_state*)data; 853f8cd5488SJamal Hadi Salim 854f8cd5488SJamal Hadi Salim spin_lock(&x->lock); 855f8cd5488SJamal Hadi Salim 8562717096aSJamal Hadi Salim if (x->km.state == XFRM_STATE_VALID) { 8572717096aSJamal Hadi Salim if (xfrm_aevent_is_on()) 858f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); 8592717096aSJamal Hadi Salim else 8602717096aSJamal Hadi Salim x->xflags |= XFRM_TIME_DEFER; 8612717096aSJamal Hadi Salim } 862f8cd5488SJamal Hadi Salim 863f8cd5488SJamal Hadi Salim spin_unlock(&x->lock); 8642717096aSJamal Hadi Salim xfrm_state_put(x); 865f8cd5488SJamal Hadi Salim } 866f8cd5488SJamal Hadi Salim 8671da177e4SLinus Torvalds int xfrm_replay_check(struct xfrm_state *x, u32 seq) 8681da177e4SLinus Torvalds { 8691da177e4SLinus Torvalds u32 diff; 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds seq = ntohl(seq); 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds if (unlikely(seq == 0)) 8741da177e4SLinus Torvalds return -EINVAL; 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds if (likely(seq > x->replay.seq)) 8771da177e4SLinus Torvalds return 0; 8781da177e4SLinus Torvalds 8791da177e4SLinus Torvalds diff = x->replay.seq - seq; 8801da177e4SLinus Torvalds if (diff >= x->props.replay_window) { 8811da177e4SLinus Torvalds x->stats.replay_window++; 8821da177e4SLinus Torvalds return -EINVAL; 8831da177e4SLinus Torvalds } 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds if (x->replay.bitmap & (1U << diff)) { 8861da177e4SLinus Torvalds x->stats.replay++; 8871da177e4SLinus Torvalds return -EINVAL; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds return 0; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_check); 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds void xfrm_replay_advance(struct xfrm_state *x, u32 seq) 8941da177e4SLinus Torvalds { 8951da177e4SLinus Torvalds u32 diff; 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds seq = ntohl(seq); 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds if (seq > x->replay.seq) { 9001da177e4SLinus Torvalds diff = seq - x->replay.seq; 9011da177e4SLinus Torvalds if (diff < x->props.replay_window) 9021da177e4SLinus Torvalds x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 9031da177e4SLinus Torvalds else 9041da177e4SLinus Torvalds x->replay.bitmap = 1; 9051da177e4SLinus Torvalds x->replay.seq = seq; 9061da177e4SLinus Torvalds } else { 9071da177e4SLinus Torvalds diff = x->replay.seq - seq; 9081da177e4SLinus Torvalds x->replay.bitmap |= (1U << diff); 9091da177e4SLinus Torvalds } 910f8cd5488SJamal Hadi Salim 911f8cd5488SJamal Hadi Salim if (xfrm_aevent_is_on()) 912f8cd5488SJamal Hadi Salim xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_replay_advance); 9151da177e4SLinus Torvalds 9161da177e4SLinus Torvalds static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); 9171da177e4SLinus Torvalds static DEFINE_RWLOCK(xfrm_km_lock); 9181da177e4SLinus Torvalds 91926b15dadSJamal Hadi Salim void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 9201da177e4SLinus Torvalds { 9211da177e4SLinus Torvalds struct xfrm_mgr *km; 9221da177e4SLinus Torvalds 92326b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 92426b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 92526b15dadSJamal Hadi Salim if (km->notify_policy) 92626b15dadSJamal Hadi Salim km->notify_policy(xp, dir, c); 92726b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 92826b15dadSJamal Hadi Salim } 92926b15dadSJamal Hadi Salim 93026b15dadSJamal Hadi Salim void km_state_notify(struct xfrm_state *x, struct km_event *c) 93126b15dadSJamal Hadi Salim { 93226b15dadSJamal Hadi Salim struct xfrm_mgr *km; 93326b15dadSJamal Hadi Salim read_lock(&xfrm_km_lock); 93426b15dadSJamal Hadi Salim list_for_each_entry(km, &xfrm_km_list, list) 93526b15dadSJamal Hadi Salim if (km->notify) 93626b15dadSJamal Hadi Salim km->notify(x, c); 93726b15dadSJamal Hadi Salim read_unlock(&xfrm_km_lock); 93826b15dadSJamal Hadi Salim } 93926b15dadSJamal Hadi Salim 94026b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_policy_notify); 94126b15dadSJamal Hadi Salim EXPORT_SYMBOL(km_state_notify); 94226b15dadSJamal Hadi Salim 94353bc6b4dSJamal Hadi Salim void km_state_expired(struct xfrm_state *x, int hard, u32 pid) 94426b15dadSJamal Hadi Salim { 94526b15dadSJamal Hadi Salim struct km_event c; 94626b15dadSJamal Hadi Salim 947bf08867fSHerbert Xu c.data.hard = hard; 94853bc6b4dSJamal Hadi Salim c.pid = pid; 949f60f6b8fSHerbert Xu c.event = XFRM_MSG_EXPIRE; 95026b15dadSJamal Hadi Salim km_state_notify(x, &c); 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds if (hard) 9531da177e4SLinus Torvalds wake_up(&km_waitq); 9541da177e4SLinus Torvalds } 9551da177e4SLinus Torvalds 95653bc6b4dSJamal Hadi Salim EXPORT_SYMBOL(km_state_expired); 95726b15dadSJamal Hadi Salim /* 95826b15dadSJamal Hadi Salim * We send to all registered managers regardless of failure 95926b15dadSJamal Hadi Salim * We are happy with one success 96026b15dadSJamal Hadi Salim */ 961980ebd25SJamal Hadi Salim int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 9621da177e4SLinus Torvalds { 96326b15dadSJamal Hadi Salim int err = -EINVAL, acqret; 9641da177e4SLinus Torvalds struct xfrm_mgr *km; 9651da177e4SLinus Torvalds 9661da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 9671da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 96826b15dadSJamal Hadi Salim acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); 96926b15dadSJamal Hadi Salim if (!acqret) 97026b15dadSJamal Hadi Salim err = acqret; 9711da177e4SLinus Torvalds } 9721da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 9731da177e4SLinus Torvalds return err; 9741da177e4SLinus Torvalds } 975980ebd25SJamal Hadi Salim EXPORT_SYMBOL(km_query); 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport) 9781da177e4SLinus Torvalds { 9791da177e4SLinus Torvalds int err = -EINVAL; 9801da177e4SLinus Torvalds struct xfrm_mgr *km; 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 9831da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 9841da177e4SLinus Torvalds if (km->new_mapping) 9851da177e4SLinus Torvalds err = km->new_mapping(x, ipaddr, sport); 9861da177e4SLinus Torvalds if (!err) 9871da177e4SLinus Torvalds break; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 9901da177e4SLinus Torvalds return err; 9911da177e4SLinus Torvalds } 9921da177e4SLinus Torvalds EXPORT_SYMBOL(km_new_mapping); 9931da177e4SLinus Torvalds 9946c5c8ca7SJamal Hadi Salim void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) 9951da177e4SLinus Torvalds { 99626b15dadSJamal Hadi Salim struct km_event c; 9971da177e4SLinus Torvalds 998bf08867fSHerbert Xu c.data.hard = hard; 9996c5c8ca7SJamal Hadi Salim c.pid = pid; 1000f60f6b8fSHerbert Xu c.event = XFRM_MSG_POLEXPIRE; 100126b15dadSJamal Hadi Salim km_policy_notify(pol, dir, &c); 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds if (hard) 10041da177e4SLinus Torvalds wake_up(&km_waitq); 10051da177e4SLinus Torvalds } 1006a70fcb0bSDavid S. Miller EXPORT_SYMBOL(km_policy_expired); 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 10091da177e4SLinus Torvalds { 10101da177e4SLinus Torvalds int err; 10111da177e4SLinus Torvalds u8 *data; 10121da177e4SLinus Torvalds struct xfrm_mgr *km; 10131da177e4SLinus Torvalds struct xfrm_policy *pol = NULL; 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds if (optlen <= 0 || optlen > PAGE_SIZE) 10161da177e4SLinus Torvalds return -EMSGSIZE; 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds data = kmalloc(optlen, GFP_KERNEL); 10191da177e4SLinus Torvalds if (!data) 10201da177e4SLinus Torvalds return -ENOMEM; 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds err = -EFAULT; 10231da177e4SLinus Torvalds if (copy_from_user(data, optval, optlen)) 10241da177e4SLinus Torvalds goto out; 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds err = -EINVAL; 10271da177e4SLinus Torvalds read_lock(&xfrm_km_lock); 10281da177e4SLinus Torvalds list_for_each_entry(km, &xfrm_km_list, list) { 1029cb969f07SVenkat Yekkirala pol = km->compile_policy(sk, optname, data, 10301da177e4SLinus Torvalds optlen, &err); 10311da177e4SLinus Torvalds if (err >= 0) 10321da177e4SLinus Torvalds break; 10331da177e4SLinus Torvalds } 10341da177e4SLinus Torvalds read_unlock(&xfrm_km_lock); 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds if (err >= 0) { 10371da177e4SLinus Torvalds xfrm_sk_policy_insert(sk, err, pol); 10381da177e4SLinus Torvalds xfrm_pol_put(pol); 10391da177e4SLinus Torvalds err = 0; 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds out: 10431da177e4SLinus Torvalds kfree(data); 10441da177e4SLinus Torvalds return err; 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_user_policy); 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds int xfrm_register_km(struct xfrm_mgr *km) 10491da177e4SLinus Torvalds { 10501da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 10511da177e4SLinus Torvalds list_add_tail(&km->list, &xfrm_km_list); 10521da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 10531da177e4SLinus Torvalds return 0; 10541da177e4SLinus Torvalds } 10551da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_register_km); 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds int xfrm_unregister_km(struct xfrm_mgr *km) 10581da177e4SLinus Torvalds { 10591da177e4SLinus Torvalds write_lock_bh(&xfrm_km_lock); 10601da177e4SLinus Torvalds list_del(&km->list); 10611da177e4SLinus Torvalds write_unlock_bh(&xfrm_km_lock); 10621da177e4SLinus Torvalds return 0; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_unregister_km); 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 10671da177e4SLinus Torvalds { 10681da177e4SLinus Torvalds int err = 0; 10691da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 10701da177e4SLinus Torvalds return -EINVAL; 10711da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 10721da177e4SLinus Torvalds return -EAFNOSUPPORT; 1073f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 10741da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 10751da177e4SLinus Torvalds err = -ENOBUFS; 10761da177e4SLinus Torvalds else { 10771da177e4SLinus Torvalds afinfo->state_bydst = xfrm_state_bydst; 10781da177e4SLinus Torvalds afinfo->state_byspi = xfrm_state_byspi; 10791da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = afinfo; 10801da177e4SLinus Torvalds } 1081f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 10821da177e4SLinus Torvalds return err; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_register_afinfo); 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 10871da177e4SLinus Torvalds { 10881da177e4SLinus Torvalds int err = 0; 10891da177e4SLinus Torvalds if (unlikely(afinfo == NULL)) 10901da177e4SLinus Torvalds return -EINVAL; 10911da177e4SLinus Torvalds if (unlikely(afinfo->family >= NPROTO)) 10921da177e4SLinus Torvalds return -EAFNOSUPPORT; 1093f3111502SIngo Molnar write_lock_bh(&xfrm_state_afinfo_lock); 10941da177e4SLinus Torvalds if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 10951da177e4SLinus Torvalds if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) 10961da177e4SLinus Torvalds err = -EINVAL; 10971da177e4SLinus Torvalds else { 10981da177e4SLinus Torvalds xfrm_state_afinfo[afinfo->family] = NULL; 10991da177e4SLinus Torvalds afinfo->state_byspi = NULL; 11001da177e4SLinus Torvalds afinfo->state_bydst = NULL; 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds } 1103f3111502SIngo Molnar write_unlock_bh(&xfrm_state_afinfo_lock); 11041da177e4SLinus Torvalds return err; 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_unregister_afinfo); 11071da177e4SLinus Torvalds 11081da177e4SLinus Torvalds static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) 11091da177e4SLinus Torvalds { 11101da177e4SLinus Torvalds struct xfrm_state_afinfo *afinfo; 11111da177e4SLinus Torvalds if (unlikely(family >= NPROTO)) 11121da177e4SLinus Torvalds return NULL; 11131da177e4SLinus Torvalds read_lock(&xfrm_state_afinfo_lock); 11141da177e4SLinus Torvalds afinfo = xfrm_state_afinfo[family]; 1115546be240SHerbert Xu if (unlikely(!afinfo)) 11161da177e4SLinus Torvalds read_unlock(&xfrm_state_afinfo_lock); 11171da177e4SLinus Torvalds return afinfo; 11181da177e4SLinus Torvalds } 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) 11211da177e4SLinus Torvalds { 1122546be240SHerbert Xu read_unlock(&xfrm_state_afinfo_lock); 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 11261da177e4SLinus Torvalds void xfrm_state_delete_tunnel(struct xfrm_state *x) 11271da177e4SLinus Torvalds { 11281da177e4SLinus Torvalds if (x->tunnel) { 11291da177e4SLinus Torvalds struct xfrm_state *t = x->tunnel; 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds if (atomic_read(&t->tunnel_users) == 2) 11321da177e4SLinus Torvalds xfrm_state_delete(t); 11331da177e4SLinus Torvalds atomic_dec(&t->tunnel_users); 11341da177e4SLinus Torvalds xfrm_state_put(t); 11351da177e4SLinus Torvalds x->tunnel = NULL; 11361da177e4SLinus Torvalds } 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds EXPORT_SYMBOL(xfrm_state_delete_tunnel); 11391da177e4SLinus Torvalds 114080b30c10SHerbert Xu /* 114180b30c10SHerbert Xu * This function is NOT optimal. For example, with ESP it will give an 114280b30c10SHerbert Xu * MTU that's usually two bytes short of being optimal. However, it will 114380b30c10SHerbert Xu * usually give an answer that's a multiple of 4 provided the input is 114480b30c10SHerbert Xu * also a multiple of 4. 114580b30c10SHerbert Xu */ 11461da177e4SLinus Torvalds int xfrm_state_mtu(struct xfrm_state *x, int mtu) 11471da177e4SLinus Torvalds { 11481da177e4SLinus Torvalds int res = mtu; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds res -= x->props.header_len; 11511da177e4SLinus Torvalds 11521da177e4SLinus Torvalds for (;;) { 11531da177e4SLinus Torvalds int m = res; 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds if (m < 68) 11561da177e4SLinus Torvalds return 68; 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds spin_lock_bh(&x->lock); 11591da177e4SLinus Torvalds if (x->km.state == XFRM_STATE_VALID && 11601da177e4SLinus Torvalds x->type && x->type->get_max_size) 11611da177e4SLinus Torvalds m = x->type->get_max_size(x, m); 11621da177e4SLinus Torvalds else 11631da177e4SLinus Torvalds m += x->props.header_len; 11641da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 11651da177e4SLinus Torvalds 11661da177e4SLinus Torvalds if (m <= mtu) 11671da177e4SLinus Torvalds break; 11681da177e4SLinus Torvalds res -= (m - mtu); 11691da177e4SLinus Torvalds } 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds return res; 11721da177e4SLinus Torvalds } 11731da177e4SLinus Torvalds 117472cb6962SHerbert Xu int xfrm_init_state(struct xfrm_state *x) 117572cb6962SHerbert Xu { 1176d094cd83SHerbert Xu struct xfrm_state_afinfo *afinfo; 1177d094cd83SHerbert Xu int family = x->props.family; 117872cb6962SHerbert Xu int err; 117972cb6962SHerbert Xu 1180d094cd83SHerbert Xu err = -EAFNOSUPPORT; 1181d094cd83SHerbert Xu afinfo = xfrm_state_get_afinfo(family); 1182d094cd83SHerbert Xu if (!afinfo) 1183d094cd83SHerbert Xu goto error; 1184d094cd83SHerbert Xu 1185d094cd83SHerbert Xu err = 0; 1186d094cd83SHerbert Xu if (afinfo->init_flags) 1187d094cd83SHerbert Xu err = afinfo->init_flags(x); 1188d094cd83SHerbert Xu 1189d094cd83SHerbert Xu xfrm_state_put_afinfo(afinfo); 1190d094cd83SHerbert Xu 1191d094cd83SHerbert Xu if (err) 1192d094cd83SHerbert Xu goto error; 1193d094cd83SHerbert Xu 1194d094cd83SHerbert Xu err = -EPROTONOSUPPORT; 1195d094cd83SHerbert Xu x->type = xfrm_get_type(x->id.proto, family); 119672cb6962SHerbert Xu if (x->type == NULL) 119772cb6962SHerbert Xu goto error; 119872cb6962SHerbert Xu 119972cb6962SHerbert Xu err = x->type->init_state(x); 120072cb6962SHerbert Xu if (err) 120172cb6962SHerbert Xu goto error; 120272cb6962SHerbert Xu 1203b59f45d0SHerbert Xu x->mode = xfrm_get_mode(x->props.mode, family); 1204b59f45d0SHerbert Xu if (x->mode == NULL) 1205b59f45d0SHerbert Xu goto error; 1206b59f45d0SHerbert Xu 120772cb6962SHerbert Xu x->km.state = XFRM_STATE_VALID; 120872cb6962SHerbert Xu 120972cb6962SHerbert Xu error: 121072cb6962SHerbert Xu return err; 121172cb6962SHerbert Xu } 121272cb6962SHerbert Xu 121372cb6962SHerbert Xu EXPORT_SYMBOL(xfrm_init_state); 12141da177e4SLinus Torvalds 12151da177e4SLinus Torvalds void __init xfrm_state_init(void) 12161da177e4SLinus Torvalds { 12171da177e4SLinus Torvalds int i; 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds for (i=0; i<XFRM_DST_HSIZE; i++) { 12201da177e4SLinus Torvalds INIT_LIST_HEAD(&xfrm_state_bydst[i]); 12211da177e4SLinus Torvalds INIT_LIST_HEAD(&xfrm_state_byspi[i]); 12221da177e4SLinus Torvalds } 12231da177e4SLinus Torvalds INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL); 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 1226