18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * xfrm_state.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Changes: 68c2ecf20Sopenharmony_ci * Mitsuru KANDA @USAGI 78c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI 88c2ecf20Sopenharmony_ci * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 98c2ecf20Sopenharmony_ci * IPv6 support 108c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI 118c2ecf20Sopenharmony_ci * Split up af-specific functions 128c2ecf20Sopenharmony_ci * Derek Atkins <derek@ihtfp.com> 138c2ecf20Sopenharmony_ci * Add UDP Encapsulation 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 188c2ecf20Sopenharmony_ci#include <net/xfrm.h> 198c2ecf20Sopenharmony_ci#include <linux/pfkeyv2.h> 208c2ecf20Sopenharmony_ci#include <linux/ipsec.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/cache.h> 238c2ecf20Sopenharmony_ci#include <linux/audit.h> 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci#include <linux/ktime.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/kernel.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <crypto/aead.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "xfrm_hash.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define xfrm_state_deref_prot(table, net) \ 358c2ecf20Sopenharmony_ci rcu_dereference_protected((table), lockdep_is_held(&(net)->xfrm.xfrm_state_lock)) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void xfrm_state_gc_task(struct work_struct *work); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Each xfrm_state may be linked to two tables: 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl) 428c2ecf20Sopenharmony_ci 2. Hash table by (daddr,family,reqid) to find what SAs exist for given 438c2ecf20Sopenharmony_ci destination/tunnel endpoint. (output) 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; 478c2ecf20Sopenharmony_cistatic struct kmem_cache *xfrm_state_cache __ro_after_init; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic DECLARE_WORK(xfrm_state_gc_work, xfrm_state_gc_task); 508c2ecf20Sopenharmony_cistatic HLIST_HEAD(xfrm_state_gc_list); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return refcount_inc_not_zero(&x->refcnt); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline unsigned int xfrm_dst_hash(struct net *net, 588c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 598c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 608c2ecf20Sopenharmony_ci u32 reqid, 618c2ecf20Sopenharmony_ci unsigned short family) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline unsigned int xfrm_src_hash(struct net *net, 678c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 688c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 698c2ecf20Sopenharmony_ci unsigned short family) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline unsigned int 758c2ecf20Sopenharmony_cixfrm_spi_hash(struct net *net, const xfrm_address_t *daddr, 768c2ecf20Sopenharmony_ci __be32 spi, u8 proto, unsigned short family) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void xfrm_hash_transfer(struct hlist_head *list, 828c2ecf20Sopenharmony_ci struct hlist_head *ndsttable, 838c2ecf20Sopenharmony_ci struct hlist_head *nsrctable, 848c2ecf20Sopenharmony_ci struct hlist_head *nspitable, 858c2ecf20Sopenharmony_ci unsigned int nhashmask) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct hlist_node *tmp; 888c2ecf20Sopenharmony_ci struct xfrm_state *x; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(x, tmp, list, bydst) { 918c2ecf20Sopenharmony_ci unsigned int h; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 948c2ecf20Sopenharmony_ci x->props.reqid, x->props.family, 958c2ecf20Sopenharmony_ci nhashmask); 968c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bydst, ndsttable + h); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, 998c2ecf20Sopenharmony_ci x->props.family, 1008c2ecf20Sopenharmony_ci nhashmask); 1018c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bysrc, nsrctable + h); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (x->id.spi) { 1048c2ecf20Sopenharmony_ci h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, 1058c2ecf20Sopenharmony_ci x->id.proto, x->props.family, 1068c2ecf20Sopenharmony_ci nhashmask); 1078c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->byspi, nspitable + h); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic unsigned long xfrm_hash_new_size(unsigned int state_hmask) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return ((state_hmask + 1) << 1) * sizeof(struct hlist_head); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void xfrm_hash_resize(struct work_struct *work) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct net *net = container_of(work, struct net, xfrm.state_hash_work); 1208c2ecf20Sopenharmony_ci struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; 1218c2ecf20Sopenharmony_ci unsigned long nsize, osize; 1228c2ecf20Sopenharmony_ci unsigned int nhashmask, ohashmask; 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci nsize = xfrm_hash_new_size(net->xfrm.state_hmask); 1268c2ecf20Sopenharmony_ci ndst = xfrm_hash_alloc(nsize); 1278c2ecf20Sopenharmony_ci if (!ndst) 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci nsrc = xfrm_hash_alloc(nsize); 1308c2ecf20Sopenharmony_ci if (!nsrc) { 1318c2ecf20Sopenharmony_ci xfrm_hash_free(ndst, nsize); 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci nspi = xfrm_hash_alloc(nsize); 1358c2ecf20Sopenharmony_ci if (!nspi) { 1368c2ecf20Sopenharmony_ci xfrm_hash_free(ndst, nsize); 1378c2ecf20Sopenharmony_ci xfrm_hash_free(nsrc, nsize); 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 1428c2ecf20Sopenharmony_ci write_seqcount_begin(&net->xfrm.xfrm_state_hash_generation); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; 1458c2ecf20Sopenharmony_ci odst = xfrm_state_deref_prot(net->xfrm.state_bydst, net); 1468c2ecf20Sopenharmony_ci for (i = net->xfrm.state_hmask; i >= 0; i--) 1478c2ecf20Sopenharmony_ci xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nhashmask); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci osrc = xfrm_state_deref_prot(net->xfrm.state_bysrc, net); 1508c2ecf20Sopenharmony_ci ospi = xfrm_state_deref_prot(net->xfrm.state_byspi, net); 1518c2ecf20Sopenharmony_ci ohashmask = net->xfrm.state_hmask; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci rcu_assign_pointer(net->xfrm.state_bydst, ndst); 1548c2ecf20Sopenharmony_ci rcu_assign_pointer(net->xfrm.state_bysrc, nsrc); 1558c2ecf20Sopenharmony_ci rcu_assign_pointer(net->xfrm.state_byspi, nspi); 1568c2ecf20Sopenharmony_ci net->xfrm.state_hmask = nhashmask; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci write_seqcount_end(&net->xfrm.xfrm_state_hash_generation); 1598c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci osize = (ohashmask + 1) * sizeof(struct hlist_head); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci synchronize_rcu(); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci xfrm_hash_free(odst, osize); 1668c2ecf20Sopenharmony_ci xfrm_hash_free(osrc, osize); 1678c2ecf20Sopenharmony_ci xfrm_hash_free(ospi, osize); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_state_afinfo_lock); 1718c2ecf20Sopenharmony_cistatic struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO]; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_state_gc_lock); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint __xfrm_state_delete(struct xfrm_state *x); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciint km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 1788c2ecf20Sopenharmony_cistatic bool km_is_alive(const struct km_event *c); 1798c2ecf20Sopenharmony_civoid km_state_expired(struct xfrm_state *x, int hard, u32 portid); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciint xfrm_register_type(const struct xfrm_type *type, unsigned short family) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 1848c2ecf20Sopenharmony_ci int err = 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!afinfo) 1878c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define X(afi, T, name) do { \ 1908c2ecf20Sopenharmony_ci WARN_ON((afi)->type_ ## name); \ 1918c2ecf20Sopenharmony_ci (afi)->type_ ## name = (T); \ 1928c2ecf20Sopenharmony_ci } while (0) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (type->proto) { 1958c2ecf20Sopenharmony_ci case IPPROTO_COMP: 1968c2ecf20Sopenharmony_ci X(afinfo, type, comp); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case IPPROTO_AH: 1998c2ecf20Sopenharmony_ci X(afinfo, type, ah); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case IPPROTO_ESP: 2028c2ecf20Sopenharmony_ci X(afinfo, type, esp); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case IPPROTO_IPIP: 2058c2ecf20Sopenharmony_ci X(afinfo, type, ipip); 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case IPPROTO_DSTOPTS: 2088c2ecf20Sopenharmony_ci X(afinfo, type, dstopts); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case IPPROTO_ROUTING: 2118c2ecf20Sopenharmony_ci X(afinfo, type, routing); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci case IPPROTO_IPV6: 2148c2ecf20Sopenharmony_ci X(afinfo, type, ipip6); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci default: 2178c2ecf20Sopenharmony_ci WARN_ON(1); 2188c2ecf20Sopenharmony_ci err = -EPROTONOSUPPORT; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci#undef X 2228c2ecf20Sopenharmony_ci rcu_read_unlock(); 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_register_type); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci#define X(afi, T, name) do { \ 2358c2ecf20Sopenharmony_ci WARN_ON((afi)->type_ ## name != (T)); \ 2368c2ecf20Sopenharmony_ci (afi)->type_ ## name = NULL; \ 2378c2ecf20Sopenharmony_ci } while (0) 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci switch (type->proto) { 2408c2ecf20Sopenharmony_ci case IPPROTO_COMP: 2418c2ecf20Sopenharmony_ci X(afinfo, type, comp); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case IPPROTO_AH: 2448c2ecf20Sopenharmony_ci X(afinfo, type, ah); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case IPPROTO_ESP: 2478c2ecf20Sopenharmony_ci X(afinfo, type, esp); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case IPPROTO_IPIP: 2508c2ecf20Sopenharmony_ci X(afinfo, type, ipip); 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case IPPROTO_DSTOPTS: 2538c2ecf20Sopenharmony_ci X(afinfo, type, dstopts); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case IPPROTO_ROUTING: 2568c2ecf20Sopenharmony_ci X(afinfo, type, routing); 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci case IPPROTO_IPV6: 2598c2ecf20Sopenharmony_ci X(afinfo, type, ipip6); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci WARN_ON(1); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci#undef X 2668c2ecf20Sopenharmony_ci rcu_read_unlock(); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_unregister_type); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci const struct xfrm_type *type = NULL; 2738c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo; 2748c2ecf20Sopenharmony_ci int modload_attempted = 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ciretry: 2778c2ecf20Sopenharmony_ci afinfo = xfrm_state_get_afinfo(family); 2788c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 2798c2ecf20Sopenharmony_ci return NULL; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci switch (proto) { 2828c2ecf20Sopenharmony_ci case IPPROTO_COMP: 2838c2ecf20Sopenharmony_ci type = afinfo->type_comp; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case IPPROTO_AH: 2868c2ecf20Sopenharmony_ci type = afinfo->type_ah; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case IPPROTO_ESP: 2898c2ecf20Sopenharmony_ci type = afinfo->type_esp; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case IPPROTO_IPIP: 2928c2ecf20Sopenharmony_ci type = afinfo->type_ipip; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case IPPROTO_DSTOPTS: 2958c2ecf20Sopenharmony_ci type = afinfo->type_dstopts; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case IPPROTO_ROUTING: 2988c2ecf20Sopenharmony_ci type = afinfo->type_routing; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case IPPROTO_IPV6: 3018c2ecf20Sopenharmony_ci type = afinfo->type_ipip6; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci default: 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (unlikely(type && !try_module_get(type->owner))) 3088c2ecf20Sopenharmony_ci type = NULL; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci rcu_read_unlock(); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (!type && !modload_attempted) { 3138c2ecf20Sopenharmony_ci request_module("xfrm-type-%d-%d", family, proto); 3148c2ecf20Sopenharmony_ci modload_attempted = 1; 3158c2ecf20Sopenharmony_ci goto retry; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return type; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void xfrm_put_type(const struct xfrm_type *type) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci module_put(type->owner); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ciint xfrm_register_type_offload(const struct xfrm_type_offload *type, 3278c2ecf20Sopenharmony_ci unsigned short family) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 3308c2ecf20Sopenharmony_ci int err = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 3338c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci switch (type->proto) { 3368c2ecf20Sopenharmony_ci case IPPROTO_ESP: 3378c2ecf20Sopenharmony_ci WARN_ON(afinfo->type_offload_esp); 3388c2ecf20Sopenharmony_ci afinfo->type_offload_esp = type; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci WARN_ON(1); 3428c2ecf20Sopenharmony_ci err = -EPROTONOSUPPORT; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci rcu_read_unlock(); 3478c2ecf20Sopenharmony_ci return err; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_register_type_offload); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_civoid xfrm_unregister_type_offload(const struct xfrm_type_offload *type, 3528c2ecf20Sopenharmony_ci unsigned short family) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 3578c2ecf20Sopenharmony_ci return; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci switch (type->proto) { 3608c2ecf20Sopenharmony_ci case IPPROTO_ESP: 3618c2ecf20Sopenharmony_ci WARN_ON(afinfo->type_offload_esp != type); 3628c2ecf20Sopenharmony_ci afinfo->type_offload_esp = NULL; 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci default: 3658c2ecf20Sopenharmony_ci WARN_ON(1); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci rcu_read_unlock(); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_unregister_type_offload); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct xfrm_type_offload * 3738c2ecf20Sopenharmony_cixfrm_get_type_offload(u8 proto, unsigned short family, bool try_load) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci const struct xfrm_type_offload *type = NULL; 3768c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciretry: 3798c2ecf20Sopenharmony_ci afinfo = xfrm_state_get_afinfo(family); 3808c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 3818c2ecf20Sopenharmony_ci return NULL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci switch (proto) { 3848c2ecf20Sopenharmony_ci case IPPROTO_ESP: 3858c2ecf20Sopenharmony_ci type = afinfo->type_offload_esp; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if ((type && !try_module_get(type->owner))) 3928c2ecf20Sopenharmony_ci type = NULL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rcu_read_unlock(); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!type && try_load) { 3978c2ecf20Sopenharmony_ci request_module("xfrm-offload-%d-%d", family, proto); 3988c2ecf20Sopenharmony_ci try_load = false; 3998c2ecf20Sopenharmony_ci goto retry; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return type; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void xfrm_put_type_offload(const struct xfrm_type_offload *type) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci module_put(type->owner); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic const struct xfrm_mode xfrm4_mode_map[XFRM_MODE_MAX] = { 4118c2ecf20Sopenharmony_ci [XFRM_MODE_BEET] = { 4128c2ecf20Sopenharmony_ci .encap = XFRM_MODE_BEET, 4138c2ecf20Sopenharmony_ci .flags = XFRM_MODE_FLAG_TUNNEL, 4148c2ecf20Sopenharmony_ci .family = AF_INET, 4158c2ecf20Sopenharmony_ci }, 4168c2ecf20Sopenharmony_ci [XFRM_MODE_TRANSPORT] = { 4178c2ecf20Sopenharmony_ci .encap = XFRM_MODE_TRANSPORT, 4188c2ecf20Sopenharmony_ci .family = AF_INET, 4198c2ecf20Sopenharmony_ci }, 4208c2ecf20Sopenharmony_ci [XFRM_MODE_TUNNEL] = { 4218c2ecf20Sopenharmony_ci .encap = XFRM_MODE_TUNNEL, 4228c2ecf20Sopenharmony_ci .flags = XFRM_MODE_FLAG_TUNNEL, 4238c2ecf20Sopenharmony_ci .family = AF_INET, 4248c2ecf20Sopenharmony_ci }, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct xfrm_mode xfrm6_mode_map[XFRM_MODE_MAX] = { 4288c2ecf20Sopenharmony_ci [XFRM_MODE_BEET] = { 4298c2ecf20Sopenharmony_ci .encap = XFRM_MODE_BEET, 4308c2ecf20Sopenharmony_ci .flags = XFRM_MODE_FLAG_TUNNEL, 4318c2ecf20Sopenharmony_ci .family = AF_INET6, 4328c2ecf20Sopenharmony_ci }, 4338c2ecf20Sopenharmony_ci [XFRM_MODE_ROUTEOPTIMIZATION] = { 4348c2ecf20Sopenharmony_ci .encap = XFRM_MODE_ROUTEOPTIMIZATION, 4358c2ecf20Sopenharmony_ci .family = AF_INET6, 4368c2ecf20Sopenharmony_ci }, 4378c2ecf20Sopenharmony_ci [XFRM_MODE_TRANSPORT] = { 4388c2ecf20Sopenharmony_ci .encap = XFRM_MODE_TRANSPORT, 4398c2ecf20Sopenharmony_ci .family = AF_INET6, 4408c2ecf20Sopenharmony_ci }, 4418c2ecf20Sopenharmony_ci [XFRM_MODE_TUNNEL] = { 4428c2ecf20Sopenharmony_ci .encap = XFRM_MODE_TUNNEL, 4438c2ecf20Sopenharmony_ci .flags = XFRM_MODE_FLAG_TUNNEL, 4448c2ecf20Sopenharmony_ci .family = AF_INET6, 4458c2ecf20Sopenharmony_ci }, 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic const struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci const struct xfrm_mode *mode; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (unlikely(encap >= XFRM_MODE_MAX)) 4538c2ecf20Sopenharmony_ci return NULL; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (family) { 4568c2ecf20Sopenharmony_ci case AF_INET: 4578c2ecf20Sopenharmony_ci mode = &xfrm4_mode_map[encap]; 4588c2ecf20Sopenharmony_ci if (mode->family == family) 4598c2ecf20Sopenharmony_ci return mode; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case AF_INET6: 4628c2ecf20Sopenharmony_ci mode = &xfrm6_mode_map[encap]; 4638c2ecf20Sopenharmony_ci if (mode->family == family) 4648c2ecf20Sopenharmony_ci return mode; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci default: 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return NULL; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid xfrm_state_free(struct xfrm_state *x) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci kmem_cache_free(xfrm_state_cache, x); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_free); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void ___xfrm_state_destroy(struct xfrm_state *x) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci hrtimer_cancel(&x->mtimer); 4828c2ecf20Sopenharmony_ci del_timer_sync(&x->rtimer); 4838c2ecf20Sopenharmony_ci kfree(x->aead); 4848c2ecf20Sopenharmony_ci kfree(x->aalg); 4858c2ecf20Sopenharmony_ci kfree(x->ealg); 4868c2ecf20Sopenharmony_ci kfree(x->calg); 4878c2ecf20Sopenharmony_ci kfree(x->encap); 4888c2ecf20Sopenharmony_ci kfree(x->coaddr); 4898c2ecf20Sopenharmony_ci kfree(x->replay_esn); 4908c2ecf20Sopenharmony_ci kfree(x->preplay_esn); 4918c2ecf20Sopenharmony_ci if (x->type_offload) 4928c2ecf20Sopenharmony_ci xfrm_put_type_offload(x->type_offload); 4938c2ecf20Sopenharmony_ci if (x->type) { 4948c2ecf20Sopenharmony_ci x->type->destructor(x); 4958c2ecf20Sopenharmony_ci xfrm_put_type(x->type); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci if (x->xfrag.page) 4988c2ecf20Sopenharmony_ci put_page(x->xfrag.page); 4998c2ecf20Sopenharmony_ci xfrm_dev_state_free(x); 5008c2ecf20Sopenharmony_ci security_xfrm_state_free(x); 5018c2ecf20Sopenharmony_ci xfrm_state_free(x); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void xfrm_state_gc_task(struct work_struct *work) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct xfrm_state *x; 5078c2ecf20Sopenharmony_ci struct hlist_node *tmp; 5088c2ecf20Sopenharmony_ci struct hlist_head gc_list; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_state_gc_lock); 5118c2ecf20Sopenharmony_ci hlist_move_list(&xfrm_state_gc_list, &gc_list); 5128c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_state_gc_lock); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci synchronize_rcu(); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(x, tmp, &gc_list, gclist) 5178c2ecf20Sopenharmony_ci ___xfrm_state_destroy(x); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct xfrm_state *x = container_of(me, struct xfrm_state, mtimer); 5238c2ecf20Sopenharmony_ci enum hrtimer_restart ret = HRTIMER_NORESTART; 5248c2ecf20Sopenharmony_ci time64_t now = ktime_get_real_seconds(); 5258c2ecf20Sopenharmony_ci time64_t next = TIME64_MAX; 5268c2ecf20Sopenharmony_ci int warn = 0; 5278c2ecf20Sopenharmony_ci int err = 0; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci spin_lock(&x->lock); 5308c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_DEAD) 5318c2ecf20Sopenharmony_ci goto out; 5328c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_EXPIRED) 5338c2ecf20Sopenharmony_ci goto expired; 5348c2ecf20Sopenharmony_ci if (x->lft.hard_add_expires_seconds) { 5358c2ecf20Sopenharmony_ci long tmo = x->lft.hard_add_expires_seconds + 5368c2ecf20Sopenharmony_ci x->curlft.add_time - now; 5378c2ecf20Sopenharmony_ci if (tmo <= 0) { 5388c2ecf20Sopenharmony_ci if (x->xflags & XFRM_SOFT_EXPIRE) { 5398c2ecf20Sopenharmony_ci /* enter hard expire without soft expire first?! 5408c2ecf20Sopenharmony_ci * setting a new date could trigger this. 5418c2ecf20Sopenharmony_ci * workaround: fix x->curflt.add_time by below: 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci x->curlft.add_time = now - x->saved_tmo - 1; 5448c2ecf20Sopenharmony_ci tmo = x->lft.hard_add_expires_seconds - x->saved_tmo; 5458c2ecf20Sopenharmony_ci } else 5468c2ecf20Sopenharmony_ci goto expired; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci if (tmo < next) 5498c2ecf20Sopenharmony_ci next = tmo; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci if (x->lft.hard_use_expires_seconds) { 5528c2ecf20Sopenharmony_ci long tmo = x->lft.hard_use_expires_seconds + 5538c2ecf20Sopenharmony_ci (x->curlft.use_time ? : now) - now; 5548c2ecf20Sopenharmony_ci if (tmo <= 0) 5558c2ecf20Sopenharmony_ci goto expired; 5568c2ecf20Sopenharmony_ci if (tmo < next) 5578c2ecf20Sopenharmony_ci next = tmo; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci if (x->km.dying) 5608c2ecf20Sopenharmony_ci goto resched; 5618c2ecf20Sopenharmony_ci if (x->lft.soft_add_expires_seconds) { 5628c2ecf20Sopenharmony_ci long tmo = x->lft.soft_add_expires_seconds + 5638c2ecf20Sopenharmony_ci x->curlft.add_time - now; 5648c2ecf20Sopenharmony_ci if (tmo <= 0) { 5658c2ecf20Sopenharmony_ci warn = 1; 5668c2ecf20Sopenharmony_ci x->xflags &= ~XFRM_SOFT_EXPIRE; 5678c2ecf20Sopenharmony_ci } else if (tmo < next) { 5688c2ecf20Sopenharmony_ci next = tmo; 5698c2ecf20Sopenharmony_ci x->xflags |= XFRM_SOFT_EXPIRE; 5708c2ecf20Sopenharmony_ci x->saved_tmo = tmo; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci if (x->lft.soft_use_expires_seconds) { 5748c2ecf20Sopenharmony_ci long tmo = x->lft.soft_use_expires_seconds + 5758c2ecf20Sopenharmony_ci (x->curlft.use_time ? : now) - now; 5768c2ecf20Sopenharmony_ci if (tmo <= 0) 5778c2ecf20Sopenharmony_ci warn = 1; 5788c2ecf20Sopenharmony_ci else if (tmo < next) 5798c2ecf20Sopenharmony_ci next = tmo; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci x->km.dying = warn; 5838c2ecf20Sopenharmony_ci if (warn) 5848c2ecf20Sopenharmony_ci km_state_expired(x, 0, 0); 5858c2ecf20Sopenharmony_ciresched: 5868c2ecf20Sopenharmony_ci if (next != TIME64_MAX) { 5878c2ecf20Sopenharmony_ci hrtimer_forward_now(&x->mtimer, ktime_set(next, 0)); 5888c2ecf20Sopenharmony_ci ret = HRTIMER_RESTART; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci goto out; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciexpired: 5948c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) 5958c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_EXPIRED; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci err = __xfrm_state_delete(x); 5988c2ecf20Sopenharmony_ci if (!err) 5998c2ecf20Sopenharmony_ci km_state_expired(x, 1, 0); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci xfrm_audit_state_delete(x, err ? 0 : 1, true); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciout: 6048c2ecf20Sopenharmony_ci spin_unlock(&x->lock); 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void xfrm_replay_timer_handler(struct timer_list *t); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistruct xfrm_state *xfrm_state_alloc(struct net *net) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct xfrm_state *x; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci x = kmem_cache_zalloc(xfrm_state_cache, GFP_ATOMIC); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (x) { 6178c2ecf20Sopenharmony_ci write_pnet(&x->xs_net, net); 6188c2ecf20Sopenharmony_ci refcount_set(&x->refcnt, 1); 6198c2ecf20Sopenharmony_ci atomic_set(&x->tunnel_users, 0); 6208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&x->km.all); 6218c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&x->bydst); 6228c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&x->bysrc); 6238c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&x->byspi); 6248c2ecf20Sopenharmony_ci hrtimer_init(&x->mtimer, CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); 6258c2ecf20Sopenharmony_ci x->mtimer.function = xfrm_timer_handler; 6268c2ecf20Sopenharmony_ci timer_setup(&x->rtimer, xfrm_replay_timer_handler, 0); 6278c2ecf20Sopenharmony_ci x->curlft.add_time = ktime_get_real_seconds(); 6288c2ecf20Sopenharmony_ci x->lft.soft_byte_limit = XFRM_INF; 6298c2ecf20Sopenharmony_ci x->lft.soft_packet_limit = XFRM_INF; 6308c2ecf20Sopenharmony_ci x->lft.hard_byte_limit = XFRM_INF; 6318c2ecf20Sopenharmony_ci x->lft.hard_packet_limit = XFRM_INF; 6328c2ecf20Sopenharmony_ci x->replay_maxage = 0; 6338c2ecf20Sopenharmony_ci x->replay_maxdiff = 0; 6348c2ecf20Sopenharmony_ci spin_lock_init(&x->lock); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci return x; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_alloc); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_civoid __xfrm_state_destroy(struct xfrm_state *x, bool sync) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci WARN_ON(x->km.state != XFRM_STATE_DEAD); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (sync) { 6458c2ecf20Sopenharmony_ci synchronize_rcu(); 6468c2ecf20Sopenharmony_ci ___xfrm_state_destroy(x); 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_state_gc_lock); 6498c2ecf20Sopenharmony_ci hlist_add_head(&x->gclist, &xfrm_state_gc_list); 6508c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_state_gc_lock); 6518c2ecf20Sopenharmony_ci schedule_work(&xfrm_state_gc_work); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_state_destroy); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ciint __xfrm_state_delete(struct xfrm_state *x) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 6598c2ecf20Sopenharmony_ci int err = -ESRCH; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (x->km.state != XFRM_STATE_DEAD) { 6628c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_DEAD; 6638c2ecf20Sopenharmony_ci spin_lock(&net->xfrm.xfrm_state_lock); 6648c2ecf20Sopenharmony_ci list_del(&x->km.all); 6658c2ecf20Sopenharmony_ci hlist_del_rcu(&x->bydst); 6668c2ecf20Sopenharmony_ci hlist_del_rcu(&x->bysrc); 6678c2ecf20Sopenharmony_ci if (x->id.spi) 6688c2ecf20Sopenharmony_ci hlist_del_rcu(&x->byspi); 6698c2ecf20Sopenharmony_ci net->xfrm.state_num--; 6708c2ecf20Sopenharmony_ci spin_unlock(&net->xfrm.xfrm_state_lock); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (x->encap_sk) 6738c2ecf20Sopenharmony_ci sock_put(rcu_dereference_raw(x->encap_sk)); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci xfrm_dev_state_delete(x); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* All xfrm_state objects are created by xfrm_state_alloc. 6788c2ecf20Sopenharmony_ci * The xfrm_state_alloc call gives a reference, and that 6798c2ecf20Sopenharmony_ci * is what we are dropping here. 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci xfrm_state_put(x); 6828c2ecf20Sopenharmony_ci err = 0; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return err; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_state_delete); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ciint xfrm_state_delete(struct xfrm_state *x) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci int err; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 6948c2ecf20Sopenharmony_ci err = __xfrm_state_delete(x); 6958c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return err; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_delete); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_NETWORK_XFRM 7028c2ecf20Sopenharmony_cistatic inline int 7038c2ecf20Sopenharmony_cixfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci int i, err = 0; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci for (i = 0; i <= net->xfrm.state_hmask; i++) { 7088c2ecf20Sopenharmony_ci struct xfrm_state *x; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { 7118c2ecf20Sopenharmony_ci if (xfrm_id_proto_match(x->id.proto, proto) && 7128c2ecf20Sopenharmony_ci (err = security_xfrm_state_delete(x)) != 0) { 7138c2ecf20Sopenharmony_ci xfrm_audit_state_delete(x, 0, task_valid); 7148c2ecf20Sopenharmony_ci return err; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci return err; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic inline int 7238c2ecf20Sopenharmony_cixfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci int i, err = 0; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (i = 0; i <= net->xfrm.state_hmask; i++) { 7288c2ecf20Sopenharmony_ci struct xfrm_state *x; 7298c2ecf20Sopenharmony_ci struct xfrm_state_offload *xso; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { 7328c2ecf20Sopenharmony_ci xso = &x->xso; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (xso->dev == dev && 7358c2ecf20Sopenharmony_ci (err = security_xfrm_state_delete(x)) != 0) { 7368c2ecf20Sopenharmony_ci xfrm_audit_state_delete(x, 0, task_valid); 7378c2ecf20Sopenharmony_ci return err; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci#else 7458c2ecf20Sopenharmony_cistatic inline int 7468c2ecf20Sopenharmony_cixfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci return 0; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic inline int 7528c2ecf20Sopenharmony_cixfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci#endif 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ciint xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int i, err = 0, cnt = 0; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 7638c2ecf20Sopenharmony_ci err = xfrm_state_flush_secctx_check(net, proto, task_valid); 7648c2ecf20Sopenharmony_ci if (err) 7658c2ecf20Sopenharmony_ci goto out; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci err = -ESRCH; 7688c2ecf20Sopenharmony_ci for (i = 0; i <= net->xfrm.state_hmask; i++) { 7698c2ecf20Sopenharmony_ci struct xfrm_state *x; 7708c2ecf20Sopenharmony_cirestart: 7718c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { 7728c2ecf20Sopenharmony_ci if (!xfrm_state_kern(x) && 7738c2ecf20Sopenharmony_ci xfrm_id_proto_match(x->id.proto, proto)) { 7748c2ecf20Sopenharmony_ci xfrm_state_hold(x); 7758c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci err = xfrm_state_delete(x); 7788c2ecf20Sopenharmony_ci xfrm_audit_state_delete(x, err ? 0 : 1, 7798c2ecf20Sopenharmony_ci task_valid); 7808c2ecf20Sopenharmony_ci if (sync) 7818c2ecf20Sopenharmony_ci xfrm_state_put_sync(x); 7828c2ecf20Sopenharmony_ci else 7838c2ecf20Sopenharmony_ci xfrm_state_put(x); 7848c2ecf20Sopenharmony_ci if (!err) 7858c2ecf20Sopenharmony_ci cnt++; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 7888c2ecf20Sopenharmony_ci goto restart; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ciout: 7938c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 7948c2ecf20Sopenharmony_ci if (cnt) 7958c2ecf20Sopenharmony_ci err = 0; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return err; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_flush); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ciint xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci int i, err = 0, cnt = 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 8068c2ecf20Sopenharmony_ci err = xfrm_dev_state_flush_secctx_check(net, dev, task_valid); 8078c2ecf20Sopenharmony_ci if (err) 8088c2ecf20Sopenharmony_ci goto out; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci err = -ESRCH; 8118c2ecf20Sopenharmony_ci for (i = 0; i <= net->xfrm.state_hmask; i++) { 8128c2ecf20Sopenharmony_ci struct xfrm_state *x; 8138c2ecf20Sopenharmony_ci struct xfrm_state_offload *xso; 8148c2ecf20Sopenharmony_cirestart: 8158c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { 8168c2ecf20Sopenharmony_ci xso = &x->xso; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (!xfrm_state_kern(x) && xso->dev == dev) { 8198c2ecf20Sopenharmony_ci xfrm_state_hold(x); 8208c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci err = xfrm_state_delete(x); 8238c2ecf20Sopenharmony_ci xfrm_audit_state_delete(x, err ? 0 : 1, 8248c2ecf20Sopenharmony_ci task_valid); 8258c2ecf20Sopenharmony_ci xfrm_state_put(x); 8268c2ecf20Sopenharmony_ci if (!err) 8278c2ecf20Sopenharmony_ci cnt++; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 8308c2ecf20Sopenharmony_ci goto restart; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci if (cnt) 8358c2ecf20Sopenharmony_ci err = 0; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ciout: 8388c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 8398c2ecf20Sopenharmony_ci return err; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_dev_state_flush); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_civoid xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 8468c2ecf20Sopenharmony_ci si->sadcnt = net->xfrm.state_num; 8478c2ecf20Sopenharmony_ci si->sadhcnt = net->xfrm.state_hmask + 1; 8488c2ecf20Sopenharmony_ci si->sadhmcnt = xfrm_state_hashmax; 8498c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_sad_getinfo); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic void 8548c2ecf20Sopenharmony_ci__xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci const struct flowi4 *fl4 = &fl->u.ip4; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci sel->daddr.a4 = fl4->daddr; 8598c2ecf20Sopenharmony_ci sel->saddr.a4 = fl4->saddr; 8608c2ecf20Sopenharmony_ci sel->dport = xfrm_flowi_dport(fl, &fl4->uli); 8618c2ecf20Sopenharmony_ci sel->dport_mask = htons(0xffff); 8628c2ecf20Sopenharmony_ci sel->sport = xfrm_flowi_sport(fl, &fl4->uli); 8638c2ecf20Sopenharmony_ci sel->sport_mask = htons(0xffff); 8648c2ecf20Sopenharmony_ci sel->family = AF_INET; 8658c2ecf20Sopenharmony_ci sel->prefixlen_d = 32; 8668c2ecf20Sopenharmony_ci sel->prefixlen_s = 32; 8678c2ecf20Sopenharmony_ci sel->proto = fl4->flowi4_proto; 8688c2ecf20Sopenharmony_ci sel->ifindex = fl4->flowi4_oif; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic void 8728c2ecf20Sopenharmony_ci__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci const struct flowi6 *fl6 = &fl->u.ip6; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* Initialize temporary selector matching only to current session. */ 8778c2ecf20Sopenharmony_ci *(struct in6_addr *)&sel->daddr = fl6->daddr; 8788c2ecf20Sopenharmony_ci *(struct in6_addr *)&sel->saddr = fl6->saddr; 8798c2ecf20Sopenharmony_ci sel->dport = xfrm_flowi_dport(fl, &fl6->uli); 8808c2ecf20Sopenharmony_ci sel->dport_mask = htons(0xffff); 8818c2ecf20Sopenharmony_ci sel->sport = xfrm_flowi_sport(fl, &fl6->uli); 8828c2ecf20Sopenharmony_ci sel->sport_mask = htons(0xffff); 8838c2ecf20Sopenharmony_ci sel->family = AF_INET6; 8848c2ecf20Sopenharmony_ci sel->prefixlen_d = 128; 8858c2ecf20Sopenharmony_ci sel->prefixlen_s = 128; 8868c2ecf20Sopenharmony_ci sel->proto = fl6->flowi6_proto; 8878c2ecf20Sopenharmony_ci sel->ifindex = fl6->flowi6_oif; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void 8918c2ecf20Sopenharmony_cixfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl, 8928c2ecf20Sopenharmony_ci const struct xfrm_tmpl *tmpl, 8938c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, const xfrm_address_t *saddr, 8948c2ecf20Sopenharmony_ci unsigned short family) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci switch (family) { 8978c2ecf20Sopenharmony_ci case AF_INET: 8988c2ecf20Sopenharmony_ci __xfrm4_init_tempsel(&x->sel, fl); 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci case AF_INET6: 9018c2ecf20Sopenharmony_ci __xfrm6_init_tempsel(&x->sel, fl); 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci x->id = tmpl->id; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci switch (tmpl->encap_family) { 9088c2ecf20Sopenharmony_ci case AF_INET: 9098c2ecf20Sopenharmony_ci if (x->id.daddr.a4 == 0) 9108c2ecf20Sopenharmony_ci x->id.daddr.a4 = daddr->a4; 9118c2ecf20Sopenharmony_ci x->props.saddr = tmpl->saddr; 9128c2ecf20Sopenharmony_ci if (x->props.saddr.a4 == 0) 9138c2ecf20Sopenharmony_ci x->props.saddr.a4 = saddr->a4; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci case AF_INET6: 9168c2ecf20Sopenharmony_ci if (ipv6_addr_any((struct in6_addr *)&x->id.daddr)) 9178c2ecf20Sopenharmony_ci memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); 9188c2ecf20Sopenharmony_ci memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); 9198c2ecf20Sopenharmony_ci if (ipv6_addr_any((struct in6_addr *)&x->props.saddr)) 9208c2ecf20Sopenharmony_ci memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr)); 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci x->props.mode = tmpl->mode; 9258c2ecf20Sopenharmony_ci x->props.reqid = tmpl->reqid; 9268c2ecf20Sopenharmony_ci x->props.family = tmpl->encap_family; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, 9308c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 9318c2ecf20Sopenharmony_ci __be32 spi, u8 proto, 9328c2ecf20Sopenharmony_ci unsigned short family) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family); 9358c2ecf20Sopenharmony_ci struct xfrm_state *x; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) { 9388c2ecf20Sopenharmony_ci if (x->props.family != family || 9398c2ecf20Sopenharmony_ci x->id.spi != spi || 9408c2ecf20Sopenharmony_ci x->id.proto != proto || 9418c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->id.daddr, daddr, family)) 9428c2ecf20Sopenharmony_ci continue; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if ((mark & x->mark.m) != x->mark.v) 9458c2ecf20Sopenharmony_ci continue; 9468c2ecf20Sopenharmony_ci if (!xfrm_state_hold_rcu(x)) 9478c2ecf20Sopenharmony_ci continue; 9488c2ecf20Sopenharmony_ci return x; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci return NULL; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark, 9558c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 9568c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 9578c2ecf20Sopenharmony_ci u8 proto, unsigned short family) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci unsigned int h = xfrm_src_hash(net, daddr, saddr, family); 9608c2ecf20Sopenharmony_ci struct xfrm_state *x; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(x, net->xfrm.state_bysrc + h, bysrc) { 9638c2ecf20Sopenharmony_ci if (x->props.family != family || 9648c2ecf20Sopenharmony_ci x->id.proto != proto || 9658c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->id.daddr, daddr, family) || 9668c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->props.saddr, saddr, family)) 9678c2ecf20Sopenharmony_ci continue; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if ((mark & x->mark.m) != x->mark.v) 9708c2ecf20Sopenharmony_ci continue; 9718c2ecf20Sopenharmony_ci if (!xfrm_state_hold_rcu(x)) 9728c2ecf20Sopenharmony_ci continue; 9738c2ecf20Sopenharmony_ci return x; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return NULL; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic inline struct xfrm_state * 9808c2ecf20Sopenharmony_ci__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 9838c2ecf20Sopenharmony_ci u32 mark = x->mark.v & x->mark.m; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (use_spi) 9868c2ecf20Sopenharmony_ci return __xfrm_state_lookup(net, mark, &x->id.daddr, 9878c2ecf20Sopenharmony_ci x->id.spi, x->id.proto, family); 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci return __xfrm_state_lookup_byaddr(net, mark, 9908c2ecf20Sopenharmony_ci &x->id.daddr, 9918c2ecf20Sopenharmony_ci &x->props.saddr, 9928c2ecf20Sopenharmony_ci x->id.proto, family); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void xfrm_hash_grow_check(struct net *net, int have_hash_collision) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci if (have_hash_collision && 9988c2ecf20Sopenharmony_ci (net->xfrm.state_hmask + 1) < xfrm_state_hashmax && 9998c2ecf20Sopenharmony_ci net->xfrm.state_num > net->xfrm.state_hmask) 10008c2ecf20Sopenharmony_ci schedule_work(&net->xfrm.state_hash_work); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, 10048c2ecf20Sopenharmony_ci const struct flowi *fl, unsigned short family, 10058c2ecf20Sopenharmony_ci struct xfrm_state **best, int *acq_in_progress, 10068c2ecf20Sopenharmony_ci int *error) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci /* Resolution logic: 10098c2ecf20Sopenharmony_ci * 1. There is a valid state with matching selector. Done. 10108c2ecf20Sopenharmony_ci * 2. Valid state with inappropriate selector. Skip. 10118c2ecf20Sopenharmony_ci * 10128c2ecf20Sopenharmony_ci * Entering area of "sysdeps". 10138c2ecf20Sopenharmony_ci * 10148c2ecf20Sopenharmony_ci * 3. If state is not valid, selector is temporary, it selects 10158c2ecf20Sopenharmony_ci * only session which triggered previous resolution. Key 10168c2ecf20Sopenharmony_ci * manager will do something to install a state with proper 10178c2ecf20Sopenharmony_ci * selector. 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_VALID) { 10208c2ecf20Sopenharmony_ci if ((x->sel.family && 10218c2ecf20Sopenharmony_ci (x->sel.family != family || 10228c2ecf20Sopenharmony_ci !xfrm_selector_match(&x->sel, fl, family))) || 10238c2ecf20Sopenharmony_ci !security_xfrm_state_pol_flow_match(x, pol, 10248c2ecf20Sopenharmony_ci &fl->u.__fl_common)) 10258c2ecf20Sopenharmony_ci return; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (!*best || 10288c2ecf20Sopenharmony_ci (*best)->km.dying > x->km.dying || 10298c2ecf20Sopenharmony_ci ((*best)->km.dying == x->km.dying && 10308c2ecf20Sopenharmony_ci (*best)->curlft.add_time < x->curlft.add_time)) 10318c2ecf20Sopenharmony_ci *best = x; 10328c2ecf20Sopenharmony_ci } else if (x->km.state == XFRM_STATE_ACQ) { 10338c2ecf20Sopenharmony_ci *acq_in_progress = 1; 10348c2ecf20Sopenharmony_ci } else if (x->km.state == XFRM_STATE_ERROR || 10358c2ecf20Sopenharmony_ci x->km.state == XFRM_STATE_EXPIRED) { 10368c2ecf20Sopenharmony_ci if ((!x->sel.family || 10378c2ecf20Sopenharmony_ci (x->sel.family == family && 10388c2ecf20Sopenharmony_ci xfrm_selector_match(&x->sel, fl, family))) && 10398c2ecf20Sopenharmony_ci security_xfrm_state_pol_flow_match(x, pol, 10408c2ecf20Sopenharmony_ci &fl->u.__fl_common)) 10418c2ecf20Sopenharmony_ci *error = -ESRCH; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistruct xfrm_state * 10468c2ecf20Sopenharmony_cixfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, 10478c2ecf20Sopenharmony_ci const struct flowi *fl, struct xfrm_tmpl *tmpl, 10488c2ecf20Sopenharmony_ci struct xfrm_policy *pol, int *err, 10498c2ecf20Sopenharmony_ci unsigned short family, u32 if_id) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci static xfrm_address_t saddr_wildcard = { }; 10528c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 10538c2ecf20Sopenharmony_ci unsigned int h, h_wildcard; 10548c2ecf20Sopenharmony_ci struct xfrm_state *x, *x0, *to_put; 10558c2ecf20Sopenharmony_ci int acquire_in_progress = 0; 10568c2ecf20Sopenharmony_ci int error = 0; 10578c2ecf20Sopenharmony_ci struct xfrm_state *best = NULL; 10588c2ecf20Sopenharmony_ci u32 mark = pol->mark.v & pol->mark.m; 10598c2ecf20Sopenharmony_ci unsigned short encap_family = tmpl->encap_family; 10608c2ecf20Sopenharmony_ci unsigned int sequence; 10618c2ecf20Sopenharmony_ci struct km_event c; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci to_put = NULL; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci rcu_read_lock(); 10688c2ecf20Sopenharmony_ci h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family); 10698c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) { 10708c2ecf20Sopenharmony_ci if (x->props.family == encap_family && 10718c2ecf20Sopenharmony_ci x->props.reqid == tmpl->reqid && 10728c2ecf20Sopenharmony_ci (mark & x->mark.m) == x->mark.v && 10738c2ecf20Sopenharmony_ci x->if_id == if_id && 10748c2ecf20Sopenharmony_ci !(x->props.flags & XFRM_STATE_WILDRECV) && 10758c2ecf20Sopenharmony_ci xfrm_state_addr_check(x, daddr, saddr, encap_family) && 10768c2ecf20Sopenharmony_ci tmpl->mode == x->props.mode && 10778c2ecf20Sopenharmony_ci tmpl->id.proto == x->id.proto && 10788c2ecf20Sopenharmony_ci (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) 10798c2ecf20Sopenharmony_ci xfrm_state_look_at(pol, x, fl, family, 10808c2ecf20Sopenharmony_ci &best, &acquire_in_progress, &error); 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci if (best || acquire_in_progress) 10838c2ecf20Sopenharmony_ci goto found; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); 10868c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) { 10878c2ecf20Sopenharmony_ci if (x->props.family == encap_family && 10888c2ecf20Sopenharmony_ci x->props.reqid == tmpl->reqid && 10898c2ecf20Sopenharmony_ci (mark & x->mark.m) == x->mark.v && 10908c2ecf20Sopenharmony_ci x->if_id == if_id && 10918c2ecf20Sopenharmony_ci !(x->props.flags & XFRM_STATE_WILDRECV) && 10928c2ecf20Sopenharmony_ci xfrm_addr_equal(&x->id.daddr, daddr, encap_family) && 10938c2ecf20Sopenharmony_ci tmpl->mode == x->props.mode && 10948c2ecf20Sopenharmony_ci tmpl->id.proto == x->id.proto && 10958c2ecf20Sopenharmony_ci (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) 10968c2ecf20Sopenharmony_ci xfrm_state_look_at(pol, x, fl, family, 10978c2ecf20Sopenharmony_ci &best, &acquire_in_progress, &error); 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cifound: 11018c2ecf20Sopenharmony_ci x = best; 11028c2ecf20Sopenharmony_ci if (!x && !error && !acquire_in_progress) { 11038c2ecf20Sopenharmony_ci if (tmpl->id.spi && 11048c2ecf20Sopenharmony_ci (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, 11058c2ecf20Sopenharmony_ci tmpl->id.proto, encap_family)) != NULL) { 11068c2ecf20Sopenharmony_ci to_put = x0; 11078c2ecf20Sopenharmony_ci error = -EEXIST; 11088c2ecf20Sopenharmony_ci goto out; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci c.net = net; 11128c2ecf20Sopenharmony_ci /* If the KMs have no listeners (yet...), avoid allocating an SA 11138c2ecf20Sopenharmony_ci * for each and every packet - garbage collection might not 11148c2ecf20Sopenharmony_ci * handle the flood. 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci if (!km_is_alive(&c)) { 11178c2ecf20Sopenharmony_ci error = -ESRCH; 11188c2ecf20Sopenharmony_ci goto out; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci x = xfrm_state_alloc(net); 11228c2ecf20Sopenharmony_ci if (x == NULL) { 11238c2ecf20Sopenharmony_ci error = -ENOMEM; 11248c2ecf20Sopenharmony_ci goto out; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci /* Initialize temporary state matching only 11278c2ecf20Sopenharmony_ci * to current session. */ 11288c2ecf20Sopenharmony_ci xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family); 11298c2ecf20Sopenharmony_ci memcpy(&x->mark, &pol->mark, sizeof(x->mark)); 11308c2ecf20Sopenharmony_ci x->if_id = if_id; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid); 11338c2ecf20Sopenharmony_ci if (error) { 11348c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_DEAD; 11358c2ecf20Sopenharmony_ci to_put = x; 11368c2ecf20Sopenharmony_ci x = NULL; 11378c2ecf20Sopenharmony_ci goto out; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (km_query(x, tmpl, pol) == 0) { 11418c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 11428c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_ACQ; 11438c2ecf20Sopenharmony_ci list_add(&x->km.all, &net->xfrm.state_all); 11448c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); 11458c2ecf20Sopenharmony_ci h = xfrm_src_hash(net, daddr, saddr, encap_family); 11468c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); 11478c2ecf20Sopenharmony_ci if (x->id.spi) { 11488c2ecf20Sopenharmony_ci h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family); 11498c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; 11528c2ecf20Sopenharmony_ci hrtimer_start(&x->mtimer, 11538c2ecf20Sopenharmony_ci ktime_set(net->xfrm.sysctl_acq_expires, 0), 11548c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 11558c2ecf20Sopenharmony_ci net->xfrm.state_num++; 11568c2ecf20Sopenharmony_ci xfrm_hash_grow_check(net, x->bydst.next != NULL); 11578c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_DEAD; 11608c2ecf20Sopenharmony_ci to_put = x; 11618c2ecf20Sopenharmony_ci x = NULL; 11628c2ecf20Sopenharmony_ci error = -ESRCH; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ciout: 11668c2ecf20Sopenharmony_ci if (x) { 11678c2ecf20Sopenharmony_ci if (!xfrm_state_hold_rcu(x)) { 11688c2ecf20Sopenharmony_ci *err = -EAGAIN; 11698c2ecf20Sopenharmony_ci x = NULL; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci } else { 11728c2ecf20Sopenharmony_ci *err = acquire_in_progress ? -EAGAIN : error; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci rcu_read_unlock(); 11758c2ecf20Sopenharmony_ci if (to_put) 11768c2ecf20Sopenharmony_ci xfrm_state_put(to_put); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (read_seqcount_retry(&net->xfrm.xfrm_state_hash_generation, sequence)) { 11798c2ecf20Sopenharmony_ci *err = -EAGAIN; 11808c2ecf20Sopenharmony_ci if (x) { 11818c2ecf20Sopenharmony_ci xfrm_state_put(x); 11828c2ecf20Sopenharmony_ci x = NULL; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return x; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistruct xfrm_state * 11908c2ecf20Sopenharmony_cixfrm_stateonly_find(struct net *net, u32 mark, u32 if_id, 11918c2ecf20Sopenharmony_ci xfrm_address_t *daddr, xfrm_address_t *saddr, 11928c2ecf20Sopenharmony_ci unsigned short family, u8 mode, u8 proto, u32 reqid) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci unsigned int h; 11958c2ecf20Sopenharmony_ci struct xfrm_state *rx = NULL, *x = NULL; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 11988c2ecf20Sopenharmony_ci h = xfrm_dst_hash(net, daddr, saddr, reqid, family); 11998c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { 12008c2ecf20Sopenharmony_ci if (x->props.family == family && 12018c2ecf20Sopenharmony_ci x->props.reqid == reqid && 12028c2ecf20Sopenharmony_ci (mark & x->mark.m) == x->mark.v && 12038c2ecf20Sopenharmony_ci x->if_id == if_id && 12048c2ecf20Sopenharmony_ci !(x->props.flags & XFRM_STATE_WILDRECV) && 12058c2ecf20Sopenharmony_ci xfrm_state_addr_check(x, daddr, saddr, family) && 12068c2ecf20Sopenharmony_ci mode == x->props.mode && 12078c2ecf20Sopenharmony_ci proto == x->id.proto && 12088c2ecf20Sopenharmony_ci x->km.state == XFRM_STATE_VALID) { 12098c2ecf20Sopenharmony_ci rx = x; 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (rx) 12158c2ecf20Sopenharmony_ci xfrm_state_hold(rx); 12168c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return rx; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_stateonly_find); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistruct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, 12248c2ecf20Sopenharmony_ci unsigned short family) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct xfrm_state *x; 12278c2ecf20Sopenharmony_ci struct xfrm_state_walk *w; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 12308c2ecf20Sopenharmony_ci list_for_each_entry(w, &net->xfrm.state_all, all) { 12318c2ecf20Sopenharmony_ci x = container_of(w, struct xfrm_state, km); 12328c2ecf20Sopenharmony_ci if (x->props.family != family || 12338c2ecf20Sopenharmony_ci x->id.spi != spi) 12348c2ecf20Sopenharmony_ci continue; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci xfrm_state_hold(x); 12378c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 12388c2ecf20Sopenharmony_ci return x; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 12418c2ecf20Sopenharmony_ci return NULL; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_lookup_byspi); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic void __xfrm_state_insert(struct xfrm_state *x) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 12488c2ecf20Sopenharmony_ci unsigned int h; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci list_add(&x->km.all, &net->xfrm.state_all); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr, 12538c2ecf20Sopenharmony_ci x->props.reqid, x->props.family); 12548c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family); 12578c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (x->id.spi) { 12608c2ecf20Sopenharmony_ci h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, 12618c2ecf20Sopenharmony_ci x->props.family); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT); 12678c2ecf20Sopenharmony_ci if (x->replay_maxage) 12688c2ecf20Sopenharmony_ci mod_timer(&x->rtimer, jiffies + x->replay_maxage); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci net->xfrm.state_num++; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci xfrm_hash_grow_check(net, x->bydst.next != NULL); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci/* net->xfrm.xfrm_state_lock is held */ 12768c2ecf20Sopenharmony_cistatic void __xfrm_state_bump_genids(struct xfrm_state *xnew) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci struct net *net = xs_net(xnew); 12798c2ecf20Sopenharmony_ci unsigned short family = xnew->props.family; 12808c2ecf20Sopenharmony_ci u32 reqid = xnew->props.reqid; 12818c2ecf20Sopenharmony_ci struct xfrm_state *x; 12828c2ecf20Sopenharmony_ci unsigned int h; 12838c2ecf20Sopenharmony_ci u32 mark = xnew->mark.v & xnew->mark.m; 12848c2ecf20Sopenharmony_ci u32 if_id = xnew->if_id; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family); 12878c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { 12888c2ecf20Sopenharmony_ci if (x->props.family == family && 12898c2ecf20Sopenharmony_ci x->props.reqid == reqid && 12908c2ecf20Sopenharmony_ci x->if_id == if_id && 12918c2ecf20Sopenharmony_ci (mark & x->mark.m) == x->mark.v && 12928c2ecf20Sopenharmony_ci xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) && 12938c2ecf20Sopenharmony_ci xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family)) 12948c2ecf20Sopenharmony_ci x->genid++; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_civoid xfrm_state_insert(struct xfrm_state *x) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 13038c2ecf20Sopenharmony_ci __xfrm_state_bump_genids(x); 13048c2ecf20Sopenharmony_ci __xfrm_state_insert(x); 13058c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_insert); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci/* net->xfrm.xfrm_state_lock is held */ 13108c2ecf20Sopenharmony_cistatic struct xfrm_state *__find_acq_core(struct net *net, 13118c2ecf20Sopenharmony_ci const struct xfrm_mark *m, 13128c2ecf20Sopenharmony_ci unsigned short family, u8 mode, 13138c2ecf20Sopenharmony_ci u32 reqid, u32 if_id, u8 proto, 13148c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 13158c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 13168c2ecf20Sopenharmony_ci int create) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family); 13198c2ecf20Sopenharmony_ci struct xfrm_state *x; 13208c2ecf20Sopenharmony_ci u32 mark = m->v & m->m; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { 13238c2ecf20Sopenharmony_ci if (x->props.reqid != reqid || 13248c2ecf20Sopenharmony_ci x->props.mode != mode || 13258c2ecf20Sopenharmony_ci x->props.family != family || 13268c2ecf20Sopenharmony_ci x->km.state != XFRM_STATE_ACQ || 13278c2ecf20Sopenharmony_ci x->id.spi != 0 || 13288c2ecf20Sopenharmony_ci x->id.proto != proto || 13298c2ecf20Sopenharmony_ci (mark & x->mark.m) != x->mark.v || 13308c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->id.daddr, daddr, family) || 13318c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->props.saddr, saddr, family)) 13328c2ecf20Sopenharmony_ci continue; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci xfrm_state_hold(x); 13358c2ecf20Sopenharmony_ci return x; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (!create) 13398c2ecf20Sopenharmony_ci return NULL; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci x = xfrm_state_alloc(net); 13428c2ecf20Sopenharmony_ci if (likely(x)) { 13438c2ecf20Sopenharmony_ci switch (family) { 13448c2ecf20Sopenharmony_ci case AF_INET: 13458c2ecf20Sopenharmony_ci x->sel.daddr.a4 = daddr->a4; 13468c2ecf20Sopenharmony_ci x->sel.saddr.a4 = saddr->a4; 13478c2ecf20Sopenharmony_ci x->sel.prefixlen_d = 32; 13488c2ecf20Sopenharmony_ci x->sel.prefixlen_s = 32; 13498c2ecf20Sopenharmony_ci x->props.saddr.a4 = saddr->a4; 13508c2ecf20Sopenharmony_ci x->id.daddr.a4 = daddr->a4; 13518c2ecf20Sopenharmony_ci break; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci case AF_INET6: 13548c2ecf20Sopenharmony_ci x->sel.daddr.in6 = daddr->in6; 13558c2ecf20Sopenharmony_ci x->sel.saddr.in6 = saddr->in6; 13568c2ecf20Sopenharmony_ci x->sel.prefixlen_d = 128; 13578c2ecf20Sopenharmony_ci x->sel.prefixlen_s = 128; 13588c2ecf20Sopenharmony_ci x->props.saddr.in6 = saddr->in6; 13598c2ecf20Sopenharmony_ci x->id.daddr.in6 = daddr->in6; 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_ACQ; 13648c2ecf20Sopenharmony_ci x->id.proto = proto; 13658c2ecf20Sopenharmony_ci x->props.family = family; 13668c2ecf20Sopenharmony_ci x->props.mode = mode; 13678c2ecf20Sopenharmony_ci x->props.reqid = reqid; 13688c2ecf20Sopenharmony_ci x->if_id = if_id; 13698c2ecf20Sopenharmony_ci x->mark.v = m->v; 13708c2ecf20Sopenharmony_ci x->mark.m = m->m; 13718c2ecf20Sopenharmony_ci x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; 13728c2ecf20Sopenharmony_ci xfrm_state_hold(x); 13738c2ecf20Sopenharmony_ci hrtimer_start(&x->mtimer, 13748c2ecf20Sopenharmony_ci ktime_set(net->xfrm.sysctl_acq_expires, 0), 13758c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 13768c2ecf20Sopenharmony_ci list_add(&x->km.all, &net->xfrm.state_all); 13778c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); 13788c2ecf20Sopenharmony_ci h = xfrm_src_hash(net, daddr, saddr, family); 13798c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci net->xfrm.state_num++; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci xfrm_hash_grow_check(net, x->bydst.next != NULL); 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci return x; 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ciint xfrm_state_add(struct xfrm_state *x) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 13948c2ecf20Sopenharmony_ci struct xfrm_state *x1, *to_put; 13958c2ecf20Sopenharmony_ci int family; 13968c2ecf20Sopenharmony_ci int err; 13978c2ecf20Sopenharmony_ci u32 mark = x->mark.v & x->mark.m; 13988c2ecf20Sopenharmony_ci int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci family = x->props.family; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci to_put = NULL; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci x1 = __xfrm_state_locate(x, use_spi, family); 14078c2ecf20Sopenharmony_ci if (x1) { 14088c2ecf20Sopenharmony_ci to_put = x1; 14098c2ecf20Sopenharmony_ci x1 = NULL; 14108c2ecf20Sopenharmony_ci err = -EEXIST; 14118c2ecf20Sopenharmony_ci goto out; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (use_spi && x->km.seq) { 14158c2ecf20Sopenharmony_ci x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq); 14168c2ecf20Sopenharmony_ci if (x1 && ((x1->id.proto != x->id.proto) || 14178c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) { 14188c2ecf20Sopenharmony_ci to_put = x1; 14198c2ecf20Sopenharmony_ci x1 = NULL; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (use_spi && !x1) 14248c2ecf20Sopenharmony_ci x1 = __find_acq_core(net, &x->mark, family, x->props.mode, 14258c2ecf20Sopenharmony_ci x->props.reqid, x->if_id, x->id.proto, 14268c2ecf20Sopenharmony_ci &x->id.daddr, &x->props.saddr, 0); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci __xfrm_state_bump_genids(x); 14298c2ecf20Sopenharmony_ci __xfrm_state_insert(x); 14308c2ecf20Sopenharmony_ci err = 0; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ciout: 14338c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci if (x1) { 14368c2ecf20Sopenharmony_ci xfrm_state_delete(x1); 14378c2ecf20Sopenharmony_ci xfrm_state_put(x1); 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (to_put) 14418c2ecf20Sopenharmony_ci xfrm_state_put(to_put); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci return err; 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_add); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_MIGRATE 14488c2ecf20Sopenharmony_cistatic inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *security) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci struct xfrm_user_sec_ctx *uctx; 14518c2ecf20Sopenharmony_ci int size = sizeof(*uctx) + security->ctx_len; 14528c2ecf20Sopenharmony_ci int err; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci uctx = kmalloc(size, GFP_KERNEL); 14558c2ecf20Sopenharmony_ci if (!uctx) 14568c2ecf20Sopenharmony_ci return -ENOMEM; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci uctx->exttype = XFRMA_SEC_CTX; 14598c2ecf20Sopenharmony_ci uctx->len = size; 14608c2ecf20Sopenharmony_ci uctx->ctx_doi = security->ctx_doi; 14618c2ecf20Sopenharmony_ci uctx->ctx_alg = security->ctx_alg; 14628c2ecf20Sopenharmony_ci uctx->ctx_len = security->ctx_len; 14638c2ecf20Sopenharmony_ci memcpy(uctx + 1, security->ctx_str, security->ctx_len); 14648c2ecf20Sopenharmony_ci err = security_xfrm_state_alloc(x, uctx); 14658c2ecf20Sopenharmony_ci kfree(uctx); 14668c2ecf20Sopenharmony_ci if (err) 14678c2ecf20Sopenharmony_ci return err; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return 0; 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, 14738c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct net *net = xs_net(orig); 14768c2ecf20Sopenharmony_ci struct xfrm_state *x = xfrm_state_alloc(net); 14778c2ecf20Sopenharmony_ci if (!x) 14788c2ecf20Sopenharmony_ci goto out; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci memcpy(&x->id, &orig->id, sizeof(x->id)); 14818c2ecf20Sopenharmony_ci memcpy(&x->sel, &orig->sel, sizeof(x->sel)); 14828c2ecf20Sopenharmony_ci memcpy(&x->lft, &orig->lft, sizeof(x->lft)); 14838c2ecf20Sopenharmony_ci x->props.mode = orig->props.mode; 14848c2ecf20Sopenharmony_ci x->props.replay_window = orig->props.replay_window; 14858c2ecf20Sopenharmony_ci x->props.reqid = orig->props.reqid; 14868c2ecf20Sopenharmony_ci x->props.family = orig->props.family; 14878c2ecf20Sopenharmony_ci x->props.saddr = orig->props.saddr; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (orig->aalg) { 14908c2ecf20Sopenharmony_ci x->aalg = xfrm_algo_auth_clone(orig->aalg); 14918c2ecf20Sopenharmony_ci if (!x->aalg) 14928c2ecf20Sopenharmony_ci goto error; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci x->props.aalgo = orig->props.aalgo; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if (orig->aead) { 14978c2ecf20Sopenharmony_ci x->aead = xfrm_algo_aead_clone(orig->aead); 14988c2ecf20Sopenharmony_ci x->geniv = orig->geniv; 14998c2ecf20Sopenharmony_ci if (!x->aead) 15008c2ecf20Sopenharmony_ci goto error; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci if (orig->ealg) { 15038c2ecf20Sopenharmony_ci x->ealg = xfrm_algo_clone(orig->ealg); 15048c2ecf20Sopenharmony_ci if (!x->ealg) 15058c2ecf20Sopenharmony_ci goto error; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci x->props.ealgo = orig->props.ealgo; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (orig->calg) { 15108c2ecf20Sopenharmony_ci x->calg = xfrm_algo_clone(orig->calg); 15118c2ecf20Sopenharmony_ci if (!x->calg) 15128c2ecf20Sopenharmony_ci goto error; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci x->props.calgo = orig->props.calgo; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (encap || orig->encap) { 15178c2ecf20Sopenharmony_ci if (encap) 15188c2ecf20Sopenharmony_ci x->encap = kmemdup(encap, sizeof(*x->encap), 15198c2ecf20Sopenharmony_ci GFP_KERNEL); 15208c2ecf20Sopenharmony_ci else 15218c2ecf20Sopenharmony_ci x->encap = kmemdup(orig->encap, sizeof(*x->encap), 15228c2ecf20Sopenharmony_ci GFP_KERNEL); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci if (!x->encap) 15258c2ecf20Sopenharmony_ci goto error; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (orig->security) 15298c2ecf20Sopenharmony_ci if (clone_security(x, orig->security)) 15308c2ecf20Sopenharmony_ci goto error; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci if (orig->coaddr) { 15338c2ecf20Sopenharmony_ci x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr), 15348c2ecf20Sopenharmony_ci GFP_KERNEL); 15358c2ecf20Sopenharmony_ci if (!x->coaddr) 15368c2ecf20Sopenharmony_ci goto error; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (orig->replay_esn) { 15408c2ecf20Sopenharmony_ci if (xfrm_replay_clone(x, orig)) 15418c2ecf20Sopenharmony_ci goto error; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci memcpy(&x->mark, &orig->mark, sizeof(x->mark)); 15458c2ecf20Sopenharmony_ci memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark)); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci x->props.flags = orig->props.flags; 15488c2ecf20Sopenharmony_ci x->props.extra_flags = orig->props.extra_flags; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci x->if_id = orig->if_id; 15518c2ecf20Sopenharmony_ci x->tfcpad = orig->tfcpad; 15528c2ecf20Sopenharmony_ci x->replay_maxdiff = orig->replay_maxdiff; 15538c2ecf20Sopenharmony_ci x->replay_maxage = orig->replay_maxage; 15548c2ecf20Sopenharmony_ci memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft)); 15558c2ecf20Sopenharmony_ci x->km.state = orig->km.state; 15568c2ecf20Sopenharmony_ci x->km.seq = orig->km.seq; 15578c2ecf20Sopenharmony_ci x->replay = orig->replay; 15588c2ecf20Sopenharmony_ci x->preplay = orig->preplay; 15598c2ecf20Sopenharmony_ci x->mapping_maxage = orig->mapping_maxage; 15608c2ecf20Sopenharmony_ci x->lastused = orig->lastused; 15618c2ecf20Sopenharmony_ci x->new_mapping = 0; 15628c2ecf20Sopenharmony_ci x->new_mapping_sport = 0; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci return x; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci error: 15678c2ecf20Sopenharmony_ci xfrm_state_put(x); 15688c2ecf20Sopenharmony_ciout: 15698c2ecf20Sopenharmony_ci return NULL; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistruct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net, 15738c2ecf20Sopenharmony_ci u32 if_id) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci unsigned int h; 15768c2ecf20Sopenharmony_ci struct xfrm_state *x = NULL; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (m->reqid) { 15818c2ecf20Sopenharmony_ci h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr, 15828c2ecf20Sopenharmony_ci m->reqid, m->old_family); 15838c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { 15848c2ecf20Sopenharmony_ci if (x->props.mode != m->mode || 15858c2ecf20Sopenharmony_ci x->id.proto != m->proto) 15868c2ecf20Sopenharmony_ci continue; 15878c2ecf20Sopenharmony_ci if (m->reqid && x->props.reqid != m->reqid) 15888c2ecf20Sopenharmony_ci continue; 15898c2ecf20Sopenharmony_ci if (if_id != 0 && x->if_id != if_id) 15908c2ecf20Sopenharmony_ci continue; 15918c2ecf20Sopenharmony_ci if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, 15928c2ecf20Sopenharmony_ci m->old_family) || 15938c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->props.saddr, &m->old_saddr, 15948c2ecf20Sopenharmony_ci m->old_family)) 15958c2ecf20Sopenharmony_ci continue; 15968c2ecf20Sopenharmony_ci xfrm_state_hold(x); 15978c2ecf20Sopenharmony_ci break; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci } else { 16008c2ecf20Sopenharmony_ci h = xfrm_src_hash(net, &m->old_daddr, &m->old_saddr, 16018c2ecf20Sopenharmony_ci m->old_family); 16028c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bysrc+h, bysrc) { 16038c2ecf20Sopenharmony_ci if (x->props.mode != m->mode || 16048c2ecf20Sopenharmony_ci x->id.proto != m->proto) 16058c2ecf20Sopenharmony_ci continue; 16068c2ecf20Sopenharmony_ci if (if_id != 0 && x->if_id != if_id) 16078c2ecf20Sopenharmony_ci continue; 16088c2ecf20Sopenharmony_ci if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, 16098c2ecf20Sopenharmony_ci m->old_family) || 16108c2ecf20Sopenharmony_ci !xfrm_addr_equal(&x->props.saddr, &m->old_saddr, 16118c2ecf20Sopenharmony_ci m->old_family)) 16128c2ecf20Sopenharmony_ci continue; 16138c2ecf20Sopenharmony_ci xfrm_state_hold(x); 16148c2ecf20Sopenharmony_ci break; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci return x; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_migrate_state_find); 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistruct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, 16258c2ecf20Sopenharmony_ci struct xfrm_migrate *m, 16268c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci struct xfrm_state *xc; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci xc = xfrm_state_clone(x, encap); 16318c2ecf20Sopenharmony_ci if (!xc) 16328c2ecf20Sopenharmony_ci return NULL; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci xc->props.family = m->new_family; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci if (xfrm_init_state(xc) < 0) 16378c2ecf20Sopenharmony_ci goto error; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr)); 16408c2ecf20Sopenharmony_ci memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr)); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* add state */ 16438c2ecf20Sopenharmony_ci if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) { 16448c2ecf20Sopenharmony_ci /* a care is needed when the destination address of the 16458c2ecf20Sopenharmony_ci state is to be updated as it is a part of triplet */ 16468c2ecf20Sopenharmony_ci xfrm_state_insert(xc); 16478c2ecf20Sopenharmony_ci } else { 16488c2ecf20Sopenharmony_ci if (xfrm_state_add(xc) < 0) 16498c2ecf20Sopenharmony_ci goto error; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci return xc; 16538c2ecf20Sopenharmony_cierror: 16548c2ecf20Sopenharmony_ci xfrm_state_put(xc); 16558c2ecf20Sopenharmony_ci return NULL; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_migrate); 16588c2ecf20Sopenharmony_ci#endif 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ciint xfrm_state_update(struct xfrm_state *x) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci struct xfrm_state *x1, *to_put; 16638c2ecf20Sopenharmony_ci int err; 16648c2ecf20Sopenharmony_ci int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); 16658c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci to_put = NULL; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 16708c2ecf20Sopenharmony_ci x1 = __xfrm_state_locate(x, use_spi, x->props.family); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci err = -ESRCH; 16738c2ecf20Sopenharmony_ci if (!x1) 16748c2ecf20Sopenharmony_ci goto out; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci if (xfrm_state_kern(x1)) { 16778c2ecf20Sopenharmony_ci to_put = x1; 16788c2ecf20Sopenharmony_ci err = -EEXIST; 16798c2ecf20Sopenharmony_ci goto out; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (x1->km.state == XFRM_STATE_ACQ) { 16838c2ecf20Sopenharmony_ci __xfrm_state_insert(x); 16848c2ecf20Sopenharmony_ci x = NULL; 16858c2ecf20Sopenharmony_ci } 16868c2ecf20Sopenharmony_ci err = 0; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ciout: 16898c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci if (to_put) 16928c2ecf20Sopenharmony_ci xfrm_state_put(to_put); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (err) 16958c2ecf20Sopenharmony_ci return err; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (!x) { 16988c2ecf20Sopenharmony_ci xfrm_state_delete(x1); 16998c2ecf20Sopenharmony_ci xfrm_state_put(x1); 17008c2ecf20Sopenharmony_ci return 0; 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci err = -EINVAL; 17048c2ecf20Sopenharmony_ci spin_lock_bh(&x1->lock); 17058c2ecf20Sopenharmony_ci if (likely(x1->km.state == XFRM_STATE_VALID)) { 17068c2ecf20Sopenharmony_ci if (x->encap && x1->encap && 17078c2ecf20Sopenharmony_ci x->encap->encap_type == x1->encap->encap_type) 17088c2ecf20Sopenharmony_ci memcpy(x1->encap, x->encap, sizeof(*x1->encap)); 17098c2ecf20Sopenharmony_ci else if (x->encap || x1->encap) 17108c2ecf20Sopenharmony_ci goto fail; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci if (x->coaddr && x1->coaddr) { 17138c2ecf20Sopenharmony_ci memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr)); 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel))) 17168c2ecf20Sopenharmony_ci memcpy(&x1->sel, &x->sel, sizeof(x1->sel)); 17178c2ecf20Sopenharmony_ci memcpy(&x1->lft, &x->lft, sizeof(x1->lft)); 17188c2ecf20Sopenharmony_ci x1->km.dying = 0; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci hrtimer_start(&x1->mtimer, ktime_set(1, 0), 17218c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 17228c2ecf20Sopenharmony_ci if (x1->curlft.use_time) 17238c2ecf20Sopenharmony_ci xfrm_state_check_expire(x1); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (x->props.smark.m || x->props.smark.v || x->if_id) { 17268c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci if (x->props.smark.m || x->props.smark.v) 17298c2ecf20Sopenharmony_ci x1->props.smark = x->props.smark; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (x->if_id) 17328c2ecf20Sopenharmony_ci x1->if_id = x->if_id; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci __xfrm_state_bump_genids(x1); 17358c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci err = 0; 17398c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_DEAD; 17408c2ecf20Sopenharmony_ci __xfrm_state_put(x); 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cifail: 17448c2ecf20Sopenharmony_ci spin_unlock_bh(&x1->lock); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci xfrm_state_put(x1); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci return err; 17498c2ecf20Sopenharmony_ci} 17508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_update); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ciint xfrm_state_check_expire(struct xfrm_state *x) 17538c2ecf20Sopenharmony_ci{ 17548c2ecf20Sopenharmony_ci if (!x->curlft.use_time) 17558c2ecf20Sopenharmony_ci x->curlft.use_time = ktime_get_real_seconds(); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (x->curlft.bytes >= x->lft.hard_byte_limit || 17588c2ecf20Sopenharmony_ci x->curlft.packets >= x->lft.hard_packet_limit) { 17598c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_EXPIRED; 17608c2ecf20Sopenharmony_ci hrtimer_start(&x->mtimer, 0, HRTIMER_MODE_REL_SOFT); 17618c2ecf20Sopenharmony_ci return -EINVAL; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if (!x->km.dying && 17658c2ecf20Sopenharmony_ci (x->curlft.bytes >= x->lft.soft_byte_limit || 17668c2ecf20Sopenharmony_ci x->curlft.packets >= x->lft.soft_packet_limit)) { 17678c2ecf20Sopenharmony_ci x->km.dying = 1; 17688c2ecf20Sopenharmony_ci km_state_expired(x, 0, 0); 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci return 0; 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_check_expire); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistruct xfrm_state * 17758c2ecf20Sopenharmony_cixfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi, 17768c2ecf20Sopenharmony_ci u8 proto, unsigned short family) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci struct xfrm_state *x; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci rcu_read_lock(); 17818c2ecf20Sopenharmony_ci x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family); 17828c2ecf20Sopenharmony_ci rcu_read_unlock(); 17838c2ecf20Sopenharmony_ci return x; 17848c2ecf20Sopenharmony_ci} 17858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_lookup); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistruct xfrm_state * 17888c2ecf20Sopenharmony_cixfrm_state_lookup_byaddr(struct net *net, u32 mark, 17898c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, const xfrm_address_t *saddr, 17908c2ecf20Sopenharmony_ci u8 proto, unsigned short family) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct xfrm_state *x; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 17958c2ecf20Sopenharmony_ci x = __xfrm_state_lookup_byaddr(net, mark, daddr, saddr, proto, family); 17968c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 17978c2ecf20Sopenharmony_ci return x; 17988c2ecf20Sopenharmony_ci} 17998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_lookup_byaddr); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_cistruct xfrm_state * 18028c2ecf20Sopenharmony_cixfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, 18038c2ecf20Sopenharmony_ci u32 if_id, u8 proto, const xfrm_address_t *daddr, 18048c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, int create, unsigned short family) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci struct xfrm_state *x; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 18098c2ecf20Sopenharmony_ci x = __find_acq_core(net, mark, family, mode, reqid, if_id, proto, daddr, saddr, create); 18108c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return x; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_find_acq); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 18178c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 18188c2ecf20Sopenharmony_ci/* distribution counting sort function for xfrm_state and xfrm_tmpl */ 18198c2ecf20Sopenharmony_cistatic void 18208c2ecf20Sopenharmony_ci__xfrm6_sort(void **dst, void **src, int n, 18218c2ecf20Sopenharmony_ci int (*cmp)(const void *p), int maxclass) 18228c2ecf20Sopenharmony_ci{ 18238c2ecf20Sopenharmony_ci int count[XFRM_MAX_DEPTH] = { }; 18248c2ecf20Sopenharmony_ci int class[XFRM_MAX_DEPTH]; 18258c2ecf20Sopenharmony_ci int i; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 18288c2ecf20Sopenharmony_ci int c = cmp(src[i]); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci class[i] = c; 18318c2ecf20Sopenharmony_ci count[c]++; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci for (i = 2; i < maxclass; i++) 18358c2ecf20Sopenharmony_ci count[i] += count[i - 1]; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 18388c2ecf20Sopenharmony_ci dst[count[class[i] - 1]++] = src[i]; 18398c2ecf20Sopenharmony_ci src[i] = NULL; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/* Rule for xfrm_state: 18448c2ecf20Sopenharmony_ci * 18458c2ecf20Sopenharmony_ci * rule 1: select IPsec transport except AH 18468c2ecf20Sopenharmony_ci * rule 2: select MIPv6 RO or inbound trigger 18478c2ecf20Sopenharmony_ci * rule 3: select IPsec transport AH 18488c2ecf20Sopenharmony_ci * rule 4: select IPsec tunnel 18498c2ecf20Sopenharmony_ci * rule 5: others 18508c2ecf20Sopenharmony_ci */ 18518c2ecf20Sopenharmony_cistatic int __xfrm6_state_sort_cmp(const void *p) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci const struct xfrm_state *v = p; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci switch (v->props.mode) { 18568c2ecf20Sopenharmony_ci case XFRM_MODE_TRANSPORT: 18578c2ecf20Sopenharmony_ci if (v->id.proto != IPPROTO_AH) 18588c2ecf20Sopenharmony_ci return 1; 18598c2ecf20Sopenharmony_ci else 18608c2ecf20Sopenharmony_ci return 3; 18618c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 18628c2ecf20Sopenharmony_ci case XFRM_MODE_ROUTEOPTIMIZATION: 18638c2ecf20Sopenharmony_ci case XFRM_MODE_IN_TRIGGER: 18648c2ecf20Sopenharmony_ci return 2; 18658c2ecf20Sopenharmony_ci#endif 18668c2ecf20Sopenharmony_ci case XFRM_MODE_TUNNEL: 18678c2ecf20Sopenharmony_ci case XFRM_MODE_BEET: 18688c2ecf20Sopenharmony_ci return 4; 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci return 5; 18718c2ecf20Sopenharmony_ci} 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci/* Rule for xfrm_tmpl: 18748c2ecf20Sopenharmony_ci * 18758c2ecf20Sopenharmony_ci * rule 1: select IPsec transport 18768c2ecf20Sopenharmony_ci * rule 2: select MIPv6 RO or inbound trigger 18778c2ecf20Sopenharmony_ci * rule 3: select IPsec tunnel 18788c2ecf20Sopenharmony_ci * rule 4: others 18798c2ecf20Sopenharmony_ci */ 18808c2ecf20Sopenharmony_cistatic int __xfrm6_tmpl_sort_cmp(const void *p) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci const struct xfrm_tmpl *v = p; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci switch (v->mode) { 18858c2ecf20Sopenharmony_ci case XFRM_MODE_TRANSPORT: 18868c2ecf20Sopenharmony_ci return 1; 18878c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 18888c2ecf20Sopenharmony_ci case XFRM_MODE_ROUTEOPTIMIZATION: 18898c2ecf20Sopenharmony_ci case XFRM_MODE_IN_TRIGGER: 18908c2ecf20Sopenharmony_ci return 2; 18918c2ecf20Sopenharmony_ci#endif 18928c2ecf20Sopenharmony_ci case XFRM_MODE_TUNNEL: 18938c2ecf20Sopenharmony_ci case XFRM_MODE_BEET: 18948c2ecf20Sopenharmony_ci return 3; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci return 4; 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci#else 18998c2ecf20Sopenharmony_cistatic inline int __xfrm6_state_sort_cmp(const void *p) { return 5; } 19008c2ecf20Sopenharmony_cistatic inline int __xfrm6_tmpl_sort_cmp(const void *p) { return 4; } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_cistatic inline void 19038c2ecf20Sopenharmony_ci__xfrm6_sort(void **dst, void **src, int n, 19048c2ecf20Sopenharmony_ci int (*cmp)(const void *p), int maxclass) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci int i; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 19098c2ecf20Sopenharmony_ci dst[i] = src[i]; 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci#endif /* CONFIG_IPV6 */ 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_civoid 19148c2ecf20Sopenharmony_cixfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, 19158c2ecf20Sopenharmony_ci unsigned short family) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci int i; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (family == AF_INET6) 19208c2ecf20Sopenharmony_ci __xfrm6_sort((void **)dst, (void **)src, n, 19218c2ecf20Sopenharmony_ci __xfrm6_tmpl_sort_cmp, 5); 19228c2ecf20Sopenharmony_ci else 19238c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 19248c2ecf20Sopenharmony_ci dst[i] = src[i]; 19258c2ecf20Sopenharmony_ci} 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_civoid 19288c2ecf20Sopenharmony_cixfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, 19298c2ecf20Sopenharmony_ci unsigned short family) 19308c2ecf20Sopenharmony_ci{ 19318c2ecf20Sopenharmony_ci int i; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (family == AF_INET6) 19348c2ecf20Sopenharmony_ci __xfrm6_sort((void **)dst, (void **)src, n, 19358c2ecf20Sopenharmony_ci __xfrm6_state_sort_cmp, 6); 19368c2ecf20Sopenharmony_ci else 19378c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 19388c2ecf20Sopenharmony_ci dst[i] = src[i]; 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci#endif 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci/* Silly enough, but I'm lazy to build resolution list */ 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq) 19458c2ecf20Sopenharmony_ci{ 19468c2ecf20Sopenharmony_ci int i; 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci for (i = 0; i <= net->xfrm.state_hmask; i++) { 19498c2ecf20Sopenharmony_ci struct xfrm_state *x; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { 19528c2ecf20Sopenharmony_ci if (x->km.seq == seq && 19538c2ecf20Sopenharmony_ci (mark & x->mark.m) == x->mark.v && 19548c2ecf20Sopenharmony_ci x->km.state == XFRM_STATE_ACQ) { 19558c2ecf20Sopenharmony_ci xfrm_state_hold(x); 19568c2ecf20Sopenharmony_ci return x; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci return NULL; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_cistruct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci struct xfrm_state *x; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 19688c2ecf20Sopenharmony_ci x = __xfrm_find_acq_byseq(net, mark, seq); 19698c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 19708c2ecf20Sopenharmony_ci return x; 19718c2ecf20Sopenharmony_ci} 19728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_find_acq_byseq); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ciu32 xfrm_get_acqseq(void) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci u32 res; 19778c2ecf20Sopenharmony_ci static atomic_t acqseq; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci do { 19808c2ecf20Sopenharmony_ci res = atomic_inc_return(&acqseq); 19818c2ecf20Sopenharmony_ci } while (!res); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci return res; 19848c2ecf20Sopenharmony_ci} 19858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_get_acqseq); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ciint verify_spi_info(u8 proto, u32 min, u32 max) 19888c2ecf20Sopenharmony_ci{ 19898c2ecf20Sopenharmony_ci switch (proto) { 19908c2ecf20Sopenharmony_ci case IPPROTO_AH: 19918c2ecf20Sopenharmony_ci case IPPROTO_ESP: 19928c2ecf20Sopenharmony_ci break; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci case IPPROTO_COMP: 19958c2ecf20Sopenharmony_ci /* IPCOMP spi is 16-bits. */ 19968c2ecf20Sopenharmony_ci if (max >= 0x10000) 19978c2ecf20Sopenharmony_ci return -EINVAL; 19988c2ecf20Sopenharmony_ci break; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci default: 20018c2ecf20Sopenharmony_ci return -EINVAL; 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci if (min > max) 20058c2ecf20Sopenharmony_ci return -EINVAL; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci return 0; 20088c2ecf20Sopenharmony_ci} 20098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(verify_spi_info); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ciint xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 20148c2ecf20Sopenharmony_ci unsigned int h; 20158c2ecf20Sopenharmony_ci struct xfrm_state *x0; 20168c2ecf20Sopenharmony_ci int err = -ENOENT; 20178c2ecf20Sopenharmony_ci __be32 minspi = htonl(low); 20188c2ecf20Sopenharmony_ci __be32 maxspi = htonl(high); 20198c2ecf20Sopenharmony_ci __be32 newspi = 0; 20208c2ecf20Sopenharmony_ci u32 mark = x->mark.v & x->mark.m; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 20238c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_DEAD) 20248c2ecf20Sopenharmony_ci goto unlock; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci err = 0; 20278c2ecf20Sopenharmony_ci if (x->id.spi) 20288c2ecf20Sopenharmony_ci goto unlock; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci err = -ENOENT; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (minspi == maxspi) { 20338c2ecf20Sopenharmony_ci x0 = xfrm_state_lookup(net, mark, &x->id.daddr, minspi, x->id.proto, x->props.family); 20348c2ecf20Sopenharmony_ci if (x0) { 20358c2ecf20Sopenharmony_ci xfrm_state_put(x0); 20368c2ecf20Sopenharmony_ci goto unlock; 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci newspi = minspi; 20398c2ecf20Sopenharmony_ci } else { 20408c2ecf20Sopenharmony_ci u32 spi = 0; 20418c2ecf20Sopenharmony_ci for (h = 0; h < high-low+1; h++) { 20428c2ecf20Sopenharmony_ci spi = low + prandom_u32()%(high-low+1); 20438c2ecf20Sopenharmony_ci x0 = xfrm_state_lookup(net, mark, &x->id.daddr, htonl(spi), x->id.proto, x->props.family); 20448c2ecf20Sopenharmony_ci if (x0 == NULL) { 20458c2ecf20Sopenharmony_ci newspi = htonl(spi); 20468c2ecf20Sopenharmony_ci break; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci xfrm_state_put(x0); 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci if (newspi) { 20528c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 20538c2ecf20Sopenharmony_ci x->id.spi = newspi; 20548c2ecf20Sopenharmony_ci h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family); 20558c2ecf20Sopenharmony_ci hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); 20568c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci err = 0; 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ciunlock: 20628c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return err; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_alloc_spi); 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic bool __xfrm_state_filter_match(struct xfrm_state *x, 20698c2ecf20Sopenharmony_ci struct xfrm_address_filter *filter) 20708c2ecf20Sopenharmony_ci{ 20718c2ecf20Sopenharmony_ci if (filter) { 20728c2ecf20Sopenharmony_ci if ((filter->family == AF_INET || 20738c2ecf20Sopenharmony_ci filter->family == AF_INET6) && 20748c2ecf20Sopenharmony_ci x->props.family != filter->family) 20758c2ecf20Sopenharmony_ci return false; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci return addr_match(&x->props.saddr, &filter->saddr, 20788c2ecf20Sopenharmony_ci filter->splen) && 20798c2ecf20Sopenharmony_ci addr_match(&x->id.daddr, &filter->daddr, 20808c2ecf20Sopenharmony_ci filter->dplen); 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ci return true; 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ciint xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, 20868c2ecf20Sopenharmony_ci int (*func)(struct xfrm_state *, int, void*), 20878c2ecf20Sopenharmony_ci void *data) 20888c2ecf20Sopenharmony_ci{ 20898c2ecf20Sopenharmony_ci struct xfrm_state *state; 20908c2ecf20Sopenharmony_ci struct xfrm_state_walk *x; 20918c2ecf20Sopenharmony_ci int err = 0; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (walk->seq != 0 && list_empty(&walk->all)) 20948c2ecf20Sopenharmony_ci return 0; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 20978c2ecf20Sopenharmony_ci if (list_empty(&walk->all)) 20988c2ecf20Sopenharmony_ci x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all); 20998c2ecf20Sopenharmony_ci else 21008c2ecf20Sopenharmony_ci x = list_first_entry(&walk->all, struct xfrm_state_walk, all); 21018c2ecf20Sopenharmony_ci list_for_each_entry_from(x, &net->xfrm.state_all, all) { 21028c2ecf20Sopenharmony_ci if (x->state == XFRM_STATE_DEAD) 21038c2ecf20Sopenharmony_ci continue; 21048c2ecf20Sopenharmony_ci state = container_of(x, struct xfrm_state, km); 21058c2ecf20Sopenharmony_ci if (!xfrm_id_proto_match(state->id.proto, walk->proto)) 21068c2ecf20Sopenharmony_ci continue; 21078c2ecf20Sopenharmony_ci if (!__xfrm_state_filter_match(state, walk->filter)) 21088c2ecf20Sopenharmony_ci continue; 21098c2ecf20Sopenharmony_ci err = func(state, walk->seq, data); 21108c2ecf20Sopenharmony_ci if (err) { 21118c2ecf20Sopenharmony_ci list_move_tail(&walk->all, &x->all); 21128c2ecf20Sopenharmony_ci goto out; 21138c2ecf20Sopenharmony_ci } 21148c2ecf20Sopenharmony_ci walk->seq++; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci if (walk->seq == 0) { 21178c2ecf20Sopenharmony_ci err = -ENOENT; 21188c2ecf20Sopenharmony_ci goto out; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci list_del_init(&walk->all); 21218c2ecf20Sopenharmony_ciout: 21228c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 21238c2ecf20Sopenharmony_ci return err; 21248c2ecf20Sopenharmony_ci} 21258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_walk); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_civoid xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, 21288c2ecf20Sopenharmony_ci struct xfrm_address_filter *filter) 21298c2ecf20Sopenharmony_ci{ 21308c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&walk->all); 21318c2ecf20Sopenharmony_ci walk->proto = proto; 21328c2ecf20Sopenharmony_ci walk->state = XFRM_STATE_DEAD; 21338c2ecf20Sopenharmony_ci walk->seq = 0; 21348c2ecf20Sopenharmony_ci walk->filter = filter; 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_walk_init); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_civoid xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net) 21398c2ecf20Sopenharmony_ci{ 21408c2ecf20Sopenharmony_ci kfree(walk->filter); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci if (list_empty(&walk->all)) 21438c2ecf20Sopenharmony_ci return; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_state_lock); 21468c2ecf20Sopenharmony_ci list_del(&walk->all); 21478c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_state_lock); 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_walk_done); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_cistatic void xfrm_replay_timer_handler(struct timer_list *t) 21528c2ecf20Sopenharmony_ci{ 21538c2ecf20Sopenharmony_ci struct xfrm_state *x = from_timer(x, t, rtimer); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci spin_lock(&x->lock); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (x->km.state == XFRM_STATE_VALID) { 21588c2ecf20Sopenharmony_ci if (xfrm_aevent_is_on(xs_net(x))) 21598c2ecf20Sopenharmony_ci x->repl->notify(x, XFRM_REPLAY_TIMEOUT); 21608c2ecf20Sopenharmony_ci else 21618c2ecf20Sopenharmony_ci x->xflags |= XFRM_TIME_DEFER; 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci spin_unlock(&x->lock); 21658c2ecf20Sopenharmony_ci} 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_cistatic LIST_HEAD(xfrm_km_list); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_civoid km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci rcu_read_lock(); 21748c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) 21758c2ecf20Sopenharmony_ci if (km->notify_policy) 21768c2ecf20Sopenharmony_ci km->notify_policy(xp, dir, c); 21778c2ecf20Sopenharmony_ci rcu_read_unlock(); 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_civoid km_state_notify(struct xfrm_state *x, const struct km_event *c) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 21838c2ecf20Sopenharmony_ci rcu_read_lock(); 21848c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) 21858c2ecf20Sopenharmony_ci if (km->notify) 21868c2ecf20Sopenharmony_ci km->notify(x, c); 21878c2ecf20Sopenharmony_ci rcu_read_unlock(); 21888c2ecf20Sopenharmony_ci} 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_policy_notify); 21918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_state_notify); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_civoid km_state_expired(struct xfrm_state *x, int hard, u32 portid) 21948c2ecf20Sopenharmony_ci{ 21958c2ecf20Sopenharmony_ci struct km_event c; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci c.data.hard = hard; 21988c2ecf20Sopenharmony_ci c.portid = portid; 21998c2ecf20Sopenharmony_ci c.event = XFRM_MSG_EXPIRE; 22008c2ecf20Sopenharmony_ci km_state_notify(x, &c); 22018c2ecf20Sopenharmony_ci} 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_state_expired); 22048c2ecf20Sopenharmony_ci/* 22058c2ecf20Sopenharmony_ci * We send to all registered managers regardless of failure 22068c2ecf20Sopenharmony_ci * We are happy with one success 22078c2ecf20Sopenharmony_ci*/ 22088c2ecf20Sopenharmony_ciint km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) 22098c2ecf20Sopenharmony_ci{ 22108c2ecf20Sopenharmony_ci int err = -EINVAL, acqret; 22118c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci rcu_read_lock(); 22148c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 22158c2ecf20Sopenharmony_ci acqret = km->acquire(x, t, pol); 22168c2ecf20Sopenharmony_ci if (!acqret) 22178c2ecf20Sopenharmony_ci err = acqret; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci rcu_read_unlock(); 22208c2ecf20Sopenharmony_ci return err; 22218c2ecf20Sopenharmony_ci} 22228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_query); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_cistatic int __km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) 22258c2ecf20Sopenharmony_ci{ 22268c2ecf20Sopenharmony_ci int err = -EINVAL; 22278c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci rcu_read_lock(); 22308c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 22318c2ecf20Sopenharmony_ci if (km->new_mapping) 22328c2ecf20Sopenharmony_ci err = km->new_mapping(x, ipaddr, sport); 22338c2ecf20Sopenharmony_ci if (!err) 22348c2ecf20Sopenharmony_ci break; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci rcu_read_unlock(); 22378c2ecf20Sopenharmony_ci return err; 22388c2ecf20Sopenharmony_ci} 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ciint km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci int ret = 0; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (x->mapping_maxage) { 22458c2ecf20Sopenharmony_ci if ((jiffies / HZ - x->new_mapping) > x->mapping_maxage || 22468c2ecf20Sopenharmony_ci x->new_mapping_sport != sport) { 22478c2ecf20Sopenharmony_ci x->new_mapping_sport = sport; 22488c2ecf20Sopenharmony_ci x->new_mapping = jiffies / HZ; 22498c2ecf20Sopenharmony_ci ret = __km_new_mapping(x, ipaddr, sport); 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci } else { 22528c2ecf20Sopenharmony_ci ret = __km_new_mapping(x, ipaddr, sport); 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci return ret; 22568c2ecf20Sopenharmony_ci} 22578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_new_mapping); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_civoid km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid) 22608c2ecf20Sopenharmony_ci{ 22618c2ecf20Sopenharmony_ci struct km_event c; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci c.data.hard = hard; 22648c2ecf20Sopenharmony_ci c.portid = portid; 22658c2ecf20Sopenharmony_ci c.event = XFRM_MSG_POLEXPIRE; 22668c2ecf20Sopenharmony_ci km_policy_notify(pol, dir, &c); 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_policy_expired); 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_MIGRATE 22718c2ecf20Sopenharmony_ciint km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 22728c2ecf20Sopenharmony_ci const struct xfrm_migrate *m, int num_migrate, 22738c2ecf20Sopenharmony_ci const struct xfrm_kmaddress *k, 22748c2ecf20Sopenharmony_ci const struct xfrm_encap_tmpl *encap) 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci int err = -EINVAL; 22778c2ecf20Sopenharmony_ci int ret; 22788c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci rcu_read_lock(); 22818c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 22828c2ecf20Sopenharmony_ci if (km->migrate) { 22838c2ecf20Sopenharmony_ci ret = km->migrate(sel, dir, type, m, num_migrate, k, 22848c2ecf20Sopenharmony_ci encap); 22858c2ecf20Sopenharmony_ci if (!ret) 22868c2ecf20Sopenharmony_ci err = ret; 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci rcu_read_unlock(); 22908c2ecf20Sopenharmony_ci return err; 22918c2ecf20Sopenharmony_ci} 22928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_migrate); 22938c2ecf20Sopenharmony_ci#endif 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ciint km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) 22968c2ecf20Sopenharmony_ci{ 22978c2ecf20Sopenharmony_ci int err = -EINVAL; 22988c2ecf20Sopenharmony_ci int ret; 22998c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci rcu_read_lock(); 23028c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 23038c2ecf20Sopenharmony_ci if (km->report) { 23048c2ecf20Sopenharmony_ci ret = km->report(net, proto, sel, addr); 23058c2ecf20Sopenharmony_ci if (!ret) 23068c2ecf20Sopenharmony_ci err = ret; 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci rcu_read_unlock(); 23108c2ecf20Sopenharmony_ci return err; 23118c2ecf20Sopenharmony_ci} 23128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(km_report); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_cistatic bool km_is_alive(const struct km_event *c) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 23178c2ecf20Sopenharmony_ci bool is_alive = false; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci rcu_read_lock(); 23208c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 23218c2ecf20Sopenharmony_ci if (km->is_alive && km->is_alive(c)) { 23228c2ecf20Sopenharmony_ci is_alive = true; 23238c2ecf20Sopenharmony_ci break; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci rcu_read_unlock(); 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci return is_alive; 23298c2ecf20Sopenharmony_ci} 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT) 23328c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_translator_lock); 23338c2ecf20Sopenharmony_cistatic struct xfrm_translator __rcu *xfrm_translator; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_cistruct xfrm_translator *xfrm_get_translator(void) 23368c2ecf20Sopenharmony_ci{ 23378c2ecf20Sopenharmony_ci struct xfrm_translator *xtr; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci rcu_read_lock(); 23408c2ecf20Sopenharmony_ci xtr = rcu_dereference(xfrm_translator); 23418c2ecf20Sopenharmony_ci if (unlikely(!xtr)) 23428c2ecf20Sopenharmony_ci goto out; 23438c2ecf20Sopenharmony_ci if (!try_module_get(xtr->owner)) 23448c2ecf20Sopenharmony_ci xtr = NULL; 23458c2ecf20Sopenharmony_ciout: 23468c2ecf20Sopenharmony_ci rcu_read_unlock(); 23478c2ecf20Sopenharmony_ci return xtr; 23488c2ecf20Sopenharmony_ci} 23498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_get_translator); 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_civoid xfrm_put_translator(struct xfrm_translator *xtr) 23528c2ecf20Sopenharmony_ci{ 23538c2ecf20Sopenharmony_ci module_put(xtr->owner); 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_put_translator); 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ciint xfrm_register_translator(struct xfrm_translator *xtr) 23588c2ecf20Sopenharmony_ci{ 23598c2ecf20Sopenharmony_ci int err = 0; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_translator_lock); 23628c2ecf20Sopenharmony_ci if (unlikely(xfrm_translator != NULL)) 23638c2ecf20Sopenharmony_ci err = -EEXIST; 23648c2ecf20Sopenharmony_ci else 23658c2ecf20Sopenharmony_ci rcu_assign_pointer(xfrm_translator, xtr); 23668c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_translator_lock); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci return err; 23698c2ecf20Sopenharmony_ci} 23708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_register_translator); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ciint xfrm_unregister_translator(struct xfrm_translator *xtr) 23738c2ecf20Sopenharmony_ci{ 23748c2ecf20Sopenharmony_ci int err = 0; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_translator_lock); 23778c2ecf20Sopenharmony_ci if (likely(xfrm_translator != NULL)) { 23788c2ecf20Sopenharmony_ci if (rcu_access_pointer(xfrm_translator) != xtr) 23798c2ecf20Sopenharmony_ci err = -EINVAL; 23808c2ecf20Sopenharmony_ci else 23818c2ecf20Sopenharmony_ci RCU_INIT_POINTER(xfrm_translator, NULL); 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_translator_lock); 23848c2ecf20Sopenharmony_ci synchronize_rcu(); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci return err; 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_unregister_translator); 23898c2ecf20Sopenharmony_ci#endif 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ciint xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) 23928c2ecf20Sopenharmony_ci{ 23938c2ecf20Sopenharmony_ci int err; 23948c2ecf20Sopenharmony_ci u8 *data; 23958c2ecf20Sopenharmony_ci struct xfrm_mgr *km; 23968c2ecf20Sopenharmony_ci struct xfrm_policy *pol = NULL; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci if (sockptr_is_null(optval) && !optlen) { 23998c2ecf20Sopenharmony_ci xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL); 24008c2ecf20Sopenharmony_ci xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL); 24018c2ecf20Sopenharmony_ci __sk_dst_reset(sk); 24028c2ecf20Sopenharmony_ci return 0; 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (optlen <= 0 || optlen > PAGE_SIZE) 24068c2ecf20Sopenharmony_ci return -EMSGSIZE; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci data = memdup_sockptr(optval, optlen); 24098c2ecf20Sopenharmony_ci if (IS_ERR(data)) 24108c2ecf20Sopenharmony_ci return PTR_ERR(data); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci if (in_compat_syscall()) { 24138c2ecf20Sopenharmony_ci struct xfrm_translator *xtr = xfrm_get_translator(); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci if (!xtr) { 24168c2ecf20Sopenharmony_ci kfree(data); 24178c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci err = xtr->xlate_user_policy_sockptr(&data, optlen); 24218c2ecf20Sopenharmony_ci xfrm_put_translator(xtr); 24228c2ecf20Sopenharmony_ci if (err) { 24238c2ecf20Sopenharmony_ci kfree(data); 24248c2ecf20Sopenharmony_ci return err; 24258c2ecf20Sopenharmony_ci } 24268c2ecf20Sopenharmony_ci } 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci err = -EINVAL; 24298c2ecf20Sopenharmony_ci rcu_read_lock(); 24308c2ecf20Sopenharmony_ci list_for_each_entry_rcu(km, &xfrm_km_list, list) { 24318c2ecf20Sopenharmony_ci pol = km->compile_policy(sk, optname, data, 24328c2ecf20Sopenharmony_ci optlen, &err); 24338c2ecf20Sopenharmony_ci if (err >= 0) 24348c2ecf20Sopenharmony_ci break; 24358c2ecf20Sopenharmony_ci } 24368c2ecf20Sopenharmony_ci rcu_read_unlock(); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci if (err >= 0) { 24398c2ecf20Sopenharmony_ci xfrm_sk_policy_insert(sk, err, pol); 24408c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 24418c2ecf20Sopenharmony_ci __sk_dst_reset(sk); 24428c2ecf20Sopenharmony_ci err = 0; 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci kfree(data); 24468c2ecf20Sopenharmony_ci return err; 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_user_policy); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_km_lock); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ciint xfrm_register_km(struct xfrm_mgr *km) 24538c2ecf20Sopenharmony_ci{ 24548c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_km_lock); 24558c2ecf20Sopenharmony_ci list_add_tail_rcu(&km->list, &xfrm_km_list); 24568c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_km_lock); 24578c2ecf20Sopenharmony_ci return 0; 24588c2ecf20Sopenharmony_ci} 24598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_register_km); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ciint xfrm_unregister_km(struct xfrm_mgr *km) 24628c2ecf20Sopenharmony_ci{ 24638c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_km_lock); 24648c2ecf20Sopenharmony_ci list_del_rcu(&km->list); 24658c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_km_lock); 24668c2ecf20Sopenharmony_ci synchronize_rcu(); 24678c2ecf20Sopenharmony_ci return 0; 24688c2ecf20Sopenharmony_ci} 24698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_unregister_km); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ciint xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) 24728c2ecf20Sopenharmony_ci{ 24738c2ecf20Sopenharmony_ci int err = 0; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci if (WARN_ON(afinfo->family >= NPROTO)) 24768c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_state_afinfo_lock); 24798c2ecf20Sopenharmony_ci if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) 24808c2ecf20Sopenharmony_ci err = -EEXIST; 24818c2ecf20Sopenharmony_ci else 24828c2ecf20Sopenharmony_ci rcu_assign_pointer(xfrm_state_afinfo[afinfo->family], afinfo); 24838c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_state_afinfo_lock); 24848c2ecf20Sopenharmony_ci return err; 24858c2ecf20Sopenharmony_ci} 24868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_register_afinfo); 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ciint xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci int err = 0, family = afinfo->family; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci if (WARN_ON(family >= NPROTO)) 24938c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci spin_lock_bh(&xfrm_state_afinfo_lock); 24968c2ecf20Sopenharmony_ci if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { 24978c2ecf20Sopenharmony_ci if (rcu_access_pointer(xfrm_state_afinfo[family]) != afinfo) 24988c2ecf20Sopenharmony_ci err = -EINVAL; 24998c2ecf20Sopenharmony_ci else 25008c2ecf20Sopenharmony_ci RCU_INIT_POINTER(xfrm_state_afinfo[afinfo->family], NULL); 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci spin_unlock_bh(&xfrm_state_afinfo_lock); 25038c2ecf20Sopenharmony_ci synchronize_rcu(); 25048c2ecf20Sopenharmony_ci return err; 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_unregister_afinfo); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_cistruct xfrm_state_afinfo *xfrm_state_afinfo_get_rcu(unsigned int family) 25098c2ecf20Sopenharmony_ci{ 25108c2ecf20Sopenharmony_ci if (unlikely(family >= NPROTO)) 25118c2ecf20Sopenharmony_ci return NULL; 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci return rcu_dereference(xfrm_state_afinfo[family]); 25148c2ecf20Sopenharmony_ci} 25158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_state_afinfo_get_rcu); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_cistruct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) 25188c2ecf20Sopenharmony_ci{ 25198c2ecf20Sopenharmony_ci struct xfrm_state_afinfo *afinfo; 25208c2ecf20Sopenharmony_ci if (unlikely(family >= NPROTO)) 25218c2ecf20Sopenharmony_ci return NULL; 25228c2ecf20Sopenharmony_ci rcu_read_lock(); 25238c2ecf20Sopenharmony_ci afinfo = rcu_dereference(xfrm_state_afinfo[family]); 25248c2ecf20Sopenharmony_ci if (unlikely(!afinfo)) 25258c2ecf20Sopenharmony_ci rcu_read_unlock(); 25268c2ecf20Sopenharmony_ci return afinfo; 25278c2ecf20Sopenharmony_ci} 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_civoid xfrm_flush_gc(void) 25308c2ecf20Sopenharmony_ci{ 25318c2ecf20Sopenharmony_ci flush_work(&xfrm_state_gc_work); 25328c2ecf20Sopenharmony_ci} 25338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_flush_gc); 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ 25368c2ecf20Sopenharmony_civoid xfrm_state_delete_tunnel(struct xfrm_state *x) 25378c2ecf20Sopenharmony_ci{ 25388c2ecf20Sopenharmony_ci if (x->tunnel) { 25398c2ecf20Sopenharmony_ci struct xfrm_state *t = x->tunnel; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci if (atomic_read(&t->tunnel_users) == 2) 25428c2ecf20Sopenharmony_ci xfrm_state_delete(t); 25438c2ecf20Sopenharmony_ci atomic_dec(&t->tunnel_users); 25448c2ecf20Sopenharmony_ci xfrm_state_put_sync(t); 25458c2ecf20Sopenharmony_ci x->tunnel = NULL; 25468c2ecf20Sopenharmony_ci } 25478c2ecf20Sopenharmony_ci} 25488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_state_delete_tunnel); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ciu32 xfrm_state_mtu(struct xfrm_state *x, int mtu) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci const struct xfrm_type *type = READ_ONCE(x->type); 25538c2ecf20Sopenharmony_ci struct crypto_aead *aead; 25548c2ecf20Sopenharmony_ci u32 blksize, net_adj = 0; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci if (x->km.state != XFRM_STATE_VALID || 25578c2ecf20Sopenharmony_ci !type || type->proto != IPPROTO_ESP) 25588c2ecf20Sopenharmony_ci return mtu - x->props.header_len; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci aead = x->data; 25618c2ecf20Sopenharmony_ci blksize = ALIGN(crypto_aead_blocksize(aead), 4); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci switch (x->props.mode) { 25648c2ecf20Sopenharmony_ci case XFRM_MODE_TRANSPORT: 25658c2ecf20Sopenharmony_ci case XFRM_MODE_BEET: 25668c2ecf20Sopenharmony_ci if (x->props.family == AF_INET) 25678c2ecf20Sopenharmony_ci net_adj = sizeof(struct iphdr); 25688c2ecf20Sopenharmony_ci else if (x->props.family == AF_INET6) 25698c2ecf20Sopenharmony_ci net_adj = sizeof(struct ipv6hdr); 25708c2ecf20Sopenharmony_ci break; 25718c2ecf20Sopenharmony_ci case XFRM_MODE_TUNNEL: 25728c2ecf20Sopenharmony_ci break; 25738c2ecf20Sopenharmony_ci default: 25748c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 25758c2ecf20Sopenharmony_ci break; 25768c2ecf20Sopenharmony_ci } 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - 25798c2ecf20Sopenharmony_ci net_adj) & ~(blksize - 1)) + net_adj - 2; 25808c2ecf20Sopenharmony_ci} 25818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_state_mtu); 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ciint __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci const struct xfrm_mode *inner_mode; 25868c2ecf20Sopenharmony_ci const struct xfrm_mode *outer_mode; 25878c2ecf20Sopenharmony_ci int family = x->props.family; 25888c2ecf20Sopenharmony_ci int err; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci if (family == AF_INET && 25918c2ecf20Sopenharmony_ci READ_ONCE(xs_net(x)->ipv4.sysctl_ip_no_pmtu_disc)) 25928c2ecf20Sopenharmony_ci x->props.flags |= XFRM_STATE_NOPMTUDISC; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci err = -EPROTONOSUPPORT; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci if (x->sel.family != AF_UNSPEC) { 25978c2ecf20Sopenharmony_ci inner_mode = xfrm_get_mode(x->props.mode, x->sel.family); 25988c2ecf20Sopenharmony_ci if (inner_mode == NULL) 25998c2ecf20Sopenharmony_ci goto error; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) && 26028c2ecf20Sopenharmony_ci family != x->sel.family) 26038c2ecf20Sopenharmony_ci goto error; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci x->inner_mode = *inner_mode; 26068c2ecf20Sopenharmony_ci } else { 26078c2ecf20Sopenharmony_ci const struct xfrm_mode *inner_mode_iaf; 26088c2ecf20Sopenharmony_ci int iafamily = AF_INET; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci inner_mode = xfrm_get_mode(x->props.mode, x->props.family); 26118c2ecf20Sopenharmony_ci if (inner_mode == NULL) 26128c2ecf20Sopenharmony_ci goto error; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci x->inner_mode = *inner_mode; 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci if (x->props.family == AF_INET) 26178c2ecf20Sopenharmony_ci iafamily = AF_INET6; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci inner_mode_iaf = xfrm_get_mode(x->props.mode, iafamily); 26208c2ecf20Sopenharmony_ci if (inner_mode_iaf) { 26218c2ecf20Sopenharmony_ci if (inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL) 26228c2ecf20Sopenharmony_ci x->inner_mode_iaf = *inner_mode_iaf; 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci } 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci x->type = xfrm_get_type(x->id.proto, family); 26278c2ecf20Sopenharmony_ci if (x->type == NULL) 26288c2ecf20Sopenharmony_ci goto error; 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload); 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci err = x->type->init_state(x); 26338c2ecf20Sopenharmony_ci if (err) 26348c2ecf20Sopenharmony_ci goto error; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci outer_mode = xfrm_get_mode(x->props.mode, family); 26378c2ecf20Sopenharmony_ci if (!outer_mode) { 26388c2ecf20Sopenharmony_ci err = -EPROTONOSUPPORT; 26398c2ecf20Sopenharmony_ci goto error; 26408c2ecf20Sopenharmony_ci } 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci x->outer_mode = *outer_mode; 26438c2ecf20Sopenharmony_ci if (init_replay) { 26448c2ecf20Sopenharmony_ci err = xfrm_init_replay(x); 26458c2ecf20Sopenharmony_ci if (err) 26468c2ecf20Sopenharmony_ci goto error; 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cierror: 26508c2ecf20Sopenharmony_ci return err; 26518c2ecf20Sopenharmony_ci} 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_init_state); 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ciint xfrm_init_state(struct xfrm_state *x) 26568c2ecf20Sopenharmony_ci{ 26578c2ecf20Sopenharmony_ci int err; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci err = __xfrm_init_state(x, true, false); 26608c2ecf20Sopenharmony_ci if (!err) 26618c2ecf20Sopenharmony_ci x->km.state = XFRM_STATE_VALID; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci return err; 26648c2ecf20Sopenharmony_ci} 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_init_state); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ciint __net_init xfrm_state_init(struct net *net) 26698c2ecf20Sopenharmony_ci{ 26708c2ecf20Sopenharmony_ci unsigned int sz; 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci if (net_eq(net, &init_net)) 26738c2ecf20Sopenharmony_ci xfrm_state_cache = KMEM_CACHE(xfrm_state, 26748c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN | SLAB_PANIC); 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->xfrm.state_all); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci sz = sizeof(struct hlist_head) * 8; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci net->xfrm.state_bydst = xfrm_hash_alloc(sz); 26818c2ecf20Sopenharmony_ci if (!net->xfrm.state_bydst) 26828c2ecf20Sopenharmony_ci goto out_bydst; 26838c2ecf20Sopenharmony_ci net->xfrm.state_bysrc = xfrm_hash_alloc(sz); 26848c2ecf20Sopenharmony_ci if (!net->xfrm.state_bysrc) 26858c2ecf20Sopenharmony_ci goto out_bysrc; 26868c2ecf20Sopenharmony_ci net->xfrm.state_byspi = xfrm_hash_alloc(sz); 26878c2ecf20Sopenharmony_ci if (!net->xfrm.state_byspi) 26888c2ecf20Sopenharmony_ci goto out_byspi; 26898c2ecf20Sopenharmony_ci net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1); 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci net->xfrm.state_num = 0; 26928c2ecf20Sopenharmony_ci INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize); 26938c2ecf20Sopenharmony_ci spin_lock_init(&net->xfrm.xfrm_state_lock); 26948c2ecf20Sopenharmony_ci seqcount_init(&net->xfrm.xfrm_state_hash_generation); 26958c2ecf20Sopenharmony_ci return 0; 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ciout_byspi: 26988c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.state_bysrc, sz); 26998c2ecf20Sopenharmony_ciout_bysrc: 27008c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.state_bydst, sz); 27018c2ecf20Sopenharmony_ciout_bydst: 27028c2ecf20Sopenharmony_ci return -ENOMEM; 27038c2ecf20Sopenharmony_ci} 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_civoid xfrm_state_fini(struct net *net) 27068c2ecf20Sopenharmony_ci{ 27078c2ecf20Sopenharmony_ci unsigned int sz; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci flush_work(&net->xfrm.state_hash_work); 27108c2ecf20Sopenharmony_ci flush_work(&xfrm_state_gc_work); 27118c2ecf20Sopenharmony_ci xfrm_state_flush(net, 0, false, true); 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&net->xfrm.state_all)); 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head); 27168c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(net->xfrm.state_byspi)); 27178c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.state_byspi, sz); 27188c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(net->xfrm.state_bysrc)); 27198c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.state_bysrc, sz); 27208c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(net->xfrm.state_bydst)); 27218c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.state_bydst, sz); 27228c2ecf20Sopenharmony_ci} 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci#ifdef CONFIG_AUDITSYSCALL 27258c2ecf20Sopenharmony_cistatic void xfrm_audit_helper_sainfo(struct xfrm_state *x, 27268c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf) 27278c2ecf20Sopenharmony_ci{ 27288c2ecf20Sopenharmony_ci struct xfrm_sec_ctx *ctx = x->security; 27298c2ecf20Sopenharmony_ci u32 spi = ntohl(x->id.spi); 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci if (ctx) 27328c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s", 27338c2ecf20Sopenharmony_ci ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci switch (x->props.family) { 27368c2ecf20Sopenharmony_ci case AF_INET: 27378c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src=%pI4 dst=%pI4", 27388c2ecf20Sopenharmony_ci &x->props.saddr.a4, &x->id.daddr.a4); 27398c2ecf20Sopenharmony_ci break; 27408c2ecf20Sopenharmony_ci case AF_INET6: 27418c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src=%pI6 dst=%pI6", 27428c2ecf20Sopenharmony_ci x->props.saddr.a6, x->id.daddr.a6); 27438c2ecf20Sopenharmony_ci break; 27448c2ecf20Sopenharmony_ci } 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); 27478c2ecf20Sopenharmony_ci} 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_cistatic void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, 27508c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf) 27518c2ecf20Sopenharmony_ci{ 27528c2ecf20Sopenharmony_ci const struct iphdr *iph4; 27538c2ecf20Sopenharmony_ci const struct ipv6hdr *iph6; 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci switch (family) { 27568c2ecf20Sopenharmony_ci case AF_INET: 27578c2ecf20Sopenharmony_ci iph4 = ip_hdr(skb); 27588c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src=%pI4 dst=%pI4", 27598c2ecf20Sopenharmony_ci &iph4->saddr, &iph4->daddr); 27608c2ecf20Sopenharmony_ci break; 27618c2ecf20Sopenharmony_ci case AF_INET6: 27628c2ecf20Sopenharmony_ci iph6 = ipv6_hdr(skb); 27638c2ecf20Sopenharmony_ci audit_log_format(audit_buf, 27648c2ecf20Sopenharmony_ci " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x", 27658c2ecf20Sopenharmony_ci &iph6->saddr, &iph6->daddr, 27668c2ecf20Sopenharmony_ci iph6->flow_lbl[0] & 0x0f, 27678c2ecf20Sopenharmony_ci iph6->flow_lbl[1], 27688c2ecf20Sopenharmony_ci iph6->flow_lbl[2]); 27698c2ecf20Sopenharmony_ci break; 27708c2ecf20Sopenharmony_ci } 27718c2ecf20Sopenharmony_ci} 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_civoid xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid) 27748c2ecf20Sopenharmony_ci{ 27758c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SAD-add"); 27788c2ecf20Sopenharmony_ci if (audit_buf == NULL) 27798c2ecf20Sopenharmony_ci return; 27808c2ecf20Sopenharmony_ci xfrm_audit_helper_usrinfo(task_valid, audit_buf); 27818c2ecf20Sopenharmony_ci xfrm_audit_helper_sainfo(x, audit_buf); 27828c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " res=%u", result); 27838c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 27848c2ecf20Sopenharmony_ci} 27858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_add); 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_civoid xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid) 27888c2ecf20Sopenharmony_ci{ 27898c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SAD-delete"); 27928c2ecf20Sopenharmony_ci if (audit_buf == NULL) 27938c2ecf20Sopenharmony_ci return; 27948c2ecf20Sopenharmony_ci xfrm_audit_helper_usrinfo(task_valid, audit_buf); 27958c2ecf20Sopenharmony_ci xfrm_audit_helper_sainfo(x, audit_buf); 27968c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " res=%u", result); 27978c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 27988c2ecf20Sopenharmony_ci} 27998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_delete); 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_civoid xfrm_audit_state_replay_overflow(struct xfrm_state *x, 28028c2ecf20Sopenharmony_ci struct sk_buff *skb) 28038c2ecf20Sopenharmony_ci{ 28048c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 28058c2ecf20Sopenharmony_ci u32 spi; 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SA-replay-overflow"); 28088c2ecf20Sopenharmony_ci if (audit_buf == NULL) 28098c2ecf20Sopenharmony_ci return; 28108c2ecf20Sopenharmony_ci xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); 28118c2ecf20Sopenharmony_ci /* don't record the sequence number because it's inherent in this kind 28128c2ecf20Sopenharmony_ci * of audit message */ 28138c2ecf20Sopenharmony_ci spi = ntohl(x->id.spi); 28148c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); 28158c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 28168c2ecf20Sopenharmony_ci} 28178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow); 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_civoid xfrm_audit_state_replay(struct xfrm_state *x, 28208c2ecf20Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 28218c2ecf20Sopenharmony_ci{ 28228c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 28238c2ecf20Sopenharmony_ci u32 spi; 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SA-replayed-pkt"); 28268c2ecf20Sopenharmony_ci if (audit_buf == NULL) 28278c2ecf20Sopenharmony_ci return; 28288c2ecf20Sopenharmony_ci xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); 28298c2ecf20Sopenharmony_ci spi = ntohl(x->id.spi); 28308c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", 28318c2ecf20Sopenharmony_ci spi, spi, ntohl(net_seq)); 28328c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 28338c2ecf20Sopenharmony_ci} 28348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_replay); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_civoid xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family) 28378c2ecf20Sopenharmony_ci{ 28388c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SA-notfound"); 28418c2ecf20Sopenharmony_ci if (audit_buf == NULL) 28428c2ecf20Sopenharmony_ci return; 28438c2ecf20Sopenharmony_ci xfrm_audit_helper_pktinfo(skb, family, audit_buf); 28448c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 28458c2ecf20Sopenharmony_ci} 28468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple); 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_civoid xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, 28498c2ecf20Sopenharmony_ci __be32 net_spi, __be32 net_seq) 28508c2ecf20Sopenharmony_ci{ 28518c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 28528c2ecf20Sopenharmony_ci u32 spi; 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SA-notfound"); 28558c2ecf20Sopenharmony_ci if (audit_buf == NULL) 28568c2ecf20Sopenharmony_ci return; 28578c2ecf20Sopenharmony_ci xfrm_audit_helper_pktinfo(skb, family, audit_buf); 28588c2ecf20Sopenharmony_ci spi = ntohl(net_spi); 28598c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", 28608c2ecf20Sopenharmony_ci spi, spi, ntohl(net_seq)); 28618c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 28628c2ecf20Sopenharmony_ci} 28638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_notfound); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_civoid xfrm_audit_state_icvfail(struct xfrm_state *x, 28668c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 proto) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 28698c2ecf20Sopenharmony_ci __be32 net_spi; 28708c2ecf20Sopenharmony_ci __be32 net_seq; 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SA-icv-failure"); 28738c2ecf20Sopenharmony_ci if (audit_buf == NULL) 28748c2ecf20Sopenharmony_ci return; 28758c2ecf20Sopenharmony_ci xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); 28768c2ecf20Sopenharmony_ci if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) { 28778c2ecf20Sopenharmony_ci u32 spi = ntohl(net_spi); 28788c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", 28798c2ecf20Sopenharmony_ci spi, spi, ntohl(net_seq)); 28808c2ecf20Sopenharmony_ci } 28818c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 28828c2ecf20Sopenharmony_ci} 28838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail); 28848c2ecf20Sopenharmony_ci#endif /* CONFIG_AUDITSYSCALL */ 2885