18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * xfrm_policy.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 * Kazunori MIYAZAWA @USAGI 118c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki 128c2ecf20Sopenharmony_ci * Split up af-specific portion 138c2ecf20Sopenharmony_ci * Derek Atkins <derek@ihtfp.com> Add the post_input processor 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/kmod.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 228c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 238c2ecf20Sopenharmony_ci#include <linux/notifier.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/cache.h> 288c2ecf20Sopenharmony_ci#include <linux/cpu.h> 298c2ecf20Sopenharmony_ci#include <linux/audit.h> 308c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 318c2ecf20Sopenharmony_ci#include <linux/if_tunnel.h> 328c2ecf20Sopenharmony_ci#include <net/dst.h> 338c2ecf20Sopenharmony_ci#include <net/flow.h> 348c2ecf20Sopenharmony_ci#include <net/inet_ecn.h> 358c2ecf20Sopenharmony_ci#include <net/xfrm.h> 368c2ecf20Sopenharmony_ci#include <net/ip.h> 378c2ecf20Sopenharmony_ci#include <net/gre.h> 388c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 398c2ecf20Sopenharmony_ci#include <net/mip6.h> 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_STATISTICS 428c2ecf20Sopenharmony_ci#include <net/snmp.h> 438c2ecf20Sopenharmony_ci#endif 448c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_ESPINTCP 458c2ecf20Sopenharmony_ci#include <net/espintcp.h> 468c2ecf20Sopenharmony_ci#endif 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "xfrm_hash.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10)) 518c2ecf20Sopenharmony_ci#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) 528c2ecf20Sopenharmony_ci#define XFRM_MAX_QUEUE_LEN 100 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct xfrm_flo { 558c2ecf20Sopenharmony_ci struct dst_entry *dst_orig; 568c2ecf20Sopenharmony_ci u8 flags; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* prefixes smaller than this are stored in lists, not trees. */ 608c2ecf20Sopenharmony_ci#define INEXACT_PREFIXLEN_IPV4 16 618c2ecf20Sopenharmony_ci#define INEXACT_PREFIXLEN_IPV6 48 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct xfrm_pol_inexact_node { 648c2ecf20Sopenharmony_ci struct rb_node node; 658c2ecf20Sopenharmony_ci union { 668c2ecf20Sopenharmony_ci xfrm_address_t addr; 678c2ecf20Sopenharmony_ci struct rcu_head rcu; 688c2ecf20Sopenharmony_ci }; 698c2ecf20Sopenharmony_ci u8 prefixlen; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci struct rb_root root; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* the policies matching this node, can be empty list */ 748c2ecf20Sopenharmony_ci struct hlist_head hhead; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* xfrm inexact policy search tree: 788c2ecf20Sopenharmony_ci * xfrm_pol_inexact_bin = hash(dir,type,family,if_id); 798c2ecf20Sopenharmony_ci * | 808c2ecf20Sopenharmony_ci * +---- root_d: sorted by daddr:prefix 818c2ecf20Sopenharmony_ci * | | 828c2ecf20Sopenharmony_ci * | xfrm_pol_inexact_node 838c2ecf20Sopenharmony_ci * | | 848c2ecf20Sopenharmony_ci * | +- root: sorted by saddr/prefix 858c2ecf20Sopenharmony_ci * | | | 868c2ecf20Sopenharmony_ci * | | xfrm_pol_inexact_node 878c2ecf20Sopenharmony_ci * | | | 888c2ecf20Sopenharmony_ci * | | + root: unused 898c2ecf20Sopenharmony_ci * | | | 908c2ecf20Sopenharmony_ci * | | + hhead: saddr:daddr policies 918c2ecf20Sopenharmony_ci * | | 928c2ecf20Sopenharmony_ci * | +- coarse policies and all any:daddr policies 938c2ecf20Sopenharmony_ci * | 948c2ecf20Sopenharmony_ci * +---- root_s: sorted by saddr:prefix 958c2ecf20Sopenharmony_ci * | | 968c2ecf20Sopenharmony_ci * | xfrm_pol_inexact_node 978c2ecf20Sopenharmony_ci * | | 988c2ecf20Sopenharmony_ci * | + root: unused 998c2ecf20Sopenharmony_ci * | | 1008c2ecf20Sopenharmony_ci * | + hhead: saddr:any policies 1018c2ecf20Sopenharmony_ci * | 1028c2ecf20Sopenharmony_ci * +---- coarse policies and all any:any policies 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * Lookups return four candidate lists: 1058c2ecf20Sopenharmony_ci * 1. any:any list from top-level xfrm_pol_inexact_bin 1068c2ecf20Sopenharmony_ci * 2. any:daddr list from daddr tree 1078c2ecf20Sopenharmony_ci * 3. saddr:daddr list from 2nd level daddr tree 1088c2ecf20Sopenharmony_ci * 4. saddr:any list from saddr tree 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * This result set then needs to be searched for the policy with 1118c2ecf20Sopenharmony_ci * the lowest priority. If two results have same prio, youngest one wins. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct xfrm_pol_inexact_key { 1158c2ecf20Sopenharmony_ci possible_net_t net; 1168c2ecf20Sopenharmony_ci u32 if_id; 1178c2ecf20Sopenharmony_ci u16 family; 1188c2ecf20Sopenharmony_ci u8 dir, type; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistruct xfrm_pol_inexact_bin { 1228c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_key k; 1238c2ecf20Sopenharmony_ci struct rhash_head head; 1248c2ecf20Sopenharmony_ci /* list containing '*:*' policies */ 1258c2ecf20Sopenharmony_ci struct hlist_head hhead; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci seqcount_spinlock_t count; 1288c2ecf20Sopenharmony_ci /* tree sorted by daddr/prefix */ 1298c2ecf20Sopenharmony_ci struct rb_root root_d; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* tree sorted by saddr/prefix */ 1328c2ecf20Sopenharmony_ci struct rb_root root_s; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* slow path below */ 1358c2ecf20Sopenharmony_ci struct list_head inexact_bins; 1368c2ecf20Sopenharmony_ci struct rcu_head rcu; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cienum xfrm_pol_inexact_candidate_type { 1408c2ecf20Sopenharmony_ci XFRM_POL_CAND_BOTH, 1418c2ecf20Sopenharmony_ci XFRM_POL_CAND_SADDR, 1428c2ecf20Sopenharmony_ci XFRM_POL_CAND_DADDR, 1438c2ecf20Sopenharmony_ci XFRM_POL_CAND_ANY, 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci XFRM_POL_CAND_MAX, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct xfrm_pol_inexact_candidates { 1498c2ecf20Sopenharmony_ci struct hlist_head *res[XFRM_POL_CAND_MAX]; 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_if_cb_lock); 1538c2ecf20Sopenharmony_cistatic struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); 1568c2ecf20Sopenharmony_cistatic struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1] 1578c2ecf20Sopenharmony_ci __read_mostly; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct kmem_cache *xfrm_dst_cache __ro_after_init; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct rhashtable xfrm_policy_inexact_table; 1628c2ecf20Sopenharmony_cistatic const struct rhashtable_params xfrm_pol_inexact_params; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr); 1658c2ecf20Sopenharmony_cistatic int stale_bundle(struct dst_entry *dst); 1668c2ecf20Sopenharmony_cistatic int xfrm_bundle_ok(struct xfrm_dst *xdst); 1678c2ecf20Sopenharmony_cistatic void xfrm_policy_queue_process(struct timer_list *t); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void __xfrm_policy_link(struct xfrm_policy *pol, int dir); 1708c2ecf20Sopenharmony_cistatic struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, 1718c2ecf20Sopenharmony_ci int dir); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_bin * 1748c2ecf20Sopenharmony_cixfrm_policy_inexact_lookup(struct net *net, u8 type, u16 family, u8 dir, 1758c2ecf20Sopenharmony_ci u32 if_id); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_bin * 1788c2ecf20Sopenharmony_cixfrm_policy_inexact_lookup_rcu(struct net *net, 1798c2ecf20Sopenharmony_ci u8 type, u16 family, u8 dir, u32 if_id); 1808c2ecf20Sopenharmony_cistatic struct xfrm_policy * 1818c2ecf20Sopenharmony_cixfrm_policy_insert_list(struct hlist_head *chain, struct xfrm_policy *policy, 1828c2ecf20Sopenharmony_ci bool excl); 1838c2ecf20Sopenharmony_cistatic void xfrm_policy_insert_inexact_list(struct hlist_head *chain, 1848c2ecf20Sopenharmony_ci struct xfrm_policy *policy); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic bool 1878c2ecf20Sopenharmony_cixfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand, 1888c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *b, 1898c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 1908c2ecf20Sopenharmony_ci const xfrm_address_t *daddr); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return refcount_inc_not_zero(&policy->refcnt); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline bool 1988c2ecf20Sopenharmony_ci__xfrm4_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci const struct flowi4 *fl4 = &fl->u.ip4; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return addr4_match(fl4->daddr, sel->daddr.a4, sel->prefixlen_d) && 2038c2ecf20Sopenharmony_ci addr4_match(fl4->saddr, sel->saddr.a4, sel->prefixlen_s) && 2048c2ecf20Sopenharmony_ci !((xfrm_flowi_dport(fl, &fl4->uli) ^ sel->dport) & sel->dport_mask) && 2058c2ecf20Sopenharmony_ci !((xfrm_flowi_sport(fl, &fl4->uli) ^ sel->sport) & sel->sport_mask) && 2068c2ecf20Sopenharmony_ci (fl4->flowi4_proto == sel->proto || !sel->proto) && 2078c2ecf20Sopenharmony_ci (fl4->flowi4_oif == sel->ifindex || !sel->ifindex); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic inline bool 2118c2ecf20Sopenharmony_ci__xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci const struct flowi6 *fl6 = &fl->u.ip6; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return addr_match(&fl6->daddr, &sel->daddr, sel->prefixlen_d) && 2168c2ecf20Sopenharmony_ci addr_match(&fl6->saddr, &sel->saddr, sel->prefixlen_s) && 2178c2ecf20Sopenharmony_ci !((xfrm_flowi_dport(fl, &fl6->uli) ^ sel->dport) & sel->dport_mask) && 2188c2ecf20Sopenharmony_ci !((xfrm_flowi_sport(fl, &fl6->uli) ^ sel->sport) & sel->sport_mask) && 2198c2ecf20Sopenharmony_ci (fl6->flowi6_proto == sel->proto || !sel->proto) && 2208c2ecf20Sopenharmony_ci (fl6->flowi6_oif == sel->ifindex || !sel->ifindex); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cibool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, 2248c2ecf20Sopenharmony_ci unsigned short family) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci switch (family) { 2278c2ecf20Sopenharmony_ci case AF_INET: 2288c2ecf20Sopenharmony_ci return __xfrm4_selector_match(sel, fl); 2298c2ecf20Sopenharmony_ci case AF_INET6: 2308c2ecf20Sopenharmony_ci return __xfrm6_selector_match(sel, fl); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return false; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (unlikely(family >= ARRAY_SIZE(xfrm_policy_afinfo))) 2408c2ecf20Sopenharmony_ci return NULL; 2418c2ecf20Sopenharmony_ci rcu_read_lock(); 2428c2ecf20Sopenharmony_ci afinfo = rcu_dereference(xfrm_policy_afinfo[family]); 2438c2ecf20Sopenharmony_ci if (unlikely(!afinfo)) 2448c2ecf20Sopenharmony_ci rcu_read_unlock(); 2458c2ecf20Sopenharmony_ci return afinfo; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* Called with rcu_read_lock(). */ 2498c2ecf20Sopenharmony_cistatic const struct xfrm_if_cb *xfrm_if_get_cb(void) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci return rcu_dereference(xfrm_if_cb); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistruct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, 2558c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 2568c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 2578c2ecf20Sopenharmony_ci int family, u32 mark) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo; 2608c2ecf20Sopenharmony_ci struct dst_entry *dst; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci afinfo = xfrm_policy_get_afinfo(family); 2638c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 2648c2ecf20Sopenharmony_ci return ERR_PTR(-EAFNOSUPPORT); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr, mark); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci rcu_read_unlock(); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return dst; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_dst_lookup); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, 2758c2ecf20Sopenharmony_ci int tos, int oif, 2768c2ecf20Sopenharmony_ci xfrm_address_t *prev_saddr, 2778c2ecf20Sopenharmony_ci xfrm_address_t *prev_daddr, 2788c2ecf20Sopenharmony_ci int family, u32 mark) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 2818c2ecf20Sopenharmony_ci xfrm_address_t *saddr = &x->props.saddr; 2828c2ecf20Sopenharmony_ci xfrm_address_t *daddr = &x->id.daddr; 2838c2ecf20Sopenharmony_ci struct dst_entry *dst; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) { 2868c2ecf20Sopenharmony_ci saddr = x->coaddr; 2878c2ecf20Sopenharmony_ci daddr = prev_daddr; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) { 2908c2ecf20Sopenharmony_ci saddr = prev_saddr; 2918c2ecf20Sopenharmony_ci daddr = x->coaddr; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family, mark); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!IS_ERR(dst)) { 2978c2ecf20Sopenharmony_ci if (prev_saddr != saddr) 2988c2ecf20Sopenharmony_ci memcpy(prev_saddr, saddr, sizeof(*prev_saddr)); 2998c2ecf20Sopenharmony_ci if (prev_daddr != daddr) 3008c2ecf20Sopenharmony_ci memcpy(prev_daddr, daddr, sizeof(*prev_daddr)); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return dst; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic inline unsigned long make_jiffies(long secs) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) 3098c2ecf20Sopenharmony_ci return MAX_SCHEDULE_TIMEOUT-1; 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci return secs*HZ; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void xfrm_policy_timer(struct timer_list *t) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct xfrm_policy *xp = from_timer(xp, t, timer); 3178c2ecf20Sopenharmony_ci time64_t now = ktime_get_real_seconds(); 3188c2ecf20Sopenharmony_ci time64_t next = TIME64_MAX; 3198c2ecf20Sopenharmony_ci int warn = 0; 3208c2ecf20Sopenharmony_ci int dir; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci read_lock(&xp->lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (unlikely(xp->walk.dead)) 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dir = xfrm_policy_id2dir(xp->index); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (xp->lft.hard_add_expires_seconds) { 3308c2ecf20Sopenharmony_ci time64_t tmo = xp->lft.hard_add_expires_seconds + 3318c2ecf20Sopenharmony_ci xp->curlft.add_time - now; 3328c2ecf20Sopenharmony_ci if (tmo <= 0) 3338c2ecf20Sopenharmony_ci goto expired; 3348c2ecf20Sopenharmony_ci if (tmo < next) 3358c2ecf20Sopenharmony_ci next = tmo; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci if (xp->lft.hard_use_expires_seconds) { 3388c2ecf20Sopenharmony_ci time64_t tmo = xp->lft.hard_use_expires_seconds + 3398c2ecf20Sopenharmony_ci (xp->curlft.use_time ? : xp->curlft.add_time) - now; 3408c2ecf20Sopenharmony_ci if (tmo <= 0) 3418c2ecf20Sopenharmony_ci goto expired; 3428c2ecf20Sopenharmony_ci if (tmo < next) 3438c2ecf20Sopenharmony_ci next = tmo; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci if (xp->lft.soft_add_expires_seconds) { 3468c2ecf20Sopenharmony_ci time64_t tmo = xp->lft.soft_add_expires_seconds + 3478c2ecf20Sopenharmony_ci xp->curlft.add_time - now; 3488c2ecf20Sopenharmony_ci if (tmo <= 0) { 3498c2ecf20Sopenharmony_ci warn = 1; 3508c2ecf20Sopenharmony_ci tmo = XFRM_KM_TIMEOUT; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci if (tmo < next) 3538c2ecf20Sopenharmony_ci next = tmo; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (xp->lft.soft_use_expires_seconds) { 3568c2ecf20Sopenharmony_ci time64_t tmo = xp->lft.soft_use_expires_seconds + 3578c2ecf20Sopenharmony_ci (xp->curlft.use_time ? : xp->curlft.add_time) - now; 3588c2ecf20Sopenharmony_ci if (tmo <= 0) { 3598c2ecf20Sopenharmony_ci warn = 1; 3608c2ecf20Sopenharmony_ci tmo = XFRM_KM_TIMEOUT; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci if (tmo < next) 3638c2ecf20Sopenharmony_ci next = tmo; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (warn) 3678c2ecf20Sopenharmony_ci km_policy_expired(xp, dir, 0, 0); 3688c2ecf20Sopenharmony_ci if (next != TIME64_MAX && 3698c2ecf20Sopenharmony_ci !mod_timer(&xp->timer, jiffies + make_jiffies(next))) 3708c2ecf20Sopenharmony_ci xfrm_pol_hold(xp); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciout: 3738c2ecf20Sopenharmony_ci read_unlock(&xp->lock); 3748c2ecf20Sopenharmony_ci xfrm_pol_put(xp); 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ciexpired: 3788c2ecf20Sopenharmony_ci read_unlock(&xp->lock); 3798c2ecf20Sopenharmony_ci if (!xfrm_policy_delete(xp, dir)) 3808c2ecf20Sopenharmony_ci km_policy_expired(xp, dir, 1, 0); 3818c2ecf20Sopenharmony_ci xfrm_pol_put(xp); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 3858c2ecf20Sopenharmony_ci * SPD calls. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistruct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct xfrm_policy *policy; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci policy = kzalloc(sizeof(struct xfrm_policy), gfp); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (policy) { 3958c2ecf20Sopenharmony_ci write_pnet(&policy->xp_net, net); 3968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&policy->walk.all); 3978c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&policy->bydst_inexact_list); 3988c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&policy->bydst); 3998c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&policy->byidx); 4008c2ecf20Sopenharmony_ci rwlock_init(&policy->lock); 4018c2ecf20Sopenharmony_ci refcount_set(&policy->refcnt, 1); 4028c2ecf20Sopenharmony_ci skb_queue_head_init(&policy->polq.hold_queue); 4038c2ecf20Sopenharmony_ci timer_setup(&policy->timer, xfrm_policy_timer, 0); 4048c2ecf20Sopenharmony_ci timer_setup(&policy->polq.hold_timer, 4058c2ecf20Sopenharmony_ci xfrm_policy_queue_process, 0); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci return policy; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_alloc); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic void xfrm_policy_destroy_rcu(struct rcu_head *head) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct xfrm_policy *policy = container_of(head, struct xfrm_policy, rcu); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci security_xfrm_policy_free(policy->security); 4168c2ecf20Sopenharmony_ci kfree(policy); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* Destroy xfrm_policy: descendant resources must be released to this moment. */ 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_civoid xfrm_policy_destroy(struct xfrm_policy *policy) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci BUG_ON(!policy->walk.dead); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer)) 4268c2ecf20Sopenharmony_ci BUG(); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci call_rcu(&policy->rcu, xfrm_policy_destroy_rcu); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_destroy); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* Rule must be locked. Release descendant resources, announce 4338c2ecf20Sopenharmony_ci * entry dead. The rule must be unlinked from lists to the moment. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void xfrm_policy_kill(struct xfrm_policy *policy) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci write_lock_bh(&policy->lock); 4398c2ecf20Sopenharmony_ci policy->walk.dead = 1; 4408c2ecf20Sopenharmony_ci write_unlock_bh(&policy->lock); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci atomic_inc(&policy->genid); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (del_timer(&policy->polq.hold_timer)) 4458c2ecf20Sopenharmony_ci xfrm_pol_put(policy); 4468c2ecf20Sopenharmony_ci skb_queue_purge(&policy->polq.hold_queue); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (del_timer(&policy->timer)) 4498c2ecf20Sopenharmony_ci xfrm_pol_put(policy); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci xfrm_pol_put(policy); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic inline unsigned int idx_hash(struct net *net, u32 index) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci return __idx_hash(index, net->xfrm.policy_idx_hmask); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/* calculate policy hash thresholds */ 4628c2ecf20Sopenharmony_cistatic void __get_hash_thresh(struct net *net, 4638c2ecf20Sopenharmony_ci unsigned short family, int dir, 4648c2ecf20Sopenharmony_ci u8 *dbits, u8 *sbits) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci switch (family) { 4678c2ecf20Sopenharmony_ci case AF_INET: 4688c2ecf20Sopenharmony_ci *dbits = net->xfrm.policy_bydst[dir].dbits4; 4698c2ecf20Sopenharmony_ci *sbits = net->xfrm.policy_bydst[dir].sbits4; 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci case AF_INET6: 4738c2ecf20Sopenharmony_ci *dbits = net->xfrm.policy_bydst[dir].dbits6; 4748c2ecf20Sopenharmony_ci *sbits = net->xfrm.policy_bydst[dir].sbits6; 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci default: 4788c2ecf20Sopenharmony_ci *dbits = 0; 4798c2ecf20Sopenharmony_ci *sbits = 0; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic struct hlist_head *policy_hash_bysel(struct net *net, 4848c2ecf20Sopenharmony_ci const struct xfrm_selector *sel, 4858c2ecf20Sopenharmony_ci unsigned short family, int dir) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 4888c2ecf20Sopenharmony_ci unsigned int hash; 4898c2ecf20Sopenharmony_ci u8 dbits; 4908c2ecf20Sopenharmony_ci u8 sbits; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci __get_hash_thresh(net, family, dir, &dbits, &sbits); 4938c2ecf20Sopenharmony_ci hash = __sel_hash(sel, family, hmask, dbits, sbits); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (hash == hmask + 1) 4968c2ecf20Sopenharmony_ci return NULL; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return rcu_dereference_check(net->xfrm.policy_bydst[dir].table, 4998c2ecf20Sopenharmony_ci lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct hlist_head *policy_hash_direct(struct net *net, 5038c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 5048c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 5058c2ecf20Sopenharmony_ci unsigned short family, int dir) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 5088c2ecf20Sopenharmony_ci unsigned int hash; 5098c2ecf20Sopenharmony_ci u8 dbits; 5108c2ecf20Sopenharmony_ci u8 sbits; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci __get_hash_thresh(net, family, dir, &dbits, &sbits); 5138c2ecf20Sopenharmony_ci hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return rcu_dereference_check(net->xfrm.policy_bydst[dir].table, 5168c2ecf20Sopenharmony_ci lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void xfrm_dst_hash_transfer(struct net *net, 5208c2ecf20Sopenharmony_ci struct hlist_head *list, 5218c2ecf20Sopenharmony_ci struct hlist_head *ndsttable, 5228c2ecf20Sopenharmony_ci unsigned int nhashmask, 5238c2ecf20Sopenharmony_ci int dir) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct hlist_node *tmp, *entry0 = NULL; 5268c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 5278c2ecf20Sopenharmony_ci unsigned int h0 = 0; 5288c2ecf20Sopenharmony_ci u8 dbits; 5298c2ecf20Sopenharmony_ci u8 sbits; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ciredo: 5328c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(pol, tmp, list, bydst) { 5338c2ecf20Sopenharmony_ci unsigned int h; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci __get_hash_thresh(net, pol->family, dir, &dbits, &sbits); 5368c2ecf20Sopenharmony_ci h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, 5378c2ecf20Sopenharmony_ci pol->family, nhashmask, dbits, sbits); 5388c2ecf20Sopenharmony_ci if (!entry0) { 5398c2ecf20Sopenharmony_ci hlist_del_rcu(&pol->bydst); 5408c2ecf20Sopenharmony_ci hlist_add_head_rcu(&pol->bydst, ndsttable + h); 5418c2ecf20Sopenharmony_ci h0 = h; 5428c2ecf20Sopenharmony_ci } else { 5438c2ecf20Sopenharmony_ci if (h != h0) 5448c2ecf20Sopenharmony_ci continue; 5458c2ecf20Sopenharmony_ci hlist_del_rcu(&pol->bydst); 5468c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&pol->bydst, entry0); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci entry0 = &pol->bydst; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci if (!hlist_empty(list)) { 5518c2ecf20Sopenharmony_ci entry0 = NULL; 5528c2ecf20Sopenharmony_ci goto redo; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic void xfrm_idx_hash_transfer(struct hlist_head *list, 5578c2ecf20Sopenharmony_ci struct hlist_head *nidxtable, 5588c2ecf20Sopenharmony_ci unsigned int nhashmask) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct hlist_node *tmp; 5618c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(pol, tmp, list, byidx) { 5648c2ecf20Sopenharmony_ci unsigned int h; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci h = __idx_hash(pol->index, nhashmask); 5678c2ecf20Sopenharmony_ci hlist_add_head(&pol->byidx, nidxtable+h); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic unsigned long xfrm_new_hash_mask(unsigned int old_hmask) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci return ((old_hmask + 1) << 1) - 1; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic void xfrm_bydst_resize(struct net *net, int dir) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 5798c2ecf20Sopenharmony_ci unsigned int nhashmask = xfrm_new_hash_mask(hmask); 5808c2ecf20Sopenharmony_ci unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head); 5818c2ecf20Sopenharmony_ci struct hlist_head *ndst = xfrm_hash_alloc(nsize); 5828c2ecf20Sopenharmony_ci struct hlist_head *odst; 5838c2ecf20Sopenharmony_ci int i; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (!ndst) 5868c2ecf20Sopenharmony_ci return; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 5898c2ecf20Sopenharmony_ci write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, 5928c2ecf20Sopenharmony_ci lockdep_is_held(&net->xfrm.xfrm_policy_lock)); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci for (i = hmask; i >= 0; i--) 5958c2ecf20Sopenharmony_ci xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst); 5988c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].hmask = nhashmask; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation); 6018c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci synchronize_rcu(); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head)); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void xfrm_byidx_resize(struct net *net, int total) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_idx_hmask; 6118c2ecf20Sopenharmony_ci unsigned int nhashmask = xfrm_new_hash_mask(hmask); 6128c2ecf20Sopenharmony_ci unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head); 6138c2ecf20Sopenharmony_ci struct hlist_head *oidx = net->xfrm.policy_byidx; 6148c2ecf20Sopenharmony_ci struct hlist_head *nidx = xfrm_hash_alloc(nsize); 6158c2ecf20Sopenharmony_ci int i; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (!nidx) 6188c2ecf20Sopenharmony_ci return; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci for (i = hmask; i >= 0; i--) 6238c2ecf20Sopenharmony_ci xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci net->xfrm.policy_byidx = nidx; 6268c2ecf20Sopenharmony_ci net->xfrm.policy_idx_hmask = nhashmask; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head)); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci unsigned int cnt = net->xfrm.policy_count[dir]; 6368c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (total) 6398c2ecf20Sopenharmony_ci *total += cnt; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if ((hmask + 1) < xfrm_policy_hashmax && 6428c2ecf20Sopenharmony_ci cnt > hmask) 6438c2ecf20Sopenharmony_ci return 1; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic inline int xfrm_byidx_should_resize(struct net *net, int total) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci unsigned int hmask = net->xfrm.policy_idx_hmask; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if ((hmask + 1) < xfrm_policy_hashmax && 6538c2ecf20Sopenharmony_ci total > hmask) 6548c2ecf20Sopenharmony_ci return 1; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_civoid xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; 6628c2ecf20Sopenharmony_ci si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; 6638c2ecf20Sopenharmony_ci si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; 6648c2ecf20Sopenharmony_ci si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]; 6658c2ecf20Sopenharmony_ci si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]; 6668c2ecf20Sopenharmony_ci si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; 6678c2ecf20Sopenharmony_ci si->spdhcnt = net->xfrm.policy_idx_hmask; 6688c2ecf20Sopenharmony_ci si->spdhmcnt = xfrm_policy_hashmax; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_spd_getinfo); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(hash_resize_mutex); 6738c2ecf20Sopenharmony_cistatic void xfrm_hash_resize(struct work_struct *work) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct net *net = container_of(work, struct net, xfrm.policy_hash_work); 6768c2ecf20Sopenharmony_ci int dir, total; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci mutex_lock(&hash_resize_mutex); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci total = 0; 6818c2ecf20Sopenharmony_ci for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { 6828c2ecf20Sopenharmony_ci if (xfrm_bydst_should_resize(net, dir, &total)) 6838c2ecf20Sopenharmony_ci xfrm_bydst_resize(net, dir); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci if (xfrm_byidx_should_resize(net, total)) 6868c2ecf20Sopenharmony_ci xfrm_byidx_resize(net, total); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mutex_unlock(&hash_resize_mutex); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci/* Make sure *pol can be inserted into fastbin. 6928c2ecf20Sopenharmony_ci * Useful to check that later insert requests will be sucessful 6938c2ecf20Sopenharmony_ci * (provided xfrm_policy_lock is held throughout). 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_bin * 6968c2ecf20Sopenharmony_cixfrm_policy_inexact_alloc_bin(const struct xfrm_policy *pol, u8 dir) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin, *prev; 6998c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_key k = { 7008c2ecf20Sopenharmony_ci .family = pol->family, 7018c2ecf20Sopenharmony_ci .type = pol->type, 7028c2ecf20Sopenharmony_ci .dir = dir, 7038c2ecf20Sopenharmony_ci .if_id = pol->if_id, 7048c2ecf20Sopenharmony_ci }; 7058c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci write_pnet(&k.net, net); 7108c2ecf20Sopenharmony_ci bin = rhashtable_lookup_fast(&xfrm_policy_inexact_table, &k, 7118c2ecf20Sopenharmony_ci xfrm_pol_inexact_params); 7128c2ecf20Sopenharmony_ci if (bin) 7138c2ecf20Sopenharmony_ci return bin; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci bin = kzalloc(sizeof(*bin), GFP_ATOMIC); 7168c2ecf20Sopenharmony_ci if (!bin) 7178c2ecf20Sopenharmony_ci return NULL; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci bin->k = k; 7208c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&bin->hhead); 7218c2ecf20Sopenharmony_ci bin->root_d = RB_ROOT; 7228c2ecf20Sopenharmony_ci bin->root_s = RB_ROOT; 7238c2ecf20Sopenharmony_ci seqcount_spinlock_init(&bin->count, &net->xfrm.xfrm_policy_lock); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci prev = rhashtable_lookup_get_insert_key(&xfrm_policy_inexact_table, 7268c2ecf20Sopenharmony_ci &bin->k, &bin->head, 7278c2ecf20Sopenharmony_ci xfrm_pol_inexact_params); 7288c2ecf20Sopenharmony_ci if (!prev) { 7298c2ecf20Sopenharmony_ci list_add(&bin->inexact_bins, &net->xfrm.inexact_bins); 7308c2ecf20Sopenharmony_ci return bin; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci kfree(bin); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return IS_ERR(prev) ? NULL : prev; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic bool xfrm_pol_inexact_addr_use_any_list(const xfrm_address_t *addr, 7398c2ecf20Sopenharmony_ci int family, u8 prefixlen) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci if (xfrm_addr_any(addr, family)) 7428c2ecf20Sopenharmony_ci return true; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (family == AF_INET6 && prefixlen < INEXACT_PREFIXLEN_IPV6) 7458c2ecf20Sopenharmony_ci return true; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (family == AF_INET && prefixlen < INEXACT_PREFIXLEN_IPV4) 7488c2ecf20Sopenharmony_ci return true; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return false; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic bool 7548c2ecf20Sopenharmony_cixfrm_policy_inexact_insert_use_any_list(const struct xfrm_policy *policy) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci const xfrm_address_t *addr; 7578c2ecf20Sopenharmony_ci bool saddr_any, daddr_any; 7588c2ecf20Sopenharmony_ci u8 prefixlen; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci addr = &policy->selector.saddr; 7618c2ecf20Sopenharmony_ci prefixlen = policy->selector.prefixlen_s; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci saddr_any = xfrm_pol_inexact_addr_use_any_list(addr, 7648c2ecf20Sopenharmony_ci policy->family, 7658c2ecf20Sopenharmony_ci prefixlen); 7668c2ecf20Sopenharmony_ci addr = &policy->selector.daddr; 7678c2ecf20Sopenharmony_ci prefixlen = policy->selector.prefixlen_d; 7688c2ecf20Sopenharmony_ci daddr_any = xfrm_pol_inexact_addr_use_any_list(addr, 7698c2ecf20Sopenharmony_ci policy->family, 7708c2ecf20Sopenharmony_ci prefixlen); 7718c2ecf20Sopenharmony_ci return saddr_any && daddr_any; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void xfrm_pol_inexact_node_init(struct xfrm_pol_inexact_node *node, 7758c2ecf20Sopenharmony_ci const xfrm_address_t *addr, u8 prefixlen) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci node->addr = *addr; 7788c2ecf20Sopenharmony_ci node->prefixlen = prefixlen; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_node * 7828c2ecf20Sopenharmony_cixfrm_pol_inexact_node_alloc(const xfrm_address_t *addr, u8 prefixlen) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci node = kzalloc(sizeof(*node), GFP_ATOMIC); 7878c2ecf20Sopenharmony_ci if (node) 7888c2ecf20Sopenharmony_ci xfrm_pol_inexact_node_init(node, addr, prefixlen); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return node; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic int xfrm_policy_addr_delta(const xfrm_address_t *a, 7948c2ecf20Sopenharmony_ci const xfrm_address_t *b, 7958c2ecf20Sopenharmony_ci u8 prefixlen, u16 family) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci u32 ma, mb, mask; 7988c2ecf20Sopenharmony_ci unsigned int pdw, pbi; 7998c2ecf20Sopenharmony_ci int delta = 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci switch (family) { 8028c2ecf20Sopenharmony_ci case AF_INET: 8038c2ecf20Sopenharmony_ci if (prefixlen == 0) 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci mask = ~0U << (32 - prefixlen); 8068c2ecf20Sopenharmony_ci ma = ntohl(a->a4) & mask; 8078c2ecf20Sopenharmony_ci mb = ntohl(b->a4) & mask; 8088c2ecf20Sopenharmony_ci if (ma < mb) 8098c2ecf20Sopenharmony_ci delta = -1; 8108c2ecf20Sopenharmony_ci else if (ma > mb) 8118c2ecf20Sopenharmony_ci delta = 1; 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci case AF_INET6: 8148c2ecf20Sopenharmony_ci pdw = prefixlen >> 5; 8158c2ecf20Sopenharmony_ci pbi = prefixlen & 0x1f; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (pdw) { 8188c2ecf20Sopenharmony_ci delta = memcmp(a->a6, b->a6, pdw << 2); 8198c2ecf20Sopenharmony_ci if (delta) 8208c2ecf20Sopenharmony_ci return delta; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci if (pbi) { 8238c2ecf20Sopenharmony_ci mask = ~0U << (32 - pbi); 8248c2ecf20Sopenharmony_ci ma = ntohl(a->a6[pdw]) & mask; 8258c2ecf20Sopenharmony_ci mb = ntohl(b->a6[pdw]) & mask; 8268c2ecf20Sopenharmony_ci if (ma < mb) 8278c2ecf20Sopenharmony_ci delta = -1; 8288c2ecf20Sopenharmony_ci else if (ma > mb) 8298c2ecf20Sopenharmony_ci delta = 1; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci return delta; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic void xfrm_policy_inexact_list_reinsert(struct net *net, 8408c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *n, 8418c2ecf20Sopenharmony_ci u16 family) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci unsigned int matched_s, matched_d; 8448c2ecf20Sopenharmony_ci struct xfrm_policy *policy, *p; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci matched_s = 0; 8478c2ecf20Sopenharmony_ci matched_d = 0; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) { 8508c2ecf20Sopenharmony_ci struct hlist_node *newpos = NULL; 8518c2ecf20Sopenharmony_ci bool matches_s, matches_d; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!policy->bydst_reinsert) 8548c2ecf20Sopenharmony_ci continue; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci WARN_ON_ONCE(policy->family != family); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci policy->bydst_reinsert = false; 8598c2ecf20Sopenharmony_ci hlist_for_each_entry(p, &n->hhead, bydst) { 8608c2ecf20Sopenharmony_ci if (policy->priority > p->priority) 8618c2ecf20Sopenharmony_ci newpos = &p->bydst; 8628c2ecf20Sopenharmony_ci else if (policy->priority == p->priority && 8638c2ecf20Sopenharmony_ci policy->pos > p->pos) 8648c2ecf20Sopenharmony_ci newpos = &p->bydst; 8658c2ecf20Sopenharmony_ci else 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (newpos) 8708c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&policy->bydst, newpos); 8718c2ecf20Sopenharmony_ci else 8728c2ecf20Sopenharmony_ci hlist_add_head_rcu(&policy->bydst, &n->hhead); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* paranoia checks follow. 8758c2ecf20Sopenharmony_ci * Check that the reinserted policy matches at least 8768c2ecf20Sopenharmony_ci * saddr or daddr for current node prefix. 8778c2ecf20Sopenharmony_ci * 8788c2ecf20Sopenharmony_ci * Matching both is fine, matching saddr in one policy 8798c2ecf20Sopenharmony_ci * (but not daddr) and then matching only daddr in another 8808c2ecf20Sopenharmony_ci * is a bug. 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci matches_s = xfrm_policy_addr_delta(&policy->selector.saddr, 8838c2ecf20Sopenharmony_ci &n->addr, 8848c2ecf20Sopenharmony_ci n->prefixlen, 8858c2ecf20Sopenharmony_ci family) == 0; 8868c2ecf20Sopenharmony_ci matches_d = xfrm_policy_addr_delta(&policy->selector.daddr, 8878c2ecf20Sopenharmony_ci &n->addr, 8888c2ecf20Sopenharmony_ci n->prefixlen, 8898c2ecf20Sopenharmony_ci family) == 0; 8908c2ecf20Sopenharmony_ci if (matches_s && matches_d) 8918c2ecf20Sopenharmony_ci continue; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci WARN_ON_ONCE(!matches_s && !matches_d); 8948c2ecf20Sopenharmony_ci if (matches_s) 8958c2ecf20Sopenharmony_ci matched_s++; 8968c2ecf20Sopenharmony_ci if (matches_d) 8978c2ecf20Sopenharmony_ci matched_d++; 8988c2ecf20Sopenharmony_ci WARN_ON_ONCE(matched_s && matched_d); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic void xfrm_policy_inexact_node_reinsert(struct net *net, 9038c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *n, 9048c2ecf20Sopenharmony_ci struct rb_root *new, 9058c2ecf20Sopenharmony_ci u16 family) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 9088c2ecf20Sopenharmony_ci struct rb_node **p, *parent; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* we should not have another subtree here */ 9118c2ecf20Sopenharmony_ci WARN_ON_ONCE(!RB_EMPTY_ROOT(&n->root)); 9128c2ecf20Sopenharmony_cirestart: 9138c2ecf20Sopenharmony_ci parent = NULL; 9148c2ecf20Sopenharmony_ci p = &new->rb_node; 9158c2ecf20Sopenharmony_ci while (*p) { 9168c2ecf20Sopenharmony_ci u8 prefixlen; 9178c2ecf20Sopenharmony_ci int delta; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci parent = *p; 9208c2ecf20Sopenharmony_ci node = rb_entry(*p, struct xfrm_pol_inexact_node, node); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci prefixlen = min(node->prefixlen, n->prefixlen); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci delta = xfrm_policy_addr_delta(&n->addr, &node->addr, 9258c2ecf20Sopenharmony_ci prefixlen, family); 9268c2ecf20Sopenharmony_ci if (delta < 0) { 9278c2ecf20Sopenharmony_ci p = &parent->rb_left; 9288c2ecf20Sopenharmony_ci } else if (delta > 0) { 9298c2ecf20Sopenharmony_ci p = &parent->rb_right; 9308c2ecf20Sopenharmony_ci } else { 9318c2ecf20Sopenharmony_ci bool same_prefixlen = node->prefixlen == n->prefixlen; 9328c2ecf20Sopenharmony_ci struct xfrm_policy *tmp; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci hlist_for_each_entry(tmp, &n->hhead, bydst) { 9358c2ecf20Sopenharmony_ci tmp->bydst_reinsert = true; 9368c2ecf20Sopenharmony_ci hlist_del_rcu(&tmp->bydst); 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci node->prefixlen = prefixlen; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci xfrm_policy_inexact_list_reinsert(net, node, family); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (same_prefixlen) { 9448c2ecf20Sopenharmony_ci kfree_rcu(n, rcu); 9458c2ecf20Sopenharmony_ci return; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci rb_erase(*p, new); 9498c2ecf20Sopenharmony_ci kfree_rcu(n, rcu); 9508c2ecf20Sopenharmony_ci n = node; 9518c2ecf20Sopenharmony_ci goto restart; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci rb_link_node_rcu(&n->node, parent, p); 9568c2ecf20Sopenharmony_ci rb_insert_color(&n->node, new); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/* merge nodes v and n */ 9608c2ecf20Sopenharmony_cistatic void xfrm_policy_inexact_node_merge(struct net *net, 9618c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *v, 9628c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *n, 9638c2ecf20Sopenharmony_ci u16 family) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 9668c2ecf20Sopenharmony_ci struct xfrm_policy *tmp; 9678c2ecf20Sopenharmony_ci struct rb_node *rnode; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* To-be-merged node v has a subtree. 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * Dismantle it and insert its nodes to n->root. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_ci while ((rnode = rb_first(&v->root)) != NULL) { 9748c2ecf20Sopenharmony_ci node = rb_entry(rnode, struct xfrm_pol_inexact_node, node); 9758c2ecf20Sopenharmony_ci rb_erase(&node->node, &v->root); 9768c2ecf20Sopenharmony_ci xfrm_policy_inexact_node_reinsert(net, node, &n->root, 9778c2ecf20Sopenharmony_ci family); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci hlist_for_each_entry(tmp, &v->hhead, bydst) { 9818c2ecf20Sopenharmony_ci tmp->bydst_reinsert = true; 9828c2ecf20Sopenharmony_ci hlist_del_rcu(&tmp->bydst); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci xfrm_policy_inexact_list_reinsert(net, n, family); 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_node * 9898c2ecf20Sopenharmony_cixfrm_policy_inexact_insert_node(struct net *net, 9908c2ecf20Sopenharmony_ci struct rb_root *root, 9918c2ecf20Sopenharmony_ci xfrm_address_t *addr, 9928c2ecf20Sopenharmony_ci u16 family, u8 prefixlen, u8 dir) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *cached = NULL; 9958c2ecf20Sopenharmony_ci struct rb_node **p, *parent = NULL; 9968c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci p = &root->rb_node; 9998c2ecf20Sopenharmony_ci while (*p) { 10008c2ecf20Sopenharmony_ci int delta; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci parent = *p; 10038c2ecf20Sopenharmony_ci node = rb_entry(*p, struct xfrm_pol_inexact_node, node); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci delta = xfrm_policy_addr_delta(addr, &node->addr, 10068c2ecf20Sopenharmony_ci node->prefixlen, 10078c2ecf20Sopenharmony_ci family); 10088c2ecf20Sopenharmony_ci if (delta == 0 && prefixlen >= node->prefixlen) { 10098c2ecf20Sopenharmony_ci WARN_ON_ONCE(cached); /* ipsec policies got lost */ 10108c2ecf20Sopenharmony_ci return node; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (delta < 0) 10148c2ecf20Sopenharmony_ci p = &parent->rb_left; 10158c2ecf20Sopenharmony_ci else 10168c2ecf20Sopenharmony_ci p = &parent->rb_right; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (prefixlen < node->prefixlen) { 10198c2ecf20Sopenharmony_ci delta = xfrm_policy_addr_delta(addr, &node->addr, 10208c2ecf20Sopenharmony_ci prefixlen, 10218c2ecf20Sopenharmony_ci family); 10228c2ecf20Sopenharmony_ci if (delta) 10238c2ecf20Sopenharmony_ci continue; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* This node is a subnet of the new prefix. It needs 10268c2ecf20Sopenharmony_ci * to be removed and re-inserted with the smaller 10278c2ecf20Sopenharmony_ci * prefix and all nodes that are now also covered 10288c2ecf20Sopenharmony_ci * by the reduced prefixlen. 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ci rb_erase(&node->node, root); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (!cached) { 10338c2ecf20Sopenharmony_ci xfrm_pol_inexact_node_init(node, addr, 10348c2ecf20Sopenharmony_ci prefixlen); 10358c2ecf20Sopenharmony_ci cached = node; 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci /* This node also falls within the new 10388c2ecf20Sopenharmony_ci * prefixlen. Merge the to-be-reinserted 10398c2ecf20Sopenharmony_ci * node and this one. 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ci xfrm_policy_inexact_node_merge(net, node, 10428c2ecf20Sopenharmony_ci cached, family); 10438c2ecf20Sopenharmony_ci kfree_rcu(node, rcu); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* restart */ 10478c2ecf20Sopenharmony_ci p = &root->rb_node; 10488c2ecf20Sopenharmony_ci parent = NULL; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci node = cached; 10538c2ecf20Sopenharmony_ci if (!node) { 10548c2ecf20Sopenharmony_ci node = xfrm_pol_inexact_node_alloc(addr, prefixlen); 10558c2ecf20Sopenharmony_ci if (!node) 10568c2ecf20Sopenharmony_ci return NULL; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci rb_link_node_rcu(&node->node, parent, p); 10608c2ecf20Sopenharmony_ci rb_insert_color(&node->node, root); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return node; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic void xfrm_policy_inexact_gc_tree(struct rb_root *r, bool rm) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 10688c2ecf20Sopenharmony_ci struct rb_node *rn = rb_first(r); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci while (rn) { 10718c2ecf20Sopenharmony_ci node = rb_entry(rn, struct xfrm_pol_inexact_node, node); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci xfrm_policy_inexact_gc_tree(&node->root, rm); 10748c2ecf20Sopenharmony_ci rn = rb_next(rn); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (!hlist_empty(&node->hhead) || !RB_EMPTY_ROOT(&node->root)) { 10778c2ecf20Sopenharmony_ci WARN_ON_ONCE(rm); 10788c2ecf20Sopenharmony_ci continue; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci rb_erase(&node->node, r); 10828c2ecf20Sopenharmony_ci kfree_rcu(node, rcu); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool net_exit) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci write_seqcount_begin(&b->count); 10898c2ecf20Sopenharmony_ci xfrm_policy_inexact_gc_tree(&b->root_d, net_exit); 10908c2ecf20Sopenharmony_ci xfrm_policy_inexact_gc_tree(&b->root_s, net_exit); 10918c2ecf20Sopenharmony_ci write_seqcount_end(&b->count); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (!RB_EMPTY_ROOT(&b->root_d) || !RB_EMPTY_ROOT(&b->root_s) || 10948c2ecf20Sopenharmony_ci !hlist_empty(&b->hhead)) { 10958c2ecf20Sopenharmony_ci WARN_ON_ONCE(net_exit); 10968c2ecf20Sopenharmony_ci return; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (rhashtable_remove_fast(&xfrm_policy_inexact_table, &b->head, 11008c2ecf20Sopenharmony_ci xfrm_pol_inexact_params) == 0) { 11018c2ecf20Sopenharmony_ci list_del(&b->inexact_bins); 11028c2ecf20Sopenharmony_ci kfree_rcu(b, rcu); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct net *net = read_pnet(&b->k.net); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 11118c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(b, false); 11128c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic void __xfrm_policy_inexact_flush(struct net *net) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin, *t; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci list_for_each_entry_safe(bin, t, &net->xfrm.inexact_bins, inexact_bins) 11228c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(bin, false); 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic struct hlist_head * 11268c2ecf20Sopenharmony_cixfrm_policy_inexact_alloc_chain(struct xfrm_pol_inexact_bin *bin, 11278c2ecf20Sopenharmony_ci struct xfrm_policy *policy, u8 dir) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *n; 11308c2ecf20Sopenharmony_ci struct net *net; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci net = xp_net(policy); 11338c2ecf20Sopenharmony_ci lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (xfrm_policy_inexact_insert_use_any_list(policy)) 11368c2ecf20Sopenharmony_ci return &bin->hhead; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (xfrm_pol_inexact_addr_use_any_list(&policy->selector.daddr, 11398c2ecf20Sopenharmony_ci policy->family, 11408c2ecf20Sopenharmony_ci policy->selector.prefixlen_d)) { 11418c2ecf20Sopenharmony_ci write_seqcount_begin(&bin->count); 11428c2ecf20Sopenharmony_ci n = xfrm_policy_inexact_insert_node(net, 11438c2ecf20Sopenharmony_ci &bin->root_s, 11448c2ecf20Sopenharmony_ci &policy->selector.saddr, 11458c2ecf20Sopenharmony_ci policy->family, 11468c2ecf20Sopenharmony_ci policy->selector.prefixlen_s, 11478c2ecf20Sopenharmony_ci dir); 11488c2ecf20Sopenharmony_ci write_seqcount_end(&bin->count); 11498c2ecf20Sopenharmony_ci if (!n) 11508c2ecf20Sopenharmony_ci return NULL; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return &n->hhead; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* daddr is fixed */ 11568c2ecf20Sopenharmony_ci write_seqcount_begin(&bin->count); 11578c2ecf20Sopenharmony_ci n = xfrm_policy_inexact_insert_node(net, 11588c2ecf20Sopenharmony_ci &bin->root_d, 11598c2ecf20Sopenharmony_ci &policy->selector.daddr, 11608c2ecf20Sopenharmony_ci policy->family, 11618c2ecf20Sopenharmony_ci policy->selector.prefixlen_d, dir); 11628c2ecf20Sopenharmony_ci write_seqcount_end(&bin->count); 11638c2ecf20Sopenharmony_ci if (!n) 11648c2ecf20Sopenharmony_ci return NULL; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* saddr is wildcard */ 11678c2ecf20Sopenharmony_ci if (xfrm_pol_inexact_addr_use_any_list(&policy->selector.saddr, 11688c2ecf20Sopenharmony_ci policy->family, 11698c2ecf20Sopenharmony_ci policy->selector.prefixlen_s)) 11708c2ecf20Sopenharmony_ci return &n->hhead; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci write_seqcount_begin(&bin->count); 11738c2ecf20Sopenharmony_ci n = xfrm_policy_inexact_insert_node(net, 11748c2ecf20Sopenharmony_ci &n->root, 11758c2ecf20Sopenharmony_ci &policy->selector.saddr, 11768c2ecf20Sopenharmony_ci policy->family, 11778c2ecf20Sopenharmony_ci policy->selector.prefixlen_s, dir); 11788c2ecf20Sopenharmony_ci write_seqcount_end(&bin->count); 11798c2ecf20Sopenharmony_ci if (!n) 11808c2ecf20Sopenharmony_ci return NULL; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return &n->hhead; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic struct xfrm_policy * 11868c2ecf20Sopenharmony_cixfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin; 11898c2ecf20Sopenharmony_ci struct xfrm_policy *delpol; 11908c2ecf20Sopenharmony_ci struct hlist_head *chain; 11918c2ecf20Sopenharmony_ci struct net *net; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci bin = xfrm_policy_inexact_alloc_bin(policy, dir); 11948c2ecf20Sopenharmony_ci if (!bin) 11958c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci net = xp_net(policy); 11988c2ecf20Sopenharmony_ci lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci chain = xfrm_policy_inexact_alloc_chain(bin, policy, dir); 12018c2ecf20Sopenharmony_ci if (!chain) { 12028c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(bin, false); 12038c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci delpol = xfrm_policy_insert_list(chain, policy, excl); 12078c2ecf20Sopenharmony_ci if (delpol && excl) { 12088c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(bin, false); 12098c2ecf20Sopenharmony_ci return ERR_PTR(-EEXIST); 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci chain = &net->xfrm.policy_inexact[dir]; 12138c2ecf20Sopenharmony_ci xfrm_policy_insert_inexact_list(chain, policy); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (delpol) 12168c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(bin, false); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return delpol; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic void xfrm_hash_rebuild(struct work_struct *work) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct net *net = container_of(work, struct net, 12248c2ecf20Sopenharmony_ci xfrm.policy_hthresh.work); 12258c2ecf20Sopenharmony_ci unsigned int hmask; 12268c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 12278c2ecf20Sopenharmony_ci struct xfrm_policy *policy; 12288c2ecf20Sopenharmony_ci struct hlist_head *chain; 12298c2ecf20Sopenharmony_ci struct hlist_head *odst; 12308c2ecf20Sopenharmony_ci struct hlist_node *newpos; 12318c2ecf20Sopenharmony_ci int i; 12328c2ecf20Sopenharmony_ci int dir; 12338c2ecf20Sopenharmony_ci unsigned seq; 12348c2ecf20Sopenharmony_ci u8 lbits4, rbits4, lbits6, rbits6; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci mutex_lock(&hash_resize_mutex); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* read selector prefixlen thresholds */ 12398c2ecf20Sopenharmony_ci do { 12408c2ecf20Sopenharmony_ci seq = read_seqbegin(&net->xfrm.policy_hthresh.lock); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci lbits4 = net->xfrm.policy_hthresh.lbits4; 12438c2ecf20Sopenharmony_ci rbits4 = net->xfrm.policy_hthresh.rbits4; 12448c2ecf20Sopenharmony_ci lbits6 = net->xfrm.policy_hthresh.lbits6; 12458c2ecf20Sopenharmony_ci rbits6 = net->xfrm.policy_hthresh.rbits6; 12468c2ecf20Sopenharmony_ci } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq)); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 12498c2ecf20Sopenharmony_ci write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* make sure that we can insert the indirect policies again before 12528c2ecf20Sopenharmony_ci * we start with destructive action. 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci list_for_each_entry(policy, &net->xfrm.policy_all, walk.all) { 12558c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin; 12568c2ecf20Sopenharmony_ci u8 dbits, sbits; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci dir = xfrm_policy_id2dir(policy->index); 12598c2ecf20Sopenharmony_ci if (policy->walk.dead || dir >= XFRM_POLICY_MAX) 12608c2ecf20Sopenharmony_ci continue; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) { 12638c2ecf20Sopenharmony_ci if (policy->family == AF_INET) { 12648c2ecf20Sopenharmony_ci dbits = rbits4; 12658c2ecf20Sopenharmony_ci sbits = lbits4; 12668c2ecf20Sopenharmony_ci } else { 12678c2ecf20Sopenharmony_ci dbits = rbits6; 12688c2ecf20Sopenharmony_ci sbits = lbits6; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci } else { 12718c2ecf20Sopenharmony_ci if (policy->family == AF_INET) { 12728c2ecf20Sopenharmony_ci dbits = lbits4; 12738c2ecf20Sopenharmony_ci sbits = rbits4; 12748c2ecf20Sopenharmony_ci } else { 12758c2ecf20Sopenharmony_ci dbits = lbits6; 12768c2ecf20Sopenharmony_ci sbits = rbits6; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (policy->selector.prefixlen_d < dbits || 12818c2ecf20Sopenharmony_ci policy->selector.prefixlen_s < sbits) 12828c2ecf20Sopenharmony_ci continue; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci bin = xfrm_policy_inexact_alloc_bin(policy, dir); 12858c2ecf20Sopenharmony_ci if (!bin) 12868c2ecf20Sopenharmony_ci goto out_unlock; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (!xfrm_policy_inexact_alloc_chain(bin, policy, dir)) 12898c2ecf20Sopenharmony_ci goto out_unlock; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci /* reset the bydst and inexact table in all directions */ 12938c2ecf20Sopenharmony_ci for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { 12948c2ecf20Sopenharmony_ci struct hlist_node *n; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(policy, n, 12978c2ecf20Sopenharmony_ci &net->xfrm.policy_inexact[dir], 12988c2ecf20Sopenharmony_ci bydst_inexact_list) { 12998c2ecf20Sopenharmony_ci hlist_del_rcu(&policy->bydst); 13008c2ecf20Sopenharmony_ci hlist_del_init(&policy->bydst_inexact_list); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci hmask = net->xfrm.policy_bydst[dir].hmask; 13048c2ecf20Sopenharmony_ci odst = net->xfrm.policy_bydst[dir].table; 13058c2ecf20Sopenharmony_ci for (i = hmask; i >= 0; i--) { 13068c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(policy, n, odst + i, bydst) 13078c2ecf20Sopenharmony_ci hlist_del_rcu(&policy->bydst); 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) { 13108c2ecf20Sopenharmony_ci /* dir out => dst = remote, src = local */ 13118c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].dbits4 = rbits4; 13128c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].sbits4 = lbits4; 13138c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].dbits6 = rbits6; 13148c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].sbits6 = lbits6; 13158c2ecf20Sopenharmony_ci } else { 13168c2ecf20Sopenharmony_ci /* dir in/fwd => dst = local, src = remote */ 13178c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].dbits4 = lbits4; 13188c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].sbits4 = rbits4; 13198c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].dbits6 = lbits6; 13208c2ecf20Sopenharmony_ci net->xfrm.policy_bydst[dir].sbits6 = rbits6; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* re-insert all policies by order of creation */ 13258c2ecf20Sopenharmony_ci list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) { 13268c2ecf20Sopenharmony_ci if (policy->walk.dead) 13278c2ecf20Sopenharmony_ci continue; 13288c2ecf20Sopenharmony_ci dir = xfrm_policy_id2dir(policy->index); 13298c2ecf20Sopenharmony_ci if (dir >= XFRM_POLICY_MAX) { 13308c2ecf20Sopenharmony_ci /* skip socket policies */ 13318c2ecf20Sopenharmony_ci continue; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci newpos = NULL; 13348c2ecf20Sopenharmony_ci chain = policy_hash_bysel(net, &policy->selector, 13358c2ecf20Sopenharmony_ci policy->family, dir); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (!chain) { 13388c2ecf20Sopenharmony_ci void *p = xfrm_policy_inexact_insert(policy, dir, 0); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci WARN_ONCE(IS_ERR(p), "reinsert: %ld\n", PTR_ERR(p)); 13418c2ecf20Sopenharmony_ci continue; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst) { 13458c2ecf20Sopenharmony_ci if (policy->priority >= pol->priority) 13468c2ecf20Sopenharmony_ci newpos = &pol->bydst; 13478c2ecf20Sopenharmony_ci else 13488c2ecf20Sopenharmony_ci break; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci if (newpos) 13518c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&policy->bydst, newpos); 13528c2ecf20Sopenharmony_ci else 13538c2ecf20Sopenharmony_ci hlist_add_head_rcu(&policy->bydst, chain); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ciout_unlock: 13578c2ecf20Sopenharmony_ci __xfrm_policy_inexact_flush(net); 13588c2ecf20Sopenharmony_ci write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation); 13598c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci mutex_unlock(&hash_resize_mutex); 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_civoid xfrm_policy_hash_rebuild(struct net *net) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci schedule_work(&net->xfrm.policy_hthresh.work); 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_hash_rebuild); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci/* Generate new index... KAME seems to generate them ordered by cost 13718c2ecf20Sopenharmony_ci * of an absolute inpredictability of ordering of rules. This will not pass. */ 13728c2ecf20Sopenharmony_cistatic u32 xfrm_gen_index(struct net *net, int dir, u32 index) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci for (;;) { 13758c2ecf20Sopenharmony_ci struct hlist_head *list; 13768c2ecf20Sopenharmony_ci struct xfrm_policy *p; 13778c2ecf20Sopenharmony_ci u32 idx; 13788c2ecf20Sopenharmony_ci int found; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (!index) { 13818c2ecf20Sopenharmony_ci idx = (net->xfrm.idx_generator | dir); 13828c2ecf20Sopenharmony_ci net->xfrm.idx_generator += 8; 13838c2ecf20Sopenharmony_ci } else { 13848c2ecf20Sopenharmony_ci idx = index; 13858c2ecf20Sopenharmony_ci index = 0; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (idx == 0) 13898c2ecf20Sopenharmony_ci idx = 8; 13908c2ecf20Sopenharmony_ci list = net->xfrm.policy_byidx + idx_hash(net, idx); 13918c2ecf20Sopenharmony_ci found = 0; 13928c2ecf20Sopenharmony_ci hlist_for_each_entry(p, list, byidx) { 13938c2ecf20Sopenharmony_ci if (p->index == idx) { 13948c2ecf20Sopenharmony_ci found = 1; 13958c2ecf20Sopenharmony_ci break; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci if (!found) 13998c2ecf20Sopenharmony_ci return idx; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s2) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci u32 *p1 = (u32 *) s1; 14068c2ecf20Sopenharmony_ci u32 *p2 = (u32 *) s2; 14078c2ecf20Sopenharmony_ci int len = sizeof(struct xfrm_selector) / sizeof(u32); 14088c2ecf20Sopenharmony_ci int i; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 14118c2ecf20Sopenharmony_ci if (p1[i] != p2[i]) 14128c2ecf20Sopenharmony_ci return 1; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic void xfrm_policy_requeue(struct xfrm_policy *old, 14198c2ecf20Sopenharmony_ci struct xfrm_policy *new) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct xfrm_policy_queue *pq = &old->polq; 14228c2ecf20Sopenharmony_ci struct sk_buff_head list; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (skb_queue_empty(&pq->hold_queue)) 14258c2ecf20Sopenharmony_ci return; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci __skb_queue_head_init(&list); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci spin_lock_bh(&pq->hold_queue.lock); 14308c2ecf20Sopenharmony_ci skb_queue_splice_init(&pq->hold_queue, &list); 14318c2ecf20Sopenharmony_ci if (del_timer(&pq->hold_timer)) 14328c2ecf20Sopenharmony_ci xfrm_pol_put(old); 14338c2ecf20Sopenharmony_ci spin_unlock_bh(&pq->hold_queue.lock); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci pq = &new->polq; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci spin_lock_bh(&pq->hold_queue.lock); 14388c2ecf20Sopenharmony_ci skb_queue_splice(&list, &pq->hold_queue); 14398c2ecf20Sopenharmony_ci pq->timeout = XFRM_QUEUE_TMO_MIN; 14408c2ecf20Sopenharmony_ci if (!mod_timer(&pq->hold_timer, jiffies)) 14418c2ecf20Sopenharmony_ci xfrm_pol_hold(new); 14428c2ecf20Sopenharmony_ci spin_unlock_bh(&pq->hold_queue.lock); 14438c2ecf20Sopenharmony_ci} 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_cistatic inline bool xfrm_policy_mark_match(const struct xfrm_mark *mark, 14468c2ecf20Sopenharmony_ci struct xfrm_policy *pol) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci return mark->v == pol->mark.v && mark->m == pol->mark.m; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic u32 xfrm_pol_bin_key(const void *data, u32 len, u32 seed) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci const struct xfrm_pol_inexact_key *k = data; 14548c2ecf20Sopenharmony_ci u32 a = k->type << 24 | k->dir << 16 | k->family; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci return jhash_3words(a, k->if_id, net_hash_mix(read_pnet(&k->net)), 14578c2ecf20Sopenharmony_ci seed); 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic u32 xfrm_pol_bin_obj(const void *data, u32 len, u32 seed) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci const struct xfrm_pol_inexact_bin *b = data; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci return xfrm_pol_bin_key(&b->k, 0, seed); 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_cistatic int xfrm_pol_bin_cmp(struct rhashtable_compare_arg *arg, 14688c2ecf20Sopenharmony_ci const void *ptr) 14698c2ecf20Sopenharmony_ci{ 14708c2ecf20Sopenharmony_ci const struct xfrm_pol_inexact_key *key = arg->key; 14718c2ecf20Sopenharmony_ci const struct xfrm_pol_inexact_bin *b = ptr; 14728c2ecf20Sopenharmony_ci int ret; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (!net_eq(read_pnet(&b->k.net), read_pnet(&key->net))) 14758c2ecf20Sopenharmony_ci return -1; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci ret = b->k.dir ^ key->dir; 14788c2ecf20Sopenharmony_ci if (ret) 14798c2ecf20Sopenharmony_ci return ret; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci ret = b->k.type ^ key->type; 14828c2ecf20Sopenharmony_ci if (ret) 14838c2ecf20Sopenharmony_ci return ret; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ret = b->k.family ^ key->family; 14868c2ecf20Sopenharmony_ci if (ret) 14878c2ecf20Sopenharmony_ci return ret; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return b->k.if_id ^ key->if_id; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic const struct rhashtable_params xfrm_pol_inexact_params = { 14938c2ecf20Sopenharmony_ci .head_offset = offsetof(struct xfrm_pol_inexact_bin, head), 14948c2ecf20Sopenharmony_ci .hashfn = xfrm_pol_bin_key, 14958c2ecf20Sopenharmony_ci .obj_hashfn = xfrm_pol_bin_obj, 14968c2ecf20Sopenharmony_ci .obj_cmpfn = xfrm_pol_bin_cmp, 14978c2ecf20Sopenharmony_ci .automatic_shrinking = true, 14988c2ecf20Sopenharmony_ci}; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cistatic void xfrm_policy_insert_inexact_list(struct hlist_head *chain, 15018c2ecf20Sopenharmony_ci struct xfrm_policy *policy) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *delpol = NULL; 15048c2ecf20Sopenharmony_ci struct hlist_node *newpos = NULL; 15058c2ecf20Sopenharmony_ci int i = 0; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst_inexact_list) { 15088c2ecf20Sopenharmony_ci if (pol->type == policy->type && 15098c2ecf20Sopenharmony_ci pol->if_id == policy->if_id && 15108c2ecf20Sopenharmony_ci !selector_cmp(&pol->selector, &policy->selector) && 15118c2ecf20Sopenharmony_ci xfrm_policy_mark_match(&policy->mark, pol) && 15128c2ecf20Sopenharmony_ci xfrm_sec_ctx_match(pol->security, policy->security) && 15138c2ecf20Sopenharmony_ci !WARN_ON(delpol)) { 15148c2ecf20Sopenharmony_ci delpol = pol; 15158c2ecf20Sopenharmony_ci if (policy->priority > pol->priority) 15168c2ecf20Sopenharmony_ci continue; 15178c2ecf20Sopenharmony_ci } else if (policy->priority >= pol->priority) { 15188c2ecf20Sopenharmony_ci newpos = &pol->bydst_inexact_list; 15198c2ecf20Sopenharmony_ci continue; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci if (delpol) 15228c2ecf20Sopenharmony_ci break; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (newpos) 15268c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos); 15278c2ecf20Sopenharmony_ci else 15288c2ecf20Sopenharmony_ci hlist_add_head_rcu(&policy->bydst_inexact_list, chain); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst_inexact_list) { 15318c2ecf20Sopenharmony_ci pol->pos = i; 15328c2ecf20Sopenharmony_ci i++; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic struct xfrm_policy *xfrm_policy_insert_list(struct hlist_head *chain, 15378c2ecf20Sopenharmony_ci struct xfrm_policy *policy, 15388c2ecf20Sopenharmony_ci bool excl) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *newpos = NULL, *delpol = NULL; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst) { 15438c2ecf20Sopenharmony_ci if (pol->type == policy->type && 15448c2ecf20Sopenharmony_ci pol->if_id == policy->if_id && 15458c2ecf20Sopenharmony_ci !selector_cmp(&pol->selector, &policy->selector) && 15468c2ecf20Sopenharmony_ci xfrm_policy_mark_match(&policy->mark, pol) && 15478c2ecf20Sopenharmony_ci xfrm_sec_ctx_match(pol->security, policy->security) && 15488c2ecf20Sopenharmony_ci !WARN_ON(delpol)) { 15498c2ecf20Sopenharmony_ci if (excl) 15508c2ecf20Sopenharmony_ci return ERR_PTR(-EEXIST); 15518c2ecf20Sopenharmony_ci delpol = pol; 15528c2ecf20Sopenharmony_ci if (policy->priority > pol->priority) 15538c2ecf20Sopenharmony_ci continue; 15548c2ecf20Sopenharmony_ci } else if (policy->priority >= pol->priority) { 15558c2ecf20Sopenharmony_ci newpos = pol; 15568c2ecf20Sopenharmony_ci continue; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci if (delpol) 15598c2ecf20Sopenharmony_ci break; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (newpos) 15638c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&policy->bydst, &newpos->bydst); 15648c2ecf20Sopenharmony_ci else 15658c2ecf20Sopenharmony_ci hlist_add_head_rcu(&policy->bydst, chain); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci return delpol; 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ciint xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci struct net *net = xp_net(policy); 15738c2ecf20Sopenharmony_ci struct xfrm_policy *delpol; 15748c2ecf20Sopenharmony_ci struct hlist_head *chain; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 15778c2ecf20Sopenharmony_ci chain = policy_hash_bysel(net, &policy->selector, policy->family, dir); 15788c2ecf20Sopenharmony_ci if (chain) 15798c2ecf20Sopenharmony_ci delpol = xfrm_policy_insert_list(chain, policy, excl); 15808c2ecf20Sopenharmony_ci else 15818c2ecf20Sopenharmony_ci delpol = xfrm_policy_inexact_insert(policy, dir, excl); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (IS_ERR(delpol)) { 15848c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 15858c2ecf20Sopenharmony_ci return PTR_ERR(delpol); 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci __xfrm_policy_link(policy, dir); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* After previous checking, family can either be AF_INET or AF_INET6 */ 15918c2ecf20Sopenharmony_ci if (policy->family == AF_INET) 15928c2ecf20Sopenharmony_ci rt_genid_bump_ipv4(net); 15938c2ecf20Sopenharmony_ci else 15948c2ecf20Sopenharmony_ci rt_genid_bump_ipv6(net); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (delpol) { 15978c2ecf20Sopenharmony_ci xfrm_policy_requeue(delpol, policy); 15988c2ecf20Sopenharmony_ci __xfrm_policy_unlink(delpol, dir); 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir, policy->index); 16018c2ecf20Sopenharmony_ci hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index)); 16028c2ecf20Sopenharmony_ci policy->curlft.add_time = ktime_get_real_seconds(); 16038c2ecf20Sopenharmony_ci policy->curlft.use_time = 0; 16048c2ecf20Sopenharmony_ci if (!mod_timer(&policy->timer, jiffies + HZ)) 16058c2ecf20Sopenharmony_ci xfrm_pol_hold(policy); 16068c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (delpol) 16098c2ecf20Sopenharmony_ci xfrm_policy_kill(delpol); 16108c2ecf20Sopenharmony_ci else if (xfrm_bydst_should_resize(net, dir, NULL)) 16118c2ecf20Sopenharmony_ci schedule_work(&net->xfrm.policy_hash_work); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci return 0; 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_insert); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic struct xfrm_policy * 16188c2ecf20Sopenharmony_ci__xfrm_policy_bysel_ctx(struct hlist_head *chain, const struct xfrm_mark *mark, 16198c2ecf20Sopenharmony_ci u32 if_id, u8 type, int dir, struct xfrm_selector *sel, 16208c2ecf20Sopenharmony_ci struct xfrm_sec_ctx *ctx) 16218c2ecf20Sopenharmony_ci{ 16228c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (!chain) 16258c2ecf20Sopenharmony_ci return NULL; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst) { 16288c2ecf20Sopenharmony_ci if (pol->type == type && 16298c2ecf20Sopenharmony_ci pol->if_id == if_id && 16308c2ecf20Sopenharmony_ci xfrm_policy_mark_match(mark, pol) && 16318c2ecf20Sopenharmony_ci !selector_cmp(sel, &pol->selector) && 16328c2ecf20Sopenharmony_ci xfrm_sec_ctx_match(ctx, pol->security)) 16338c2ecf20Sopenharmony_ci return pol; 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci return NULL; 16378c2ecf20Sopenharmony_ci} 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_cistruct xfrm_policy * 16408c2ecf20Sopenharmony_cixfrm_policy_bysel_ctx(struct net *net, const struct xfrm_mark *mark, u32 if_id, 16418c2ecf20Sopenharmony_ci u8 type, int dir, struct xfrm_selector *sel, 16428c2ecf20Sopenharmony_ci struct xfrm_sec_ctx *ctx, int delete, int *err) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin = NULL; 16458c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *ret = NULL; 16468c2ecf20Sopenharmony_ci struct hlist_head *chain; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci *err = 0; 16498c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 16508c2ecf20Sopenharmony_ci chain = policy_hash_bysel(net, sel, sel->family, dir); 16518c2ecf20Sopenharmony_ci if (!chain) { 16528c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_candidates cand; 16538c2ecf20Sopenharmony_ci int i; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci bin = xfrm_policy_inexact_lookup(net, type, 16568c2ecf20Sopenharmony_ci sel->family, dir, if_id); 16578c2ecf20Sopenharmony_ci if (!bin) { 16588c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 16598c2ecf20Sopenharmony_ci return NULL; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (!xfrm_policy_find_inexact_candidates(&cand, bin, 16638c2ecf20Sopenharmony_ci &sel->saddr, 16648c2ecf20Sopenharmony_ci &sel->daddr)) { 16658c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 16668c2ecf20Sopenharmony_ci return NULL; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci pol = NULL; 16708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cand.res); i++) { 16718c2ecf20Sopenharmony_ci struct xfrm_policy *tmp; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci tmp = __xfrm_policy_bysel_ctx(cand.res[i], mark, 16748c2ecf20Sopenharmony_ci if_id, type, dir, 16758c2ecf20Sopenharmony_ci sel, ctx); 16768c2ecf20Sopenharmony_ci if (!tmp) 16778c2ecf20Sopenharmony_ci continue; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if (!pol || tmp->pos < pol->pos) 16808c2ecf20Sopenharmony_ci pol = tmp; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci } else { 16838c2ecf20Sopenharmony_ci pol = __xfrm_policy_bysel_ctx(chain, mark, if_id, type, dir, 16848c2ecf20Sopenharmony_ci sel, ctx); 16858c2ecf20Sopenharmony_ci } 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci if (pol) { 16888c2ecf20Sopenharmony_ci xfrm_pol_hold(pol); 16898c2ecf20Sopenharmony_ci if (delete) { 16908c2ecf20Sopenharmony_ci *err = security_xfrm_policy_delete(pol->security); 16918c2ecf20Sopenharmony_ci if (*err) { 16928c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 16938c2ecf20Sopenharmony_ci return pol; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci __xfrm_policy_unlink(pol, dir); 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci ret = pol; 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (ret && delete) 17028c2ecf20Sopenharmony_ci xfrm_policy_kill(ret); 17038c2ecf20Sopenharmony_ci if (bin && delete) 17048c2ecf20Sopenharmony_ci xfrm_policy_inexact_prune_bin(bin); 17058c2ecf20Sopenharmony_ci return ret; 17068c2ecf20Sopenharmony_ci} 17078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_bysel_ctx); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_cistruct xfrm_policy * 17108c2ecf20Sopenharmony_cixfrm_policy_byid(struct net *net, const struct xfrm_mark *mark, u32 if_id, 17118c2ecf20Sopenharmony_ci u8 type, int dir, u32 id, int delete, int *err) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *ret; 17148c2ecf20Sopenharmony_ci struct hlist_head *chain; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci *err = -ENOENT; 17178c2ecf20Sopenharmony_ci if (xfrm_policy_id2dir(id) != dir) 17188c2ecf20Sopenharmony_ci return NULL; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci *err = 0; 17218c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 17228c2ecf20Sopenharmony_ci chain = net->xfrm.policy_byidx + idx_hash(net, id); 17238c2ecf20Sopenharmony_ci ret = NULL; 17248c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, byidx) { 17258c2ecf20Sopenharmony_ci if (pol->type == type && pol->index == id && 17268c2ecf20Sopenharmony_ci pol->if_id == if_id && xfrm_policy_mark_match(mark, pol)) { 17278c2ecf20Sopenharmony_ci xfrm_pol_hold(pol); 17288c2ecf20Sopenharmony_ci if (delete) { 17298c2ecf20Sopenharmony_ci *err = security_xfrm_policy_delete( 17308c2ecf20Sopenharmony_ci pol->security); 17318c2ecf20Sopenharmony_ci if (*err) { 17328c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 17338c2ecf20Sopenharmony_ci return pol; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci __xfrm_policy_unlink(pol, dir); 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci ret = pol; 17388c2ecf20Sopenharmony_ci break; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (ret && delete) 17448c2ecf20Sopenharmony_ci xfrm_policy_kill(ret); 17458c2ecf20Sopenharmony_ci return ret; 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_byid); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_NETWORK_XFRM 17508c2ecf20Sopenharmony_cistatic inline int 17518c2ecf20Sopenharmony_cixfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) 17528c2ecf20Sopenharmony_ci{ 17538c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 17548c2ecf20Sopenharmony_ci int err = 0; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { 17578c2ecf20Sopenharmony_ci if (pol->walk.dead || 17588c2ecf20Sopenharmony_ci xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX || 17598c2ecf20Sopenharmony_ci pol->type != type) 17608c2ecf20Sopenharmony_ci continue; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci err = security_xfrm_policy_delete(pol->security); 17638c2ecf20Sopenharmony_ci if (err) { 17648c2ecf20Sopenharmony_ci xfrm_audit_policy_delete(pol, 0, task_valid); 17658c2ecf20Sopenharmony_ci return err; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci return err; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci#else 17718c2ecf20Sopenharmony_cistatic inline int 17728c2ecf20Sopenharmony_cixfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci return 0; 17758c2ecf20Sopenharmony_ci} 17768c2ecf20Sopenharmony_ci#endif 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ciint xfrm_policy_flush(struct net *net, u8 type, bool task_valid) 17798c2ecf20Sopenharmony_ci{ 17808c2ecf20Sopenharmony_ci int dir, err = 0, cnt = 0; 17818c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci err = xfrm_policy_flush_secctx_check(net, type, task_valid); 17868c2ecf20Sopenharmony_ci if (err) 17878c2ecf20Sopenharmony_ci goto out; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ciagain: 17908c2ecf20Sopenharmony_ci list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { 17918c2ecf20Sopenharmony_ci dir = xfrm_policy_id2dir(pol->index); 17928c2ecf20Sopenharmony_ci if (pol->walk.dead || 17938c2ecf20Sopenharmony_ci dir >= XFRM_POLICY_MAX || 17948c2ecf20Sopenharmony_ci pol->type != type) 17958c2ecf20Sopenharmony_ci continue; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci __xfrm_policy_unlink(pol, dir); 17988c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 17998c2ecf20Sopenharmony_ci cnt++; 18008c2ecf20Sopenharmony_ci xfrm_audit_policy_delete(pol, 1, task_valid); 18018c2ecf20Sopenharmony_ci xfrm_policy_kill(pol); 18028c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 18038c2ecf20Sopenharmony_ci goto again; 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci if (cnt) 18068c2ecf20Sopenharmony_ci __xfrm_policy_inexact_flush(net); 18078c2ecf20Sopenharmony_ci else 18088c2ecf20Sopenharmony_ci err = -ESRCH; 18098c2ecf20Sopenharmony_ciout: 18108c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 18118c2ecf20Sopenharmony_ci return err; 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_flush); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ciint xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, 18168c2ecf20Sopenharmony_ci int (*func)(struct xfrm_policy *, int, int, void*), 18178c2ecf20Sopenharmony_ci void *data) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 18208c2ecf20Sopenharmony_ci struct xfrm_policy_walk_entry *x; 18218c2ecf20Sopenharmony_ci int error = 0; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (walk->type >= XFRM_POLICY_TYPE_MAX && 18248c2ecf20Sopenharmony_ci walk->type != XFRM_POLICY_TYPE_ANY) 18258c2ecf20Sopenharmony_ci return -EINVAL; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (list_empty(&walk->walk.all) && walk->seq != 0) 18288c2ecf20Sopenharmony_ci return 0; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 18318c2ecf20Sopenharmony_ci if (list_empty(&walk->walk.all)) 18328c2ecf20Sopenharmony_ci x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all); 18338c2ecf20Sopenharmony_ci else 18348c2ecf20Sopenharmony_ci x = list_first_entry(&walk->walk.all, 18358c2ecf20Sopenharmony_ci struct xfrm_policy_walk_entry, all); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci list_for_each_entry_from(x, &net->xfrm.policy_all, all) { 18388c2ecf20Sopenharmony_ci if (x->dead) 18398c2ecf20Sopenharmony_ci continue; 18408c2ecf20Sopenharmony_ci pol = container_of(x, struct xfrm_policy, walk); 18418c2ecf20Sopenharmony_ci if (walk->type != XFRM_POLICY_TYPE_ANY && 18428c2ecf20Sopenharmony_ci walk->type != pol->type) 18438c2ecf20Sopenharmony_ci continue; 18448c2ecf20Sopenharmony_ci error = func(pol, xfrm_policy_id2dir(pol->index), 18458c2ecf20Sopenharmony_ci walk->seq, data); 18468c2ecf20Sopenharmony_ci if (error) { 18478c2ecf20Sopenharmony_ci list_move_tail(&walk->walk.all, &x->all); 18488c2ecf20Sopenharmony_ci goto out; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci walk->seq++; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci if (walk->seq == 0) { 18538c2ecf20Sopenharmony_ci error = -ENOENT; 18548c2ecf20Sopenharmony_ci goto out; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci list_del_init(&walk->walk.all); 18578c2ecf20Sopenharmony_ciout: 18588c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 18598c2ecf20Sopenharmony_ci return error; 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_walk); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_civoid xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&walk->walk.all); 18668c2ecf20Sopenharmony_ci walk->walk.dead = 1; 18678c2ecf20Sopenharmony_ci walk->type = type; 18688c2ecf20Sopenharmony_ci walk->seq = 0; 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_walk_init); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_civoid xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci if (list_empty(&walk->walk.all)) 18758c2ecf20Sopenharmony_ci return; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME where is net? */ 18788c2ecf20Sopenharmony_ci list_del(&walk->walk.all); 18798c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_walk_done); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci/* 18848c2ecf20Sopenharmony_ci * Find policy to apply to this flow. 18858c2ecf20Sopenharmony_ci * 18868c2ecf20Sopenharmony_ci * Returns 0 if policy found, else an -errno. 18878c2ecf20Sopenharmony_ci */ 18888c2ecf20Sopenharmony_cistatic int xfrm_policy_match(const struct xfrm_policy *pol, 18898c2ecf20Sopenharmony_ci const struct flowi *fl, 18908c2ecf20Sopenharmony_ci u8 type, u16 family, int dir, u32 if_id) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci const struct xfrm_selector *sel = &pol->selector; 18938c2ecf20Sopenharmony_ci int ret = -ESRCH; 18948c2ecf20Sopenharmony_ci bool match; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (pol->family != family || 18978c2ecf20Sopenharmony_ci pol->if_id != if_id || 18988c2ecf20Sopenharmony_ci (fl->flowi_mark & pol->mark.m) != pol->mark.v || 18998c2ecf20Sopenharmony_ci pol->type != type) 19008c2ecf20Sopenharmony_ci return ret; 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci match = xfrm_selector_match(sel, fl, family); 19038c2ecf20Sopenharmony_ci if (match) 19048c2ecf20Sopenharmony_ci ret = security_xfrm_policy_lookup(pol->security, fl->flowi_secid, 19058c2ecf20Sopenharmony_ci dir); 19068c2ecf20Sopenharmony_ci return ret; 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_node * 19108c2ecf20Sopenharmony_cixfrm_policy_lookup_inexact_addr(const struct rb_root *r, 19118c2ecf20Sopenharmony_ci seqcount_spinlock_t *count, 19128c2ecf20Sopenharmony_ci const xfrm_address_t *addr, u16 family) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci const struct rb_node *parent; 19158c2ecf20Sopenharmony_ci int seq; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ciagain: 19188c2ecf20Sopenharmony_ci seq = read_seqcount_begin(count); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci parent = rcu_dereference_raw(r->rb_node); 19218c2ecf20Sopenharmony_ci while (parent) { 19228c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *node; 19238c2ecf20Sopenharmony_ci int delta; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci node = rb_entry(parent, struct xfrm_pol_inexact_node, node); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci delta = xfrm_policy_addr_delta(addr, &node->addr, 19288c2ecf20Sopenharmony_ci node->prefixlen, family); 19298c2ecf20Sopenharmony_ci if (delta < 0) { 19308c2ecf20Sopenharmony_ci parent = rcu_dereference_raw(parent->rb_left); 19318c2ecf20Sopenharmony_ci continue; 19328c2ecf20Sopenharmony_ci } else if (delta > 0) { 19338c2ecf20Sopenharmony_ci parent = rcu_dereference_raw(parent->rb_right); 19348c2ecf20Sopenharmony_ci continue; 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci return node; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci if (read_seqcount_retry(count, seq)) 19418c2ecf20Sopenharmony_ci goto again; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci return NULL; 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic bool 19478c2ecf20Sopenharmony_cixfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand, 19488c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *b, 19498c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 19508c2ecf20Sopenharmony_ci const xfrm_address_t *daddr) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_node *n; 19538c2ecf20Sopenharmony_ci u16 family; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (!b) 19568c2ecf20Sopenharmony_ci return false; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci family = b->k.family; 19598c2ecf20Sopenharmony_ci memset(cand, 0, sizeof(*cand)); 19608c2ecf20Sopenharmony_ci cand->res[XFRM_POL_CAND_ANY] = &b->hhead; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci n = xfrm_policy_lookup_inexact_addr(&b->root_d, &b->count, daddr, 19638c2ecf20Sopenharmony_ci family); 19648c2ecf20Sopenharmony_ci if (n) { 19658c2ecf20Sopenharmony_ci cand->res[XFRM_POL_CAND_DADDR] = &n->hhead; 19668c2ecf20Sopenharmony_ci n = xfrm_policy_lookup_inexact_addr(&n->root, &b->count, saddr, 19678c2ecf20Sopenharmony_ci family); 19688c2ecf20Sopenharmony_ci if (n) 19698c2ecf20Sopenharmony_ci cand->res[XFRM_POL_CAND_BOTH] = &n->hhead; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci n = xfrm_policy_lookup_inexact_addr(&b->root_s, &b->count, saddr, 19738c2ecf20Sopenharmony_ci family); 19748c2ecf20Sopenharmony_ci if (n) 19758c2ecf20Sopenharmony_ci cand->res[XFRM_POL_CAND_SADDR] = &n->hhead; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci return true; 19788c2ecf20Sopenharmony_ci} 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_bin * 19818c2ecf20Sopenharmony_cixfrm_policy_inexact_lookup_rcu(struct net *net, u8 type, u16 family, 19828c2ecf20Sopenharmony_ci u8 dir, u32 if_id) 19838c2ecf20Sopenharmony_ci{ 19848c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_key k = { 19858c2ecf20Sopenharmony_ci .family = family, 19868c2ecf20Sopenharmony_ci .type = type, 19878c2ecf20Sopenharmony_ci .dir = dir, 19888c2ecf20Sopenharmony_ci .if_id = if_id, 19898c2ecf20Sopenharmony_ci }; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci write_pnet(&k.net, net); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci return rhashtable_lookup(&xfrm_policy_inexact_table, &k, 19948c2ecf20Sopenharmony_ci xfrm_pol_inexact_params); 19958c2ecf20Sopenharmony_ci} 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_cistatic struct xfrm_pol_inexact_bin * 19988c2ecf20Sopenharmony_cixfrm_policy_inexact_lookup(struct net *net, u8 type, u16 family, 19998c2ecf20Sopenharmony_ci u8 dir, u32 if_id) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci rcu_read_lock(); 20068c2ecf20Sopenharmony_ci bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id); 20078c2ecf20Sopenharmony_ci rcu_read_unlock(); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci return bin; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistatic struct xfrm_policy * 20138c2ecf20Sopenharmony_ci__xfrm_policy_eval_candidates(struct hlist_head *chain, 20148c2ecf20Sopenharmony_ci struct xfrm_policy *prefer, 20158c2ecf20Sopenharmony_ci const struct flowi *fl, 20168c2ecf20Sopenharmony_ci u8 type, u16 family, int dir, u32 if_id) 20178c2ecf20Sopenharmony_ci{ 20188c2ecf20Sopenharmony_ci u32 priority = prefer ? prefer->priority : ~0u; 20198c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci if (!chain) 20228c2ecf20Sopenharmony_ci return NULL; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(pol, chain, bydst) { 20258c2ecf20Sopenharmony_ci int err; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci if (pol->priority > priority) 20288c2ecf20Sopenharmony_ci break; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci err = xfrm_policy_match(pol, fl, type, family, dir, if_id); 20318c2ecf20Sopenharmony_ci if (err) { 20328c2ecf20Sopenharmony_ci if (err != -ESRCH) 20338c2ecf20Sopenharmony_ci return ERR_PTR(err); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci continue; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci if (prefer) { 20398c2ecf20Sopenharmony_ci /* matches. Is it older than *prefer? */ 20408c2ecf20Sopenharmony_ci if (pol->priority == priority && 20418c2ecf20Sopenharmony_ci prefer->pos < pol->pos) 20428c2ecf20Sopenharmony_ci return prefer; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci return pol; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci return NULL; 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_cistatic struct xfrm_policy * 20528c2ecf20Sopenharmony_cixfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand, 20538c2ecf20Sopenharmony_ci struct xfrm_policy *prefer, 20548c2ecf20Sopenharmony_ci const struct flowi *fl, 20558c2ecf20Sopenharmony_ci u8 type, u16 family, int dir, u32 if_id) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci struct xfrm_policy *tmp; 20588c2ecf20Sopenharmony_ci int i; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cand->res); i++) { 20618c2ecf20Sopenharmony_ci tmp = __xfrm_policy_eval_candidates(cand->res[i], 20628c2ecf20Sopenharmony_ci prefer, 20638c2ecf20Sopenharmony_ci fl, type, family, dir, 20648c2ecf20Sopenharmony_ci if_id); 20658c2ecf20Sopenharmony_ci if (!tmp) 20668c2ecf20Sopenharmony_ci continue; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) 20698c2ecf20Sopenharmony_ci return tmp; 20708c2ecf20Sopenharmony_ci prefer = tmp; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci return prefer; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, 20778c2ecf20Sopenharmony_ci const struct flowi *fl, 20788c2ecf20Sopenharmony_ci u16 family, u8 dir, 20798c2ecf20Sopenharmony_ci u32 if_id) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_candidates cand; 20828c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, *saddr; 20838c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *bin; 20848c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *ret; 20858c2ecf20Sopenharmony_ci struct hlist_head *chain; 20868c2ecf20Sopenharmony_ci unsigned int sequence; 20878c2ecf20Sopenharmony_ci int err; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci daddr = xfrm_flowi_daddr(fl, family); 20908c2ecf20Sopenharmony_ci saddr = xfrm_flowi_saddr(fl, family); 20918c2ecf20Sopenharmony_ci if (unlikely(!daddr || !saddr)) 20928c2ecf20Sopenharmony_ci return NULL; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci rcu_read_lock(); 20958c2ecf20Sopenharmony_ci retry: 20968c2ecf20Sopenharmony_ci do { 20978c2ecf20Sopenharmony_ci sequence = read_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation); 20988c2ecf20Sopenharmony_ci chain = policy_hash_direct(net, daddr, saddr, family, dir); 20998c2ecf20Sopenharmony_ci } while (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence)); 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci ret = NULL; 21028c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(pol, chain, bydst) { 21038c2ecf20Sopenharmony_ci err = xfrm_policy_match(pol, fl, type, family, dir, if_id); 21048c2ecf20Sopenharmony_ci if (err) { 21058c2ecf20Sopenharmony_ci if (err == -ESRCH) 21068c2ecf20Sopenharmony_ci continue; 21078c2ecf20Sopenharmony_ci else { 21088c2ecf20Sopenharmony_ci ret = ERR_PTR(err); 21098c2ecf20Sopenharmony_ci goto fail; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci } else { 21128c2ecf20Sopenharmony_ci ret = pol; 21138c2ecf20Sopenharmony_ci break; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id); 21178c2ecf20Sopenharmony_ci if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr, 21188c2ecf20Sopenharmony_ci daddr)) 21198c2ecf20Sopenharmony_ci goto skip_inexact; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci pol = xfrm_policy_eval_candidates(&cand, ret, fl, type, 21228c2ecf20Sopenharmony_ci family, dir, if_id); 21238c2ecf20Sopenharmony_ci if (pol) { 21248c2ecf20Sopenharmony_ci ret = pol; 21258c2ecf20Sopenharmony_ci if (IS_ERR(pol)) 21268c2ecf20Sopenharmony_ci goto fail; 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ciskip_inexact: 21308c2ecf20Sopenharmony_ci if (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence)) 21318c2ecf20Sopenharmony_ci goto retry; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci if (ret && !xfrm_pol_hold_rcu(ret)) 21348c2ecf20Sopenharmony_ci goto retry; 21358c2ecf20Sopenharmony_cifail: 21368c2ecf20Sopenharmony_ci rcu_read_unlock(); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci return ret; 21398c2ecf20Sopenharmony_ci} 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_cistatic struct xfrm_policy *xfrm_policy_lookup(struct net *net, 21428c2ecf20Sopenharmony_ci const struct flowi *fl, 21438c2ecf20Sopenharmony_ci u16 family, u8 dir, u32 if_id) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 21468c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, 21498c2ecf20Sopenharmony_ci dir, if_id); 21508c2ecf20Sopenharmony_ci if (pol != NULL) 21518c2ecf20Sopenharmony_ci return pol; 21528c2ecf20Sopenharmony_ci#endif 21538c2ecf20Sopenharmony_ci return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, 21548c2ecf20Sopenharmony_ci dir, if_id); 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cistatic struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, 21588c2ecf20Sopenharmony_ci const struct flowi *fl, 21598c2ecf20Sopenharmony_ci u16 family, u32 if_id) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci rcu_read_lock(); 21648c2ecf20Sopenharmony_ci again: 21658c2ecf20Sopenharmony_ci pol = rcu_dereference(sk->sk_policy[dir]); 21668c2ecf20Sopenharmony_ci if (pol != NULL) { 21678c2ecf20Sopenharmony_ci bool match; 21688c2ecf20Sopenharmony_ci int err = 0; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci if (pol->family != family) { 21718c2ecf20Sopenharmony_ci pol = NULL; 21728c2ecf20Sopenharmony_ci goto out; 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci match = xfrm_selector_match(&pol->selector, fl, family); 21768c2ecf20Sopenharmony_ci if (match) { 21778c2ecf20Sopenharmony_ci if ((sk->sk_mark & pol->mark.m) != pol->mark.v || 21788c2ecf20Sopenharmony_ci pol->if_id != if_id) { 21798c2ecf20Sopenharmony_ci pol = NULL; 21808c2ecf20Sopenharmony_ci goto out; 21818c2ecf20Sopenharmony_ci } 21828c2ecf20Sopenharmony_ci err = security_xfrm_policy_lookup(pol->security, 21838c2ecf20Sopenharmony_ci fl->flowi_secid, 21848c2ecf20Sopenharmony_ci dir); 21858c2ecf20Sopenharmony_ci if (!err) { 21868c2ecf20Sopenharmony_ci if (!xfrm_pol_hold_rcu(pol)) 21878c2ecf20Sopenharmony_ci goto again; 21888c2ecf20Sopenharmony_ci } else if (err == -ESRCH) { 21898c2ecf20Sopenharmony_ci pol = NULL; 21908c2ecf20Sopenharmony_ci } else { 21918c2ecf20Sopenharmony_ci pol = ERR_PTR(err); 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci } else 21948c2ecf20Sopenharmony_ci pol = NULL; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ciout: 21978c2ecf20Sopenharmony_ci rcu_read_unlock(); 21988c2ecf20Sopenharmony_ci return pol; 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_cistatic void __xfrm_policy_link(struct xfrm_policy *pol, int dir) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci list_add(&pol->walk.all, &net->xfrm.policy_all); 22068c2ecf20Sopenharmony_ci net->xfrm.policy_count[dir]++; 22078c2ecf20Sopenharmony_ci xfrm_pol_hold(pol); 22088c2ecf20Sopenharmony_ci} 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_cistatic struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, 22118c2ecf20Sopenharmony_ci int dir) 22128c2ecf20Sopenharmony_ci{ 22138c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (list_empty(&pol->walk.all)) 22168c2ecf20Sopenharmony_ci return NULL; 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci /* Socket policies are not hashed. */ 22198c2ecf20Sopenharmony_ci if (!hlist_unhashed(&pol->bydst)) { 22208c2ecf20Sopenharmony_ci hlist_del_rcu(&pol->bydst); 22218c2ecf20Sopenharmony_ci hlist_del_init(&pol->bydst_inexact_list); 22228c2ecf20Sopenharmony_ci hlist_del(&pol->byidx); 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci list_del_init(&pol->walk.all); 22268c2ecf20Sopenharmony_ci net->xfrm.policy_count[dir]--; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci return pol; 22298c2ecf20Sopenharmony_ci} 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_cistatic void xfrm_sk_policy_link(struct xfrm_policy *pol, int dir) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci __xfrm_policy_link(pol, XFRM_POLICY_MAX + dir); 22348c2ecf20Sopenharmony_ci} 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic void xfrm_sk_policy_unlink(struct xfrm_policy *pol, int dir) 22378c2ecf20Sopenharmony_ci{ 22388c2ecf20Sopenharmony_ci __xfrm_policy_unlink(pol, XFRM_POLICY_MAX + dir); 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ciint xfrm_policy_delete(struct xfrm_policy *pol, int dir) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 22468c2ecf20Sopenharmony_ci pol = __xfrm_policy_unlink(pol, dir); 22478c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 22488c2ecf20Sopenharmony_ci if (pol) { 22498c2ecf20Sopenharmony_ci xfrm_policy_kill(pol); 22508c2ecf20Sopenharmony_ci return 0; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci return -ENOENT; 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_delete); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ciint xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) 22578c2ecf20Sopenharmony_ci{ 22588c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 22598c2ecf20Sopenharmony_ci struct xfrm_policy *old_pol; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 22628c2ecf20Sopenharmony_ci if (pol && pol->type != XFRM_POLICY_TYPE_MAIN) 22638c2ecf20Sopenharmony_ci return -EINVAL; 22648c2ecf20Sopenharmony_ci#endif 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 22678c2ecf20Sopenharmony_ci old_pol = rcu_dereference_protected(sk->sk_policy[dir], 22688c2ecf20Sopenharmony_ci lockdep_is_held(&net->xfrm.xfrm_policy_lock)); 22698c2ecf20Sopenharmony_ci if (pol) { 22708c2ecf20Sopenharmony_ci pol->curlft.add_time = ktime_get_real_seconds(); 22718c2ecf20Sopenharmony_ci pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0); 22728c2ecf20Sopenharmony_ci xfrm_sk_policy_link(pol, dir); 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci rcu_assign_pointer(sk->sk_policy[dir], pol); 22758c2ecf20Sopenharmony_ci if (old_pol) { 22768c2ecf20Sopenharmony_ci if (pol) 22778c2ecf20Sopenharmony_ci xfrm_policy_requeue(old_pol, pol); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci /* Unlinking succeeds always. This is the only function 22808c2ecf20Sopenharmony_ci * allowed to delete or replace socket policy. 22818c2ecf20Sopenharmony_ci */ 22828c2ecf20Sopenharmony_ci xfrm_sk_policy_unlink(old_pol, dir); 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci if (old_pol) { 22878c2ecf20Sopenharmony_ci xfrm_policy_kill(old_pol); 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci return 0; 22908c2ecf20Sopenharmony_ci} 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_cistatic struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) 22938c2ecf20Sopenharmony_ci{ 22948c2ecf20Sopenharmony_ci struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC); 22958c2ecf20Sopenharmony_ci struct net *net = xp_net(old); 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci if (newp) { 22988c2ecf20Sopenharmony_ci newp->selector = old->selector; 22998c2ecf20Sopenharmony_ci if (security_xfrm_policy_clone(old->security, 23008c2ecf20Sopenharmony_ci &newp->security)) { 23018c2ecf20Sopenharmony_ci kfree(newp); 23028c2ecf20Sopenharmony_ci return NULL; /* ENOMEM */ 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci newp->lft = old->lft; 23058c2ecf20Sopenharmony_ci newp->curlft = old->curlft; 23068c2ecf20Sopenharmony_ci newp->mark = old->mark; 23078c2ecf20Sopenharmony_ci newp->if_id = old->if_id; 23088c2ecf20Sopenharmony_ci newp->action = old->action; 23098c2ecf20Sopenharmony_ci newp->flags = old->flags; 23108c2ecf20Sopenharmony_ci newp->xfrm_nr = old->xfrm_nr; 23118c2ecf20Sopenharmony_ci newp->index = old->index; 23128c2ecf20Sopenharmony_ci newp->type = old->type; 23138c2ecf20Sopenharmony_ci newp->family = old->family; 23148c2ecf20Sopenharmony_ci memcpy(newp->xfrm_vec, old->xfrm_vec, 23158c2ecf20Sopenharmony_ci newp->xfrm_nr*sizeof(struct xfrm_tmpl)); 23168c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 23178c2ecf20Sopenharmony_ci xfrm_sk_policy_link(newp, dir); 23188c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 23198c2ecf20Sopenharmony_ci xfrm_pol_put(newp); 23208c2ecf20Sopenharmony_ci } 23218c2ecf20Sopenharmony_ci return newp; 23228c2ecf20Sopenharmony_ci} 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ciint __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) 23258c2ecf20Sopenharmony_ci{ 23268c2ecf20Sopenharmony_ci const struct xfrm_policy *p; 23278c2ecf20Sopenharmony_ci struct xfrm_policy *np; 23288c2ecf20Sopenharmony_ci int i, ret = 0; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci rcu_read_lock(); 23318c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 23328c2ecf20Sopenharmony_ci p = rcu_dereference(osk->sk_policy[i]); 23338c2ecf20Sopenharmony_ci if (p) { 23348c2ecf20Sopenharmony_ci np = clone_policy(p, i); 23358c2ecf20Sopenharmony_ci if (unlikely(!np)) { 23368c2ecf20Sopenharmony_ci ret = -ENOMEM; 23378c2ecf20Sopenharmony_ci break; 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci rcu_assign_pointer(sk->sk_policy[i], np); 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci } 23428c2ecf20Sopenharmony_ci rcu_read_unlock(); 23438c2ecf20Sopenharmony_ci return ret; 23448c2ecf20Sopenharmony_ci} 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic int 23478c2ecf20Sopenharmony_cixfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local, 23488c2ecf20Sopenharmony_ci xfrm_address_t *remote, unsigned short family, u32 mark) 23498c2ecf20Sopenharmony_ci{ 23508c2ecf20Sopenharmony_ci int err; 23518c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci if (unlikely(afinfo == NULL)) 23548c2ecf20Sopenharmony_ci return -EINVAL; 23558c2ecf20Sopenharmony_ci err = afinfo->get_saddr(net, oif, local, remote, mark); 23568c2ecf20Sopenharmony_ci rcu_read_unlock(); 23578c2ecf20Sopenharmony_ci return err; 23588c2ecf20Sopenharmony_ci} 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci/* Resolve list of templates for the flow, given policy. */ 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_cistatic int 23638c2ecf20Sopenharmony_cixfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, 23648c2ecf20Sopenharmony_ci struct xfrm_state **xfrm, unsigned short family) 23658c2ecf20Sopenharmony_ci{ 23668c2ecf20Sopenharmony_ci struct net *net = xp_net(policy); 23678c2ecf20Sopenharmony_ci int nx; 23688c2ecf20Sopenharmony_ci int i, error; 23698c2ecf20Sopenharmony_ci xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family); 23708c2ecf20Sopenharmony_ci xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family); 23718c2ecf20Sopenharmony_ci xfrm_address_t tmp; 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci for (nx = 0, i = 0; i < policy->xfrm_nr; i++) { 23748c2ecf20Sopenharmony_ci struct xfrm_state *x; 23758c2ecf20Sopenharmony_ci xfrm_address_t *remote = daddr; 23768c2ecf20Sopenharmony_ci xfrm_address_t *local = saddr; 23778c2ecf20Sopenharmony_ci struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i]; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if (tmpl->mode == XFRM_MODE_TUNNEL || 23808c2ecf20Sopenharmony_ci tmpl->mode == XFRM_MODE_BEET) { 23818c2ecf20Sopenharmony_ci remote = &tmpl->id.daddr; 23828c2ecf20Sopenharmony_ci local = &tmpl->saddr; 23838c2ecf20Sopenharmony_ci if (xfrm_addr_any(local, tmpl->encap_family)) { 23848c2ecf20Sopenharmony_ci error = xfrm_get_saddr(net, fl->flowi_oif, 23858c2ecf20Sopenharmony_ci &tmp, remote, 23868c2ecf20Sopenharmony_ci tmpl->encap_family, 0); 23878c2ecf20Sopenharmony_ci if (error) 23888c2ecf20Sopenharmony_ci goto fail; 23898c2ecf20Sopenharmony_ci local = &tmp; 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, 23948c2ecf20Sopenharmony_ci family, policy->if_id); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci if (x && x->km.state == XFRM_STATE_VALID) { 23978c2ecf20Sopenharmony_ci xfrm[nx++] = x; 23988c2ecf20Sopenharmony_ci daddr = remote; 23998c2ecf20Sopenharmony_ci saddr = local; 24008c2ecf20Sopenharmony_ci continue; 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci if (x) { 24038c2ecf20Sopenharmony_ci error = (x->km.state == XFRM_STATE_ERROR ? 24048c2ecf20Sopenharmony_ci -EINVAL : -EAGAIN); 24058c2ecf20Sopenharmony_ci xfrm_state_put(x); 24068c2ecf20Sopenharmony_ci } else if (error == -ESRCH) { 24078c2ecf20Sopenharmony_ci error = -EAGAIN; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci if (!tmpl->optional) 24118c2ecf20Sopenharmony_ci goto fail; 24128c2ecf20Sopenharmony_ci } 24138c2ecf20Sopenharmony_ci return nx; 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cifail: 24168c2ecf20Sopenharmony_ci for (nx--; nx >= 0; nx--) 24178c2ecf20Sopenharmony_ci xfrm_state_put(xfrm[nx]); 24188c2ecf20Sopenharmony_ci return error; 24198c2ecf20Sopenharmony_ci} 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_cistatic int 24228c2ecf20Sopenharmony_cixfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, const struct flowi *fl, 24238c2ecf20Sopenharmony_ci struct xfrm_state **xfrm, unsigned short family) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci struct xfrm_state *tp[XFRM_MAX_DEPTH]; 24268c2ecf20Sopenharmony_ci struct xfrm_state **tpp = (npols > 1) ? tp : xfrm; 24278c2ecf20Sopenharmony_ci int cnx = 0; 24288c2ecf20Sopenharmony_ci int error; 24298c2ecf20Sopenharmony_ci int ret; 24308c2ecf20Sopenharmony_ci int i; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci for (i = 0; i < npols; i++) { 24338c2ecf20Sopenharmony_ci if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) { 24348c2ecf20Sopenharmony_ci error = -ENOBUFS; 24358c2ecf20Sopenharmony_ci goto fail; 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family); 24398c2ecf20Sopenharmony_ci if (ret < 0) { 24408c2ecf20Sopenharmony_ci error = ret; 24418c2ecf20Sopenharmony_ci goto fail; 24428c2ecf20Sopenharmony_ci } else 24438c2ecf20Sopenharmony_ci cnx += ret; 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci /* found states are sorted for outbound processing */ 24478c2ecf20Sopenharmony_ci if (npols > 1) 24488c2ecf20Sopenharmony_ci xfrm_state_sort(xfrm, tpp, cnx, family); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci return cnx; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci fail: 24538c2ecf20Sopenharmony_ci for (cnx--; cnx >= 0; cnx--) 24548c2ecf20Sopenharmony_ci xfrm_state_put(tpp[cnx]); 24558c2ecf20Sopenharmony_ci return error; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_cistatic int xfrm_get_tos(const struct flowi *fl, int family) 24608c2ecf20Sopenharmony_ci{ 24618c2ecf20Sopenharmony_ci if (family == AF_INET) 24628c2ecf20Sopenharmony_ci return IPTOS_RT_MASK & fl->u.ip4.flowi4_tos; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci return 0; 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); 24708c2ecf20Sopenharmony_ci struct dst_ops *dst_ops; 24718c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci if (!afinfo) 24748c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci switch (family) { 24778c2ecf20Sopenharmony_ci case AF_INET: 24788c2ecf20Sopenharmony_ci dst_ops = &net->xfrm.xfrm4_dst_ops; 24798c2ecf20Sopenharmony_ci break; 24808c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 24818c2ecf20Sopenharmony_ci case AF_INET6: 24828c2ecf20Sopenharmony_ci dst_ops = &net->xfrm.xfrm6_dst_ops; 24838c2ecf20Sopenharmony_ci break; 24848c2ecf20Sopenharmony_ci#endif 24858c2ecf20Sopenharmony_ci default: 24868c2ecf20Sopenharmony_ci BUG(); 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci xdst = dst_alloc(dst_ops, NULL, 1, DST_OBSOLETE_NONE, 0); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci if (likely(xdst)) { 24918c2ecf20Sopenharmony_ci struct dst_entry *dst = &xdst->u.dst; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst)); 24948c2ecf20Sopenharmony_ci } else 24958c2ecf20Sopenharmony_ci xdst = ERR_PTR(-ENOBUFS); 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci rcu_read_unlock(); 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci return xdst; 25008c2ecf20Sopenharmony_ci} 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_cistatic void xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, 25038c2ecf20Sopenharmony_ci int nfheader_len) 25048c2ecf20Sopenharmony_ci{ 25058c2ecf20Sopenharmony_ci if (dst->ops->family == AF_INET6) { 25068c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 25078c2ecf20Sopenharmony_ci path->path_cookie = rt6_get_cookie(rt); 25088c2ecf20Sopenharmony_ci path->u.rt6.rt6i_nfheader_len = nfheader_len; 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci} 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_cistatic inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, 25138c2ecf20Sopenharmony_ci const struct flowi *fl) 25148c2ecf20Sopenharmony_ci{ 25158c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo = 25168c2ecf20Sopenharmony_ci xfrm_policy_get_afinfo(xdst->u.dst.ops->family); 25178c2ecf20Sopenharmony_ci int err; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci if (!afinfo) 25208c2ecf20Sopenharmony_ci return -EINVAL; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci err = afinfo->fill_dst(xdst, dev, fl); 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci rcu_read_unlock(); 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci return err; 25278c2ecf20Sopenharmony_ci} 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci/* Allocate chain of dst_entry's, attach known xfrm's, calculate 25318c2ecf20Sopenharmony_ci * all the metrics... Shortly, bundle a bundle. 25328c2ecf20Sopenharmony_ci */ 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_cistatic struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, 25358c2ecf20Sopenharmony_ci struct xfrm_state **xfrm, 25368c2ecf20Sopenharmony_ci struct xfrm_dst **bundle, 25378c2ecf20Sopenharmony_ci int nx, 25388c2ecf20Sopenharmony_ci const struct flowi *fl, 25398c2ecf20Sopenharmony_ci struct dst_entry *dst) 25408c2ecf20Sopenharmony_ci{ 25418c2ecf20Sopenharmony_ci const struct xfrm_state_afinfo *afinfo; 25428c2ecf20Sopenharmony_ci const struct xfrm_mode *inner_mode; 25438c2ecf20Sopenharmony_ci struct net *net = xp_net(policy); 25448c2ecf20Sopenharmony_ci unsigned long now = jiffies; 25458c2ecf20Sopenharmony_ci struct net_device *dev; 25468c2ecf20Sopenharmony_ci struct xfrm_dst *xdst_prev = NULL; 25478c2ecf20Sopenharmony_ci struct xfrm_dst *xdst0 = NULL; 25488c2ecf20Sopenharmony_ci int i = 0; 25498c2ecf20Sopenharmony_ci int err; 25508c2ecf20Sopenharmony_ci int header_len = 0; 25518c2ecf20Sopenharmony_ci int nfheader_len = 0; 25528c2ecf20Sopenharmony_ci int trailer_len = 0; 25538c2ecf20Sopenharmony_ci int tos; 25548c2ecf20Sopenharmony_ci int family = policy->selector.family; 25558c2ecf20Sopenharmony_ci xfrm_address_t saddr, daddr; 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci xfrm_flowi_addr_get(fl, &saddr, &daddr, family); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci tos = xfrm_get_tos(fl, family); 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci dst_hold(dst); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci for (; i < nx; i++) { 25648c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = xfrm_alloc_dst(net, family); 25658c2ecf20Sopenharmony_ci struct dst_entry *dst1 = &xdst->u.dst; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci err = PTR_ERR(xdst); 25688c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) { 25698c2ecf20Sopenharmony_ci dst_release(dst); 25708c2ecf20Sopenharmony_ci goto put_states; 25718c2ecf20Sopenharmony_ci } 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci bundle[i] = xdst; 25748c2ecf20Sopenharmony_ci if (!xdst_prev) 25758c2ecf20Sopenharmony_ci xdst0 = xdst; 25768c2ecf20Sopenharmony_ci else 25778c2ecf20Sopenharmony_ci /* Ref count is taken during xfrm_alloc_dst() 25788c2ecf20Sopenharmony_ci * No need to do dst_clone() on dst1 25798c2ecf20Sopenharmony_ci */ 25808c2ecf20Sopenharmony_ci xfrm_dst_set_child(xdst_prev, &xdst->u.dst); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci if (xfrm[i]->sel.family == AF_UNSPEC) { 25838c2ecf20Sopenharmony_ci inner_mode = xfrm_ip2inner_mode(xfrm[i], 25848c2ecf20Sopenharmony_ci xfrm_af2proto(family)); 25858c2ecf20Sopenharmony_ci if (!inner_mode) { 25868c2ecf20Sopenharmony_ci err = -EAFNOSUPPORT; 25878c2ecf20Sopenharmony_ci dst_release(dst); 25888c2ecf20Sopenharmony_ci goto put_states; 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci } else 25918c2ecf20Sopenharmony_ci inner_mode = &xfrm[i]->inner_mode; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci xdst->route = dst; 25948c2ecf20Sopenharmony_ci dst_copy_metrics(dst1, dst); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { 25978c2ecf20Sopenharmony_ci __u32 mark = 0; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci if (xfrm[i]->props.smark.v || xfrm[i]->props.smark.m) 26008c2ecf20Sopenharmony_ci mark = xfrm_smark_get(fl->flowi_mark, xfrm[i]); 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci family = xfrm[i]->props.family; 26038c2ecf20Sopenharmony_ci dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif, 26048c2ecf20Sopenharmony_ci &saddr, &daddr, family, mark); 26058c2ecf20Sopenharmony_ci err = PTR_ERR(dst); 26068c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 26078c2ecf20Sopenharmony_ci goto put_states; 26088c2ecf20Sopenharmony_ci } else 26098c2ecf20Sopenharmony_ci dst_hold(dst); 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci dst1->xfrm = xfrm[i]; 26128c2ecf20Sopenharmony_ci xdst->xfrm_genid = xfrm[i]->genid; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci dst1->obsolete = DST_OBSOLETE_FORCE_CHK; 26158c2ecf20Sopenharmony_ci dst1->lastuse = now; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci dst1->input = dst_discard; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci rcu_read_lock(); 26208c2ecf20Sopenharmony_ci afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); 26218c2ecf20Sopenharmony_ci if (likely(afinfo)) 26228c2ecf20Sopenharmony_ci dst1->output = afinfo->output; 26238c2ecf20Sopenharmony_ci else 26248c2ecf20Sopenharmony_ci dst1->output = dst_discard_out; 26258c2ecf20Sopenharmony_ci rcu_read_unlock(); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci xdst_prev = xdst; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci header_len += xfrm[i]->props.header_len; 26308c2ecf20Sopenharmony_ci if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT) 26318c2ecf20Sopenharmony_ci nfheader_len += xfrm[i]->props.header_len; 26328c2ecf20Sopenharmony_ci trailer_len += xfrm[i]->props.trailer_len; 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci xfrm_dst_set_child(xdst_prev, dst); 26368c2ecf20Sopenharmony_ci xdst0->path = dst; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci err = -ENODEV; 26398c2ecf20Sopenharmony_ci dev = dst->dev; 26408c2ecf20Sopenharmony_ci if (!dev) 26418c2ecf20Sopenharmony_ci goto free_dst; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci xfrm_init_path(xdst0, dst, nfheader_len); 26448c2ecf20Sopenharmony_ci xfrm_init_pmtu(bundle, nx); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst; 26478c2ecf20Sopenharmony_ci xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) { 26488c2ecf20Sopenharmony_ci err = xfrm_fill_dst(xdst_prev, dev, fl); 26498c2ecf20Sopenharmony_ci if (err) 26508c2ecf20Sopenharmony_ci goto free_dst; 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci xdst_prev->u.dst.header_len = header_len; 26538c2ecf20Sopenharmony_ci xdst_prev->u.dst.trailer_len = trailer_len; 26548c2ecf20Sopenharmony_ci header_len -= xdst_prev->u.dst.xfrm->props.header_len; 26558c2ecf20Sopenharmony_ci trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len; 26568c2ecf20Sopenharmony_ci } 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci return &xdst0->u.dst; 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ciput_states: 26618c2ecf20Sopenharmony_ci for (; i < nx; i++) 26628c2ecf20Sopenharmony_ci xfrm_state_put(xfrm[i]); 26638c2ecf20Sopenharmony_cifree_dst: 26648c2ecf20Sopenharmony_ci if (xdst0) 26658c2ecf20Sopenharmony_ci dst_release_immediate(&xdst0->u.dst); 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci return ERR_PTR(err); 26688c2ecf20Sopenharmony_ci} 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_cistatic int xfrm_expand_policies(const struct flowi *fl, u16 family, 26718c2ecf20Sopenharmony_ci struct xfrm_policy **pols, 26728c2ecf20Sopenharmony_ci int *num_pols, int *num_xfrms) 26738c2ecf20Sopenharmony_ci{ 26748c2ecf20Sopenharmony_ci int i; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci if (*num_pols == 0 || !pols[0]) { 26778c2ecf20Sopenharmony_ci *num_pols = 0; 26788c2ecf20Sopenharmony_ci *num_xfrms = 0; 26798c2ecf20Sopenharmony_ci return 0; 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci if (IS_ERR(pols[0])) { 26828c2ecf20Sopenharmony_ci *num_pols = 0; 26838c2ecf20Sopenharmony_ci return PTR_ERR(pols[0]); 26848c2ecf20Sopenharmony_ci } 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci *num_xfrms = pols[0]->xfrm_nr; 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 26898c2ecf20Sopenharmony_ci if (pols[0] && pols[0]->action == XFRM_POLICY_ALLOW && 26908c2ecf20Sopenharmony_ci pols[0]->type != XFRM_POLICY_TYPE_MAIN) { 26918c2ecf20Sopenharmony_ci pols[1] = xfrm_policy_lookup_bytype(xp_net(pols[0]), 26928c2ecf20Sopenharmony_ci XFRM_POLICY_TYPE_MAIN, 26938c2ecf20Sopenharmony_ci fl, family, 26948c2ecf20Sopenharmony_ci XFRM_POLICY_OUT, 26958c2ecf20Sopenharmony_ci pols[0]->if_id); 26968c2ecf20Sopenharmony_ci if (pols[1]) { 26978c2ecf20Sopenharmony_ci if (IS_ERR(pols[1])) { 26988c2ecf20Sopenharmony_ci xfrm_pols_put(pols, *num_pols); 26998c2ecf20Sopenharmony_ci *num_pols = 0; 27008c2ecf20Sopenharmony_ci return PTR_ERR(pols[1]); 27018c2ecf20Sopenharmony_ci } 27028c2ecf20Sopenharmony_ci (*num_pols)++; 27038c2ecf20Sopenharmony_ci (*num_xfrms) += pols[1]->xfrm_nr; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci } 27068c2ecf20Sopenharmony_ci#endif 27078c2ecf20Sopenharmony_ci for (i = 0; i < *num_pols; i++) { 27088c2ecf20Sopenharmony_ci if (pols[i]->action != XFRM_POLICY_ALLOW) { 27098c2ecf20Sopenharmony_ci *num_xfrms = -1; 27108c2ecf20Sopenharmony_ci break; 27118c2ecf20Sopenharmony_ci } 27128c2ecf20Sopenharmony_ci } 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci return 0; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci} 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_cistatic struct xfrm_dst * 27198c2ecf20Sopenharmony_cixfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, 27208c2ecf20Sopenharmony_ci const struct flowi *fl, u16 family, 27218c2ecf20Sopenharmony_ci struct dst_entry *dst_orig) 27228c2ecf20Sopenharmony_ci{ 27238c2ecf20Sopenharmony_ci struct net *net = xp_net(pols[0]); 27248c2ecf20Sopenharmony_ci struct xfrm_state *xfrm[XFRM_MAX_DEPTH]; 27258c2ecf20Sopenharmony_ci struct xfrm_dst *bundle[XFRM_MAX_DEPTH]; 27268c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 27278c2ecf20Sopenharmony_ci struct dst_entry *dst; 27288c2ecf20Sopenharmony_ci int err; 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci /* Try to instantiate a bundle */ 27318c2ecf20Sopenharmony_ci err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family); 27328c2ecf20Sopenharmony_ci if (err <= 0) { 27338c2ecf20Sopenharmony_ci if (err == 0) 27348c2ecf20Sopenharmony_ci return NULL; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci if (err != -EAGAIN) 27378c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); 27388c2ecf20Sopenharmony_ci return ERR_PTR(err); 27398c2ecf20Sopenharmony_ci } 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci dst = xfrm_bundle_create(pols[0], xfrm, bundle, err, fl, dst_orig); 27428c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 27438c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR); 27448c2ecf20Sopenharmony_ci return ERR_CAST(dst); 27458c2ecf20Sopenharmony_ci } 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci xdst = (struct xfrm_dst *)dst; 27488c2ecf20Sopenharmony_ci xdst->num_xfrms = err; 27498c2ecf20Sopenharmony_ci xdst->num_pols = num_pols; 27508c2ecf20Sopenharmony_ci memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols); 27518c2ecf20Sopenharmony_ci xdst->policy_genid = atomic_read(&pols[0]->genid); 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci return xdst; 27548c2ecf20Sopenharmony_ci} 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_cistatic void xfrm_policy_queue_process(struct timer_list *t) 27578c2ecf20Sopenharmony_ci{ 27588c2ecf20Sopenharmony_ci struct sk_buff *skb; 27598c2ecf20Sopenharmony_ci struct sock *sk; 27608c2ecf20Sopenharmony_ci struct dst_entry *dst; 27618c2ecf20Sopenharmony_ci struct xfrm_policy *pol = from_timer(pol, t, polq.hold_timer); 27628c2ecf20Sopenharmony_ci struct net *net = xp_net(pol); 27638c2ecf20Sopenharmony_ci struct xfrm_policy_queue *pq = &pol->polq; 27648c2ecf20Sopenharmony_ci struct flowi fl; 27658c2ecf20Sopenharmony_ci struct sk_buff_head list; 27668c2ecf20Sopenharmony_ci __u32 skb_mark; 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci spin_lock(&pq->hold_queue.lock); 27698c2ecf20Sopenharmony_ci skb = skb_peek(&pq->hold_queue); 27708c2ecf20Sopenharmony_ci if (!skb) { 27718c2ecf20Sopenharmony_ci spin_unlock(&pq->hold_queue.lock); 27728c2ecf20Sopenharmony_ci goto out; 27738c2ecf20Sopenharmony_ci } 27748c2ecf20Sopenharmony_ci dst = skb_dst(skb); 27758c2ecf20Sopenharmony_ci sk = skb->sk; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* Fixup the mark to support VTI. */ 27788c2ecf20Sopenharmony_ci skb_mark = skb->mark; 27798c2ecf20Sopenharmony_ci skb->mark = pol->mark.v; 27808c2ecf20Sopenharmony_ci xfrm_decode_session(skb, &fl, dst->ops->family); 27818c2ecf20Sopenharmony_ci skb->mark = skb_mark; 27828c2ecf20Sopenharmony_ci spin_unlock(&pq->hold_queue.lock); 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci dst_hold(xfrm_dst_path(dst)); 27858c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, xfrm_dst_path(dst), &fl, sk, XFRM_LOOKUP_QUEUE); 27868c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 27878c2ecf20Sopenharmony_ci goto purge_queue; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci if (dst->flags & DST_XFRM_QUEUE) { 27908c2ecf20Sopenharmony_ci dst_release(dst); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci if (pq->timeout >= XFRM_QUEUE_TMO_MAX) 27938c2ecf20Sopenharmony_ci goto purge_queue; 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_ci pq->timeout = pq->timeout << 1; 27968c2ecf20Sopenharmony_ci if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout)) 27978c2ecf20Sopenharmony_ci xfrm_pol_hold(pol); 27988c2ecf20Sopenharmony_ci goto out; 27998c2ecf20Sopenharmony_ci } 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci dst_release(dst); 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci __skb_queue_head_init(&list); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci spin_lock(&pq->hold_queue.lock); 28068c2ecf20Sopenharmony_ci pq->timeout = 0; 28078c2ecf20Sopenharmony_ci skb_queue_splice_init(&pq->hold_queue, &list); 28088c2ecf20Sopenharmony_ci spin_unlock(&pq->hold_queue.lock); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci while (!skb_queue_empty(&list)) { 28118c2ecf20Sopenharmony_ci skb = __skb_dequeue(&list); 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci /* Fixup the mark to support VTI. */ 28148c2ecf20Sopenharmony_ci skb_mark = skb->mark; 28158c2ecf20Sopenharmony_ci skb->mark = pol->mark.v; 28168c2ecf20Sopenharmony_ci xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family); 28178c2ecf20Sopenharmony_ci skb->mark = skb_mark; 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci dst_hold(xfrm_dst_path(skb_dst(skb))); 28208c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, xfrm_dst_path(skb_dst(skb)), &fl, skb->sk, 0); 28218c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 28228c2ecf20Sopenharmony_ci kfree_skb(skb); 28238c2ecf20Sopenharmony_ci continue; 28248c2ecf20Sopenharmony_ci } 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci nf_reset_ct(skb); 28278c2ecf20Sopenharmony_ci skb_dst_drop(skb); 28288c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci dst_output(net, skb->sk, skb); 28318c2ecf20Sopenharmony_ci } 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ciout: 28348c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 28358c2ecf20Sopenharmony_ci return; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_cipurge_queue: 28388c2ecf20Sopenharmony_ci pq->timeout = 0; 28398c2ecf20Sopenharmony_ci skb_queue_purge(&pq->hold_queue); 28408c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 28418c2ecf20Sopenharmony_ci} 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_cistatic int xdst_queue_output(struct net *net, struct sock *sk, struct sk_buff *skb) 28448c2ecf20Sopenharmony_ci{ 28458c2ecf20Sopenharmony_ci unsigned long sched_next; 28468c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 28478c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = (struct xfrm_dst *) dst; 28488c2ecf20Sopenharmony_ci struct xfrm_policy *pol = xdst->pols[0]; 28498c2ecf20Sopenharmony_ci struct xfrm_policy_queue *pq = &pol->polq; 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci if (unlikely(skb_fclone_busy(sk, skb))) { 28528c2ecf20Sopenharmony_ci kfree_skb(skb); 28538c2ecf20Sopenharmony_ci return 0; 28548c2ecf20Sopenharmony_ci } 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) { 28578c2ecf20Sopenharmony_ci kfree_skb(skb); 28588c2ecf20Sopenharmony_ci return -EAGAIN; 28598c2ecf20Sopenharmony_ci } 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci skb_dst_force(skb); 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci spin_lock_bh(&pq->hold_queue.lock); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci if (!pq->timeout) 28668c2ecf20Sopenharmony_ci pq->timeout = XFRM_QUEUE_TMO_MIN; 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci sched_next = jiffies + pq->timeout; 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci if (del_timer(&pq->hold_timer)) { 28718c2ecf20Sopenharmony_ci if (time_before(pq->hold_timer.expires, sched_next)) 28728c2ecf20Sopenharmony_ci sched_next = pq->hold_timer.expires; 28738c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 28748c2ecf20Sopenharmony_ci } 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci __skb_queue_tail(&pq->hold_queue, skb); 28778c2ecf20Sopenharmony_ci if (!mod_timer(&pq->hold_timer, sched_next)) 28788c2ecf20Sopenharmony_ci xfrm_pol_hold(pol); 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci spin_unlock_bh(&pq->hold_queue.lock); 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci return 0; 28838c2ecf20Sopenharmony_ci} 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_cistatic struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net, 28868c2ecf20Sopenharmony_ci struct xfrm_flo *xflo, 28878c2ecf20Sopenharmony_ci const struct flowi *fl, 28888c2ecf20Sopenharmony_ci int num_xfrms, 28898c2ecf20Sopenharmony_ci u16 family) 28908c2ecf20Sopenharmony_ci{ 28918c2ecf20Sopenharmony_ci int err; 28928c2ecf20Sopenharmony_ci struct net_device *dev; 28938c2ecf20Sopenharmony_ci struct dst_entry *dst; 28948c2ecf20Sopenharmony_ci struct dst_entry *dst1; 28958c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci xdst = xfrm_alloc_dst(net, family); 28988c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) 28998c2ecf20Sopenharmony_ci return xdst; 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci if (!(xflo->flags & XFRM_LOOKUP_QUEUE) || 29028c2ecf20Sopenharmony_ci net->xfrm.sysctl_larval_drop || 29038c2ecf20Sopenharmony_ci num_xfrms <= 0) 29048c2ecf20Sopenharmony_ci return xdst; 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci dst = xflo->dst_orig; 29078c2ecf20Sopenharmony_ci dst1 = &xdst->u.dst; 29088c2ecf20Sopenharmony_ci dst_hold(dst); 29098c2ecf20Sopenharmony_ci xdst->route = dst; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci dst_copy_metrics(dst1, dst); 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci dst1->obsolete = DST_OBSOLETE_FORCE_CHK; 29148c2ecf20Sopenharmony_ci dst1->flags |= DST_XFRM_QUEUE; 29158c2ecf20Sopenharmony_ci dst1->lastuse = jiffies; 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci dst1->input = dst_discard; 29188c2ecf20Sopenharmony_ci dst1->output = xdst_queue_output; 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci dst_hold(dst); 29218c2ecf20Sopenharmony_ci xfrm_dst_set_child(xdst, dst); 29228c2ecf20Sopenharmony_ci xdst->path = dst; 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci xfrm_init_path((struct xfrm_dst *)dst1, dst, 0); 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci err = -ENODEV; 29278c2ecf20Sopenharmony_ci dev = dst->dev; 29288c2ecf20Sopenharmony_ci if (!dev) 29298c2ecf20Sopenharmony_ci goto free_dst; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci err = xfrm_fill_dst(xdst, dev, fl); 29328c2ecf20Sopenharmony_ci if (err) 29338c2ecf20Sopenharmony_ci goto free_dst; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ciout: 29368c2ecf20Sopenharmony_ci return xdst; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_cifree_dst: 29398c2ecf20Sopenharmony_ci dst_release(dst1); 29408c2ecf20Sopenharmony_ci xdst = ERR_PTR(err); 29418c2ecf20Sopenharmony_ci goto out; 29428c2ecf20Sopenharmony_ci} 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_cistatic struct xfrm_dst *xfrm_bundle_lookup(struct net *net, 29458c2ecf20Sopenharmony_ci const struct flowi *fl, 29468c2ecf20Sopenharmony_ci u16 family, u8 dir, 29478c2ecf20Sopenharmony_ci struct xfrm_flo *xflo, u32 if_id) 29488c2ecf20Sopenharmony_ci{ 29498c2ecf20Sopenharmony_ci struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; 29508c2ecf20Sopenharmony_ci int num_pols = 0, num_xfrms = 0, err; 29518c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci /* Resolve policies to use if we couldn't get them from 29548c2ecf20Sopenharmony_ci * previous cache entry */ 29558c2ecf20Sopenharmony_ci num_pols = 1; 29568c2ecf20Sopenharmony_ci pols[0] = xfrm_policy_lookup(net, fl, family, dir, if_id); 29578c2ecf20Sopenharmony_ci err = xfrm_expand_policies(fl, family, pols, 29588c2ecf20Sopenharmony_ci &num_pols, &num_xfrms); 29598c2ecf20Sopenharmony_ci if (err < 0) 29608c2ecf20Sopenharmony_ci goto inc_error; 29618c2ecf20Sopenharmony_ci if (num_pols == 0) 29628c2ecf20Sopenharmony_ci return NULL; 29638c2ecf20Sopenharmony_ci if (num_xfrms <= 0) 29648c2ecf20Sopenharmony_ci goto make_dummy_bundle; 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, 29678c2ecf20Sopenharmony_ci xflo->dst_orig); 29688c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) { 29698c2ecf20Sopenharmony_ci err = PTR_ERR(xdst); 29708c2ecf20Sopenharmony_ci if (err == -EREMOTE) { 29718c2ecf20Sopenharmony_ci xfrm_pols_put(pols, num_pols); 29728c2ecf20Sopenharmony_ci return NULL; 29738c2ecf20Sopenharmony_ci } 29748c2ecf20Sopenharmony_ci 29758c2ecf20Sopenharmony_ci if (err != -EAGAIN) 29768c2ecf20Sopenharmony_ci goto error; 29778c2ecf20Sopenharmony_ci goto make_dummy_bundle; 29788c2ecf20Sopenharmony_ci } else if (xdst == NULL) { 29798c2ecf20Sopenharmony_ci num_xfrms = 0; 29808c2ecf20Sopenharmony_ci goto make_dummy_bundle; 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci return xdst; 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_cimake_dummy_bundle: 29868c2ecf20Sopenharmony_ci /* We found policies, but there's no bundles to instantiate: 29878c2ecf20Sopenharmony_ci * either because the policy blocks, has no transformations or 29888c2ecf20Sopenharmony_ci * we could not build template (no xfrm_states).*/ 29898c2ecf20Sopenharmony_ci xdst = xfrm_create_dummy_bundle(net, xflo, fl, num_xfrms, family); 29908c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) { 29918c2ecf20Sopenharmony_ci xfrm_pols_put(pols, num_pols); 29928c2ecf20Sopenharmony_ci return ERR_CAST(xdst); 29938c2ecf20Sopenharmony_ci } 29948c2ecf20Sopenharmony_ci xdst->num_pols = num_pols; 29958c2ecf20Sopenharmony_ci xdst->num_xfrms = num_xfrms; 29968c2ecf20Sopenharmony_ci memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols); 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci return xdst; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ciinc_error: 30018c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); 30028c2ecf20Sopenharmony_cierror: 30038c2ecf20Sopenharmony_ci xfrm_pols_put(pols, num_pols); 30048c2ecf20Sopenharmony_ci return ERR_PTR(err); 30058c2ecf20Sopenharmony_ci} 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_cistatic struct dst_entry *make_blackhole(struct net *net, u16 family, 30088c2ecf20Sopenharmony_ci struct dst_entry *dst_orig) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); 30118c2ecf20Sopenharmony_ci struct dst_entry *ret; 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci if (!afinfo) { 30148c2ecf20Sopenharmony_ci dst_release(dst_orig); 30158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 30168c2ecf20Sopenharmony_ci } else { 30178c2ecf20Sopenharmony_ci ret = afinfo->blackhole_route(net, dst_orig); 30188c2ecf20Sopenharmony_ci } 30198c2ecf20Sopenharmony_ci rcu_read_unlock(); 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci return ret; 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci/* Finds/creates a bundle for given flow and if_id 30258c2ecf20Sopenharmony_ci * 30268c2ecf20Sopenharmony_ci * At the moment we eat a raw IP route. Mostly to speed up lookups 30278c2ecf20Sopenharmony_ci * on interfaces with disabled IPsec. 30288c2ecf20Sopenharmony_ci * 30298c2ecf20Sopenharmony_ci * xfrm_lookup uses an if_id of 0 by default, and is provided for 30308c2ecf20Sopenharmony_ci * compatibility 30318c2ecf20Sopenharmony_ci */ 30328c2ecf20Sopenharmony_cistruct dst_entry *xfrm_lookup_with_ifid(struct net *net, 30338c2ecf20Sopenharmony_ci struct dst_entry *dst_orig, 30348c2ecf20Sopenharmony_ci const struct flowi *fl, 30358c2ecf20Sopenharmony_ci const struct sock *sk, 30368c2ecf20Sopenharmony_ci int flags, u32 if_id) 30378c2ecf20Sopenharmony_ci{ 30388c2ecf20Sopenharmony_ci struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; 30398c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 30408c2ecf20Sopenharmony_ci struct dst_entry *dst, *route; 30418c2ecf20Sopenharmony_ci u16 family = dst_orig->ops->family; 30428c2ecf20Sopenharmony_ci u8 dir = XFRM_POLICY_OUT; 30438c2ecf20Sopenharmony_ci int i, err, num_pols, num_xfrms = 0, drop_pols = 0; 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci dst = NULL; 30468c2ecf20Sopenharmony_ci xdst = NULL; 30478c2ecf20Sopenharmony_ci route = NULL; 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci sk = sk_const_to_full_sk(sk); 30508c2ecf20Sopenharmony_ci if (sk && sk->sk_policy[XFRM_POLICY_OUT]) { 30518c2ecf20Sopenharmony_ci num_pols = 1; 30528c2ecf20Sopenharmony_ci pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family, 30538c2ecf20Sopenharmony_ci if_id); 30548c2ecf20Sopenharmony_ci err = xfrm_expand_policies(fl, family, pols, 30558c2ecf20Sopenharmony_ci &num_pols, &num_xfrms); 30568c2ecf20Sopenharmony_ci if (err < 0) 30578c2ecf20Sopenharmony_ci goto dropdst; 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci if (num_pols) { 30608c2ecf20Sopenharmony_ci if (num_xfrms <= 0) { 30618c2ecf20Sopenharmony_ci drop_pols = num_pols; 30628c2ecf20Sopenharmony_ci goto no_transform; 30638c2ecf20Sopenharmony_ci } 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci xdst = xfrm_resolve_and_create_bundle( 30668c2ecf20Sopenharmony_ci pols, num_pols, fl, 30678c2ecf20Sopenharmony_ci family, dst_orig); 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) { 30708c2ecf20Sopenharmony_ci xfrm_pols_put(pols, num_pols); 30718c2ecf20Sopenharmony_ci err = PTR_ERR(xdst); 30728c2ecf20Sopenharmony_ci if (err == -EREMOTE) 30738c2ecf20Sopenharmony_ci goto nopol; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci goto dropdst; 30768c2ecf20Sopenharmony_ci } else if (xdst == NULL) { 30778c2ecf20Sopenharmony_ci num_xfrms = 0; 30788c2ecf20Sopenharmony_ci drop_pols = num_pols; 30798c2ecf20Sopenharmony_ci goto no_transform; 30808c2ecf20Sopenharmony_ci } 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci route = xdst->route; 30838c2ecf20Sopenharmony_ci } 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci if (xdst == NULL) { 30878c2ecf20Sopenharmony_ci struct xfrm_flo xflo; 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci xflo.dst_orig = dst_orig; 30908c2ecf20Sopenharmony_ci xflo.flags = flags; 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci /* To accelerate a bit... */ 30938c2ecf20Sopenharmony_ci if (!if_id && ((dst_orig->flags & DST_NOXFRM) || 30948c2ecf20Sopenharmony_ci !net->xfrm.policy_count[XFRM_POLICY_OUT])) 30958c2ecf20Sopenharmony_ci goto nopol; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id); 30988c2ecf20Sopenharmony_ci if (xdst == NULL) 30998c2ecf20Sopenharmony_ci goto nopol; 31008c2ecf20Sopenharmony_ci if (IS_ERR(xdst)) { 31018c2ecf20Sopenharmony_ci err = PTR_ERR(xdst); 31028c2ecf20Sopenharmony_ci goto dropdst; 31038c2ecf20Sopenharmony_ci } 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci num_pols = xdst->num_pols; 31068c2ecf20Sopenharmony_ci num_xfrms = xdst->num_xfrms; 31078c2ecf20Sopenharmony_ci memcpy(pols, xdst->pols, sizeof(struct xfrm_policy *) * num_pols); 31088c2ecf20Sopenharmony_ci route = xdst->route; 31098c2ecf20Sopenharmony_ci } 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci dst = &xdst->u.dst; 31128c2ecf20Sopenharmony_ci if (route == NULL && num_xfrms > 0) { 31138c2ecf20Sopenharmony_ci /* The only case when xfrm_bundle_lookup() returns a 31148c2ecf20Sopenharmony_ci * bundle with null route, is when the template could 31158c2ecf20Sopenharmony_ci * not be resolved. It means policies are there, but 31168c2ecf20Sopenharmony_ci * bundle could not be created, since we don't yet 31178c2ecf20Sopenharmony_ci * have the xfrm_state's. We need to wait for KM to 31188c2ecf20Sopenharmony_ci * negotiate new SA's or bail out with error.*/ 31198c2ecf20Sopenharmony_ci if (net->xfrm.sysctl_larval_drop) { 31208c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); 31218c2ecf20Sopenharmony_ci err = -EREMOTE; 31228c2ecf20Sopenharmony_ci goto error; 31238c2ecf20Sopenharmony_ci } 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci err = -EAGAIN; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); 31288c2ecf20Sopenharmony_ci goto error; 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_cino_transform: 31328c2ecf20Sopenharmony_ci if (num_pols == 0) 31338c2ecf20Sopenharmony_ci goto nopol; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci if ((flags & XFRM_LOOKUP_ICMP) && 31368c2ecf20Sopenharmony_ci !(pols[0]->flags & XFRM_POLICY_ICMP)) { 31378c2ecf20Sopenharmony_ci err = -ENOENT; 31388c2ecf20Sopenharmony_ci goto error; 31398c2ecf20Sopenharmony_ci } 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci for (i = 0; i < num_pols; i++) 31428c2ecf20Sopenharmony_ci pols[i]->curlft.use_time = ktime_get_real_seconds(); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci if (num_xfrms < 0) { 31458c2ecf20Sopenharmony_ci /* Prohibit the flow */ 31468c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK); 31478c2ecf20Sopenharmony_ci err = -EPERM; 31488c2ecf20Sopenharmony_ci goto error; 31498c2ecf20Sopenharmony_ci } else if (num_xfrms > 0) { 31508c2ecf20Sopenharmony_ci /* Flow transformed */ 31518c2ecf20Sopenharmony_ci dst_release(dst_orig); 31528c2ecf20Sopenharmony_ci } else { 31538c2ecf20Sopenharmony_ci /* Flow passes untransformed */ 31548c2ecf20Sopenharmony_ci dst_release(dst); 31558c2ecf20Sopenharmony_ci dst = dst_orig; 31568c2ecf20Sopenharmony_ci } 31578c2ecf20Sopenharmony_ciok: 31588c2ecf20Sopenharmony_ci xfrm_pols_put(pols, drop_pols); 31598c2ecf20Sopenharmony_ci if (dst && dst->xfrm && 31608c2ecf20Sopenharmony_ci dst->xfrm->props.mode == XFRM_MODE_TUNNEL) 31618c2ecf20Sopenharmony_ci dst->flags |= DST_XFRM_TUNNEL; 31628c2ecf20Sopenharmony_ci return dst; 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_cinopol: 31658c2ecf20Sopenharmony_ci if ((!dst_orig->dev || !(dst_orig->dev->flags & IFF_LOOPBACK)) && 31668c2ecf20Sopenharmony_ci net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { 31678c2ecf20Sopenharmony_ci err = -EPERM; 31688c2ecf20Sopenharmony_ci goto error; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci if (!(flags & XFRM_LOOKUP_ICMP)) { 31718c2ecf20Sopenharmony_ci dst = dst_orig; 31728c2ecf20Sopenharmony_ci goto ok; 31738c2ecf20Sopenharmony_ci } 31748c2ecf20Sopenharmony_ci err = -ENOENT; 31758c2ecf20Sopenharmony_cierror: 31768c2ecf20Sopenharmony_ci dst_release(dst); 31778c2ecf20Sopenharmony_cidropdst: 31788c2ecf20Sopenharmony_ci if (!(flags & XFRM_LOOKUP_KEEP_DST_REF)) 31798c2ecf20Sopenharmony_ci dst_release(dst_orig); 31808c2ecf20Sopenharmony_ci xfrm_pols_put(pols, drop_pols); 31818c2ecf20Sopenharmony_ci return ERR_PTR(err); 31828c2ecf20Sopenharmony_ci} 31838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_lookup_with_ifid); 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci/* Main function: finds/creates a bundle for given flow. 31868c2ecf20Sopenharmony_ci * 31878c2ecf20Sopenharmony_ci * At the moment we eat a raw IP route. Mostly to speed up lookups 31888c2ecf20Sopenharmony_ci * on interfaces with disabled IPsec. 31898c2ecf20Sopenharmony_ci */ 31908c2ecf20Sopenharmony_cistruct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, 31918c2ecf20Sopenharmony_ci const struct flowi *fl, const struct sock *sk, 31928c2ecf20Sopenharmony_ci int flags) 31938c2ecf20Sopenharmony_ci{ 31948c2ecf20Sopenharmony_ci return xfrm_lookup_with_ifid(net, dst_orig, fl, sk, flags, 0); 31958c2ecf20Sopenharmony_ci} 31968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_lookup); 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci/* Callers of xfrm_lookup_route() must ensure a call to dst_output(). 31998c2ecf20Sopenharmony_ci * Otherwise we may send out blackholed packets. 32008c2ecf20Sopenharmony_ci */ 32018c2ecf20Sopenharmony_cistruct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig, 32028c2ecf20Sopenharmony_ci const struct flowi *fl, 32038c2ecf20Sopenharmony_ci const struct sock *sk, int flags) 32048c2ecf20Sopenharmony_ci{ 32058c2ecf20Sopenharmony_ci struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk, 32068c2ecf20Sopenharmony_ci flags | XFRM_LOOKUP_QUEUE | 32078c2ecf20Sopenharmony_ci XFRM_LOOKUP_KEEP_DST_REF); 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (PTR_ERR(dst) == -EREMOTE) 32108c2ecf20Sopenharmony_ci return make_blackhole(net, dst_orig->ops->family, dst_orig); 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 32138c2ecf20Sopenharmony_ci dst_release(dst_orig); 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci return dst; 32168c2ecf20Sopenharmony_ci} 32178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_lookup_route); 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_cistatic inline int 32208c2ecf20Sopenharmony_cixfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl) 32218c2ecf20Sopenharmony_ci{ 32228c2ecf20Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 32238c2ecf20Sopenharmony_ci struct xfrm_state *x; 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci if (!sp || idx < 0 || idx >= sp->len) 32268c2ecf20Sopenharmony_ci return 0; 32278c2ecf20Sopenharmony_ci x = sp->xvec[idx]; 32288c2ecf20Sopenharmony_ci if (!x->type->reject) 32298c2ecf20Sopenharmony_ci return 0; 32308c2ecf20Sopenharmony_ci return x->type->reject(x, skb, fl); 32318c2ecf20Sopenharmony_ci} 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci/* When skb is transformed back to its "native" form, we have to 32348c2ecf20Sopenharmony_ci * check policy restrictions. At the moment we make this in maximally 32358c2ecf20Sopenharmony_ci * stupid way. Shame on me. :-) Of course, connected sockets must 32368c2ecf20Sopenharmony_ci * have policy cached at them. 32378c2ecf20Sopenharmony_ci */ 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_cistatic inline int 32408c2ecf20Sopenharmony_cixfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, 32418c2ecf20Sopenharmony_ci unsigned short family, u32 if_id) 32428c2ecf20Sopenharmony_ci{ 32438c2ecf20Sopenharmony_ci if (xfrm_state_kern(x)) 32448c2ecf20Sopenharmony_ci return tmpl->optional && !xfrm_state_addr_cmp(tmpl, x, tmpl->encap_family); 32458c2ecf20Sopenharmony_ci return x->id.proto == tmpl->id.proto && 32468c2ecf20Sopenharmony_ci (x->id.spi == tmpl->id.spi || !tmpl->id.spi) && 32478c2ecf20Sopenharmony_ci (x->props.reqid == tmpl->reqid || !tmpl->reqid) && 32488c2ecf20Sopenharmony_ci x->props.mode == tmpl->mode && 32498c2ecf20Sopenharmony_ci (tmpl->allalgs || (tmpl->aalgos & (1<<x->props.aalgo)) || 32508c2ecf20Sopenharmony_ci !(xfrm_id_proto_match(tmpl->id.proto, IPSEC_PROTO_ANY))) && 32518c2ecf20Sopenharmony_ci !(x->props.mode != XFRM_MODE_TRANSPORT && 32528c2ecf20Sopenharmony_ci xfrm_state_addr_cmp(tmpl, x, family)) && 32538c2ecf20Sopenharmony_ci (if_id == 0 || if_id == x->if_id); 32548c2ecf20Sopenharmony_ci} 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci/* 32578c2ecf20Sopenharmony_ci * 0 or more than 0 is returned when validation is succeeded (either bypass 32588c2ecf20Sopenharmony_ci * because of optional transport mode, or next index of the mathced secpath 32598c2ecf20Sopenharmony_ci * state with the template. 32608c2ecf20Sopenharmony_ci * -1 is returned when no matching template is found. 32618c2ecf20Sopenharmony_ci * Otherwise "-2 - errored_index" is returned. 32628c2ecf20Sopenharmony_ci */ 32638c2ecf20Sopenharmony_cistatic inline int 32648c2ecf20Sopenharmony_cixfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int start, 32658c2ecf20Sopenharmony_ci unsigned short family, u32 if_id) 32668c2ecf20Sopenharmony_ci{ 32678c2ecf20Sopenharmony_ci int idx = start; 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_ci if (tmpl->optional) { 32708c2ecf20Sopenharmony_ci if (tmpl->mode == XFRM_MODE_TRANSPORT) 32718c2ecf20Sopenharmony_ci return start; 32728c2ecf20Sopenharmony_ci } else 32738c2ecf20Sopenharmony_ci start = -1; 32748c2ecf20Sopenharmony_ci for (; idx < sp->len; idx++) { 32758c2ecf20Sopenharmony_ci if (xfrm_state_ok(tmpl, sp->xvec[idx], family, if_id)) 32768c2ecf20Sopenharmony_ci return ++idx; 32778c2ecf20Sopenharmony_ci if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) { 32788c2ecf20Sopenharmony_ci if (idx < sp->verified_cnt) { 32798c2ecf20Sopenharmony_ci /* Secpath entry previously verified, consider optional and 32808c2ecf20Sopenharmony_ci * continue searching 32818c2ecf20Sopenharmony_ci */ 32828c2ecf20Sopenharmony_ci continue; 32838c2ecf20Sopenharmony_ci } 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci if (start == -1) 32868c2ecf20Sopenharmony_ci start = -2-idx; 32878c2ecf20Sopenharmony_ci break; 32888c2ecf20Sopenharmony_ci } 32898c2ecf20Sopenharmony_ci } 32908c2ecf20Sopenharmony_ci return start; 32918c2ecf20Sopenharmony_ci} 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_cistatic void 32948c2ecf20Sopenharmony_cidecode_session4(struct sk_buff *skb, struct flowi *fl, bool reverse) 32958c2ecf20Sopenharmony_ci{ 32968c2ecf20Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 32978c2ecf20Sopenharmony_ci int ihl = iph->ihl; 32988c2ecf20Sopenharmony_ci u8 *xprth = skb_network_header(skb) + ihl * 4; 32998c2ecf20Sopenharmony_ci struct flowi4 *fl4 = &fl->u.ip4; 33008c2ecf20Sopenharmony_ci int oif = 0; 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci if (skb_dst(skb) && skb_dst(skb)->dev) 33038c2ecf20Sopenharmony_ci oif = skb_dst(skb)->dev->ifindex; 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci memset(fl4, 0, sizeof(struct flowi4)); 33068c2ecf20Sopenharmony_ci fl4->flowi4_mark = skb->mark; 33078c2ecf20Sopenharmony_ci fl4->flowi4_oif = reverse ? skb->skb_iif : oif; 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci fl4->flowi4_proto = iph->protocol; 33108c2ecf20Sopenharmony_ci fl4->daddr = reverse ? iph->saddr : iph->daddr; 33118c2ecf20Sopenharmony_ci fl4->saddr = reverse ? iph->daddr : iph->saddr; 33128c2ecf20Sopenharmony_ci fl4->flowi4_tos = iph->tos & ~INET_ECN_MASK; 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci if (!ip_is_fragment(iph)) { 33158c2ecf20Sopenharmony_ci switch (iph->protocol) { 33168c2ecf20Sopenharmony_ci case IPPROTO_UDP: 33178c2ecf20Sopenharmony_ci case IPPROTO_UDPLITE: 33188c2ecf20Sopenharmony_ci case IPPROTO_TCP: 33198c2ecf20Sopenharmony_ci case IPPROTO_SCTP: 33208c2ecf20Sopenharmony_ci case IPPROTO_DCCP: 33218c2ecf20Sopenharmony_ci if (xprth + 4 < skb->data || 33228c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 4 - skb->data)) { 33238c2ecf20Sopenharmony_ci __be16 *ports; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33268c2ecf20Sopenharmony_ci ports = (__be16 *)xprth; 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci fl4->fl4_sport = ports[!!reverse]; 33298c2ecf20Sopenharmony_ci fl4->fl4_dport = ports[!reverse]; 33308c2ecf20Sopenharmony_ci } 33318c2ecf20Sopenharmony_ci break; 33328c2ecf20Sopenharmony_ci case IPPROTO_ICMP: 33338c2ecf20Sopenharmony_ci if (xprth + 2 < skb->data || 33348c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 2 - skb->data)) { 33358c2ecf20Sopenharmony_ci u8 *icmp; 33368c2ecf20Sopenharmony_ci 33378c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33388c2ecf20Sopenharmony_ci icmp = xprth; 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci fl4->fl4_icmp_type = icmp[0]; 33418c2ecf20Sopenharmony_ci fl4->fl4_icmp_code = icmp[1]; 33428c2ecf20Sopenharmony_ci } 33438c2ecf20Sopenharmony_ci break; 33448c2ecf20Sopenharmony_ci case IPPROTO_ESP: 33458c2ecf20Sopenharmony_ci if (xprth + 4 < skb->data || 33468c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 4 - skb->data)) { 33478c2ecf20Sopenharmony_ci __be32 *ehdr; 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33508c2ecf20Sopenharmony_ci ehdr = (__be32 *)xprth; 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci fl4->fl4_ipsec_spi = ehdr[0]; 33538c2ecf20Sopenharmony_ci } 33548c2ecf20Sopenharmony_ci break; 33558c2ecf20Sopenharmony_ci case IPPROTO_AH: 33568c2ecf20Sopenharmony_ci if (xprth + 8 < skb->data || 33578c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 8 - skb->data)) { 33588c2ecf20Sopenharmony_ci __be32 *ah_hdr; 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33618c2ecf20Sopenharmony_ci ah_hdr = (__be32 *)xprth; 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci fl4->fl4_ipsec_spi = ah_hdr[1]; 33648c2ecf20Sopenharmony_ci } 33658c2ecf20Sopenharmony_ci break; 33668c2ecf20Sopenharmony_ci case IPPROTO_COMP: 33678c2ecf20Sopenharmony_ci if (xprth + 4 < skb->data || 33688c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 4 - skb->data)) { 33698c2ecf20Sopenharmony_ci __be16 *ipcomp_hdr; 33708c2ecf20Sopenharmony_ci 33718c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33728c2ecf20Sopenharmony_ci ipcomp_hdr = (__be16 *)xprth; 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1])); 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci break; 33778c2ecf20Sopenharmony_ci case IPPROTO_GRE: 33788c2ecf20Sopenharmony_ci if (xprth + 12 < skb->data || 33798c2ecf20Sopenharmony_ci pskb_may_pull(skb, xprth + 12 - skb->data)) { 33808c2ecf20Sopenharmony_ci __be16 *greflags; 33818c2ecf20Sopenharmony_ci __be32 *gre_hdr; 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_ci xprth = skb_network_header(skb) + ihl * 4; 33848c2ecf20Sopenharmony_ci greflags = (__be16 *)xprth; 33858c2ecf20Sopenharmony_ci gre_hdr = (__be32 *)xprth; 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci if (greflags[0] & GRE_KEY) { 33888c2ecf20Sopenharmony_ci if (greflags[0] & GRE_CSUM) 33898c2ecf20Sopenharmony_ci gre_hdr++; 33908c2ecf20Sopenharmony_ci fl4->fl4_gre_key = gre_hdr[1]; 33918c2ecf20Sopenharmony_ci } 33928c2ecf20Sopenharmony_ci } 33938c2ecf20Sopenharmony_ci break; 33948c2ecf20Sopenharmony_ci default: 33958c2ecf20Sopenharmony_ci fl4->fl4_ipsec_spi = 0; 33968c2ecf20Sopenharmony_ci break; 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci } 33998c2ecf20Sopenharmony_ci} 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 34028c2ecf20Sopenharmony_cistatic void 34038c2ecf20Sopenharmony_cidecode_session6(struct sk_buff *skb, struct flowi *fl, bool reverse) 34048c2ecf20Sopenharmony_ci{ 34058c2ecf20Sopenharmony_ci struct flowi6 *fl6 = &fl->u.ip6; 34068c2ecf20Sopenharmony_ci int onlyproto = 0; 34078c2ecf20Sopenharmony_ci const struct ipv6hdr *hdr = ipv6_hdr(skb); 34088c2ecf20Sopenharmony_ci u32 offset = sizeof(*hdr); 34098c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *exthdr; 34108c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 34118c2ecf20Sopenharmony_ci u16 nhoff = IP6CB(skb)->nhoff; 34128c2ecf20Sopenharmony_ci int oif = 0; 34138c2ecf20Sopenharmony_ci u8 nexthdr; 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci if (!nhoff) 34168c2ecf20Sopenharmony_ci nhoff = offsetof(struct ipv6hdr, nexthdr); 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci nexthdr = nh[nhoff]; 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci if (skb_dst(skb) && skb_dst(skb)->dev) 34218c2ecf20Sopenharmony_ci oif = skb_dst(skb)->dev->ifindex; 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_ci memset(fl6, 0, sizeof(struct flowi6)); 34248c2ecf20Sopenharmony_ci fl6->flowi6_mark = skb->mark; 34258c2ecf20Sopenharmony_ci fl6->flowi6_oif = reverse ? skb->skb_iif : oif; 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_ci fl6->daddr = reverse ? hdr->saddr : hdr->daddr; 34288c2ecf20Sopenharmony_ci fl6->saddr = reverse ? hdr->daddr : hdr->saddr; 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci while (nh + offset + sizeof(*exthdr) < skb->data || 34318c2ecf20Sopenharmony_ci pskb_may_pull(skb, nh + offset + sizeof(*exthdr) - skb->data)) { 34328c2ecf20Sopenharmony_ci nh = skb_network_header(skb); 34338c2ecf20Sopenharmony_ci exthdr = (struct ipv6_opt_hdr *)(nh + offset); 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci switch (nexthdr) { 34368c2ecf20Sopenharmony_ci case NEXTHDR_FRAGMENT: 34378c2ecf20Sopenharmony_ci onlyproto = 1; 34388c2ecf20Sopenharmony_ci fallthrough; 34398c2ecf20Sopenharmony_ci case NEXTHDR_ROUTING: 34408c2ecf20Sopenharmony_ci case NEXTHDR_HOP: 34418c2ecf20Sopenharmony_ci case NEXTHDR_DEST: 34428c2ecf20Sopenharmony_ci offset += ipv6_optlen(exthdr); 34438c2ecf20Sopenharmony_ci nexthdr = exthdr->nexthdr; 34448c2ecf20Sopenharmony_ci exthdr = (struct ipv6_opt_hdr *)(nh + offset); 34458c2ecf20Sopenharmony_ci break; 34468c2ecf20Sopenharmony_ci case IPPROTO_UDP: 34478c2ecf20Sopenharmony_ci case IPPROTO_UDPLITE: 34488c2ecf20Sopenharmony_ci case IPPROTO_TCP: 34498c2ecf20Sopenharmony_ci case IPPROTO_SCTP: 34508c2ecf20Sopenharmony_ci case IPPROTO_DCCP: 34518c2ecf20Sopenharmony_ci if (!onlyproto && (nh + offset + 4 < skb->data || 34528c2ecf20Sopenharmony_ci pskb_may_pull(skb, nh + offset + 4 - skb->data))) { 34538c2ecf20Sopenharmony_ci __be16 *ports; 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci nh = skb_network_header(skb); 34568c2ecf20Sopenharmony_ci ports = (__be16 *)(nh + offset); 34578c2ecf20Sopenharmony_ci fl6->fl6_sport = ports[!!reverse]; 34588c2ecf20Sopenharmony_ci fl6->fl6_dport = ports[!reverse]; 34598c2ecf20Sopenharmony_ci } 34608c2ecf20Sopenharmony_ci fl6->flowi6_proto = nexthdr; 34618c2ecf20Sopenharmony_ci return; 34628c2ecf20Sopenharmony_ci case IPPROTO_ICMPV6: 34638c2ecf20Sopenharmony_ci if (!onlyproto && (nh + offset + 2 < skb->data || 34648c2ecf20Sopenharmony_ci pskb_may_pull(skb, nh + offset + 2 - skb->data))) { 34658c2ecf20Sopenharmony_ci u8 *icmp; 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_ci nh = skb_network_header(skb); 34688c2ecf20Sopenharmony_ci icmp = (u8 *)(nh + offset); 34698c2ecf20Sopenharmony_ci fl6->fl6_icmp_type = icmp[0]; 34708c2ecf20Sopenharmony_ci fl6->fl6_icmp_code = icmp[1]; 34718c2ecf20Sopenharmony_ci } 34728c2ecf20Sopenharmony_ci fl6->flowi6_proto = nexthdr; 34738c2ecf20Sopenharmony_ci return; 34748c2ecf20Sopenharmony_ci case IPPROTO_GRE: 34758c2ecf20Sopenharmony_ci if (!onlyproto && 34768c2ecf20Sopenharmony_ci (nh + offset + 12 < skb->data || 34778c2ecf20Sopenharmony_ci pskb_may_pull(skb, nh + offset + 12 - skb->data))) { 34788c2ecf20Sopenharmony_ci struct gre_base_hdr *gre_hdr; 34798c2ecf20Sopenharmony_ci __be32 *gre_key; 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci nh = skb_network_header(skb); 34828c2ecf20Sopenharmony_ci gre_hdr = (struct gre_base_hdr *)(nh + offset); 34838c2ecf20Sopenharmony_ci gre_key = (__be32 *)(gre_hdr + 1); 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci if (gre_hdr->flags & GRE_KEY) { 34868c2ecf20Sopenharmony_ci if (gre_hdr->flags & GRE_CSUM) 34878c2ecf20Sopenharmony_ci gre_key++; 34888c2ecf20Sopenharmony_ci fl6->fl6_gre_key = *gre_key; 34898c2ecf20Sopenharmony_ci } 34908c2ecf20Sopenharmony_ci } 34918c2ecf20Sopenharmony_ci fl6->flowi6_proto = nexthdr; 34928c2ecf20Sopenharmony_ci return; 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 34958c2ecf20Sopenharmony_ci case IPPROTO_MH: 34968c2ecf20Sopenharmony_ci offset += ipv6_optlen(exthdr); 34978c2ecf20Sopenharmony_ci if (!onlyproto && (nh + offset + 3 < skb->data || 34988c2ecf20Sopenharmony_ci pskb_may_pull(skb, nh + offset + 3 - skb->data))) { 34998c2ecf20Sopenharmony_ci struct ip6_mh *mh; 35008c2ecf20Sopenharmony_ci 35018c2ecf20Sopenharmony_ci nh = skb_network_header(skb); 35028c2ecf20Sopenharmony_ci mh = (struct ip6_mh *)(nh + offset); 35038c2ecf20Sopenharmony_ci fl6->fl6_mh_type = mh->ip6mh_type; 35048c2ecf20Sopenharmony_ci } 35058c2ecf20Sopenharmony_ci fl6->flowi6_proto = nexthdr; 35068c2ecf20Sopenharmony_ci return; 35078c2ecf20Sopenharmony_ci#endif 35088c2ecf20Sopenharmony_ci /* XXX Why are there these headers? */ 35098c2ecf20Sopenharmony_ci case IPPROTO_AH: 35108c2ecf20Sopenharmony_ci case IPPROTO_ESP: 35118c2ecf20Sopenharmony_ci case IPPROTO_COMP: 35128c2ecf20Sopenharmony_ci default: 35138c2ecf20Sopenharmony_ci fl6->fl6_ipsec_spi = 0; 35148c2ecf20Sopenharmony_ci fl6->flowi6_proto = nexthdr; 35158c2ecf20Sopenharmony_ci return; 35168c2ecf20Sopenharmony_ci } 35178c2ecf20Sopenharmony_ci } 35188c2ecf20Sopenharmony_ci} 35198c2ecf20Sopenharmony_ci#endif 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ciint __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, 35228c2ecf20Sopenharmony_ci unsigned int family, int reverse) 35238c2ecf20Sopenharmony_ci{ 35248c2ecf20Sopenharmony_ci switch (family) { 35258c2ecf20Sopenharmony_ci case AF_INET: 35268c2ecf20Sopenharmony_ci decode_session4(skb, fl, reverse); 35278c2ecf20Sopenharmony_ci break; 35288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 35298c2ecf20Sopenharmony_ci case AF_INET6: 35308c2ecf20Sopenharmony_ci decode_session6(skb, fl, reverse); 35318c2ecf20Sopenharmony_ci break; 35328c2ecf20Sopenharmony_ci#endif 35338c2ecf20Sopenharmony_ci default: 35348c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 35358c2ecf20Sopenharmony_ci } 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci return security_xfrm_decode_session(skb, &fl->flowi_secid); 35388c2ecf20Sopenharmony_ci} 35398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_decode_session); 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_cistatic inline int secpath_has_nontransport(const struct sec_path *sp, int k, int *idxp) 35428c2ecf20Sopenharmony_ci{ 35438c2ecf20Sopenharmony_ci for (; k < sp->len; k++) { 35448c2ecf20Sopenharmony_ci if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) { 35458c2ecf20Sopenharmony_ci *idxp = k; 35468c2ecf20Sopenharmony_ci return 1; 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci return 0; 35518c2ecf20Sopenharmony_ci} 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ciint __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, 35548c2ecf20Sopenharmony_ci unsigned short family) 35558c2ecf20Sopenharmony_ci{ 35568c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 35578c2ecf20Sopenharmony_ci struct xfrm_policy *pol; 35588c2ecf20Sopenharmony_ci struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; 35598c2ecf20Sopenharmony_ci int npols = 0; 35608c2ecf20Sopenharmony_ci int xfrm_nr; 35618c2ecf20Sopenharmony_ci int pi; 35628c2ecf20Sopenharmony_ci int reverse; 35638c2ecf20Sopenharmony_ci struct flowi fl; 35648c2ecf20Sopenharmony_ci int xerr_idx = -1; 35658c2ecf20Sopenharmony_ci const struct xfrm_if_cb *ifcb; 35668c2ecf20Sopenharmony_ci struct sec_path *sp; 35678c2ecf20Sopenharmony_ci struct xfrm_if *xi; 35688c2ecf20Sopenharmony_ci u32 if_id = 0; 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci rcu_read_lock(); 35718c2ecf20Sopenharmony_ci ifcb = xfrm_if_get_cb(); 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci if (ifcb) { 35748c2ecf20Sopenharmony_ci xi = ifcb->decode_session(skb, family); 35758c2ecf20Sopenharmony_ci if (xi) { 35768c2ecf20Sopenharmony_ci if_id = xi->p.if_id; 35778c2ecf20Sopenharmony_ci net = xi->net; 35788c2ecf20Sopenharmony_ci } 35798c2ecf20Sopenharmony_ci } 35808c2ecf20Sopenharmony_ci rcu_read_unlock(); 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci reverse = dir & ~XFRM_POLICY_MASK; 35838c2ecf20Sopenharmony_ci dir &= XFRM_POLICY_MASK; 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) { 35868c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); 35878c2ecf20Sopenharmony_ci return 0; 35888c2ecf20Sopenharmony_ci } 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci nf_nat_decode_session(skb, &fl, family); 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci /* First, check used SA against their selectors. */ 35938c2ecf20Sopenharmony_ci sp = skb_sec_path(skb); 35948c2ecf20Sopenharmony_ci if (sp) { 35958c2ecf20Sopenharmony_ci int i; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci for (i = sp->len - 1; i >= 0; i--) { 35988c2ecf20Sopenharmony_ci struct xfrm_state *x = sp->xvec[i]; 35998c2ecf20Sopenharmony_ci if (!xfrm_selector_match(&x->sel, &fl, family)) { 36008c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH); 36018c2ecf20Sopenharmony_ci return 0; 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci } 36048c2ecf20Sopenharmony_ci } 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci pol = NULL; 36078c2ecf20Sopenharmony_ci sk = sk_to_full_sk(sk); 36088c2ecf20Sopenharmony_ci if (sk && sk->sk_policy[dir]) { 36098c2ecf20Sopenharmony_ci pol = xfrm_sk_policy_lookup(sk, dir, &fl, family, if_id); 36108c2ecf20Sopenharmony_ci if (IS_ERR(pol)) { 36118c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); 36128c2ecf20Sopenharmony_ci return 0; 36138c2ecf20Sopenharmony_ci } 36148c2ecf20Sopenharmony_ci } 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci if (!pol) 36178c2ecf20Sopenharmony_ci pol = xfrm_policy_lookup(net, &fl, family, dir, if_id); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci if (IS_ERR(pol)) { 36208c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); 36218c2ecf20Sopenharmony_ci return 0; 36228c2ecf20Sopenharmony_ci } 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci if (!pol) { 36258c2ecf20Sopenharmony_ci if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { 36268c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); 36278c2ecf20Sopenharmony_ci return 0; 36288c2ecf20Sopenharmony_ci } 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_ci if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) { 36318c2ecf20Sopenharmony_ci xfrm_secpath_reject(xerr_idx, skb, &fl); 36328c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); 36338c2ecf20Sopenharmony_ci return 0; 36348c2ecf20Sopenharmony_ci } 36358c2ecf20Sopenharmony_ci return 1; 36368c2ecf20Sopenharmony_ci } 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci pol->curlft.use_time = ktime_get_real_seconds(); 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci pols[0] = pol; 36418c2ecf20Sopenharmony_ci npols++; 36428c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 36438c2ecf20Sopenharmony_ci if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) { 36448c2ecf20Sopenharmony_ci pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, 36458c2ecf20Sopenharmony_ci &fl, family, 36468c2ecf20Sopenharmony_ci XFRM_POLICY_IN, if_id); 36478c2ecf20Sopenharmony_ci if (pols[1]) { 36488c2ecf20Sopenharmony_ci if (IS_ERR(pols[1])) { 36498c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); 36508c2ecf20Sopenharmony_ci xfrm_pol_put(pols[0]); 36518c2ecf20Sopenharmony_ci return 0; 36528c2ecf20Sopenharmony_ci } 36538c2ecf20Sopenharmony_ci pols[1]->curlft.use_time = ktime_get_real_seconds(); 36548c2ecf20Sopenharmony_ci npols++; 36558c2ecf20Sopenharmony_ci } 36568c2ecf20Sopenharmony_ci } 36578c2ecf20Sopenharmony_ci#endif 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci if (pol->action == XFRM_POLICY_ALLOW) { 36608c2ecf20Sopenharmony_ci static struct sec_path dummy; 36618c2ecf20Sopenharmony_ci struct xfrm_tmpl *tp[XFRM_MAX_DEPTH]; 36628c2ecf20Sopenharmony_ci struct xfrm_tmpl *stp[XFRM_MAX_DEPTH]; 36638c2ecf20Sopenharmony_ci struct xfrm_tmpl **tpp = tp; 36648c2ecf20Sopenharmony_ci int ti = 0; 36658c2ecf20Sopenharmony_ci int i, k; 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci sp = skb_sec_path(skb); 36688c2ecf20Sopenharmony_ci if (!sp) 36698c2ecf20Sopenharmony_ci sp = &dummy; 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci for (pi = 0; pi < npols; pi++) { 36728c2ecf20Sopenharmony_ci if (pols[pi] != pol && 36738c2ecf20Sopenharmony_ci pols[pi]->action != XFRM_POLICY_ALLOW) { 36748c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK); 36758c2ecf20Sopenharmony_ci goto reject; 36768c2ecf20Sopenharmony_ci } 36778c2ecf20Sopenharmony_ci if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) { 36788c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); 36798c2ecf20Sopenharmony_ci goto reject_error; 36808c2ecf20Sopenharmony_ci } 36818c2ecf20Sopenharmony_ci for (i = 0; i < pols[pi]->xfrm_nr; i++) 36828c2ecf20Sopenharmony_ci tpp[ti++] = &pols[pi]->xfrm_vec[i]; 36838c2ecf20Sopenharmony_ci } 36848c2ecf20Sopenharmony_ci xfrm_nr = ti; 36858c2ecf20Sopenharmony_ci 36868c2ecf20Sopenharmony_ci if (npols > 1) { 36878c2ecf20Sopenharmony_ci xfrm_tmpl_sort(stp, tpp, xfrm_nr, family); 36888c2ecf20Sopenharmony_ci tpp = stp; 36898c2ecf20Sopenharmony_ci } 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci /* For each tunnel xfrm, find the first matching tmpl. 36928c2ecf20Sopenharmony_ci * For each tmpl before that, find corresponding xfrm. 36938c2ecf20Sopenharmony_ci * Order is _important_. Later we will implement 36948c2ecf20Sopenharmony_ci * some barriers, but at the moment barriers 36958c2ecf20Sopenharmony_ci * are implied between each two transformations. 36968c2ecf20Sopenharmony_ci * Upon success, marks secpath entries as having been 36978c2ecf20Sopenharmony_ci * verified to allow them to be skipped in future policy 36988c2ecf20Sopenharmony_ci * checks (e.g. nested tunnels). 36998c2ecf20Sopenharmony_ci */ 37008c2ecf20Sopenharmony_ci for (i = xfrm_nr-1, k = 0; i >= 0; i--) { 37018c2ecf20Sopenharmony_ci k = xfrm_policy_ok(tpp[i], sp, k, family, if_id); 37028c2ecf20Sopenharmony_ci if (k < 0) { 37038c2ecf20Sopenharmony_ci if (k < -1) 37048c2ecf20Sopenharmony_ci /* "-2 - errored_index" returned */ 37058c2ecf20Sopenharmony_ci xerr_idx = -(2+k); 37068c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH); 37078c2ecf20Sopenharmony_ci goto reject; 37088c2ecf20Sopenharmony_ci } 37098c2ecf20Sopenharmony_ci } 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci if (secpath_has_nontransport(sp, k, &xerr_idx)) { 37128c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH); 37138c2ecf20Sopenharmony_ci goto reject; 37148c2ecf20Sopenharmony_ci } 37158c2ecf20Sopenharmony_ci 37168c2ecf20Sopenharmony_ci xfrm_pols_put(pols, npols); 37178c2ecf20Sopenharmony_ci sp->verified_cnt = k; 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci return 1; 37208c2ecf20Sopenharmony_ci } 37218c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK); 37228c2ecf20Sopenharmony_ci 37238c2ecf20Sopenharmony_cireject: 37248c2ecf20Sopenharmony_ci xfrm_secpath_reject(xerr_idx, skb, &fl); 37258c2ecf20Sopenharmony_cireject_error: 37268c2ecf20Sopenharmony_ci xfrm_pols_put(pols, npols); 37278c2ecf20Sopenharmony_ci return 0; 37288c2ecf20Sopenharmony_ci} 37298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_policy_check); 37308c2ecf20Sopenharmony_ci 37318c2ecf20Sopenharmony_ciint __xfrm_route_forward(struct sk_buff *skb, unsigned short family) 37328c2ecf20Sopenharmony_ci{ 37338c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 37348c2ecf20Sopenharmony_ci struct flowi fl; 37358c2ecf20Sopenharmony_ci struct dst_entry *dst; 37368c2ecf20Sopenharmony_ci int res = 1; 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci if (xfrm_decode_session(skb, &fl, family) < 0) { 37398c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR); 37408c2ecf20Sopenharmony_ci return 0; 37418c2ecf20Sopenharmony_ci } 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci skb_dst_force(skb); 37448c2ecf20Sopenharmony_ci if (!skb_dst(skb)) { 37458c2ecf20Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR); 37468c2ecf20Sopenharmony_ci return 0; 37478c2ecf20Sopenharmony_ci } 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE); 37508c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 37518c2ecf20Sopenharmony_ci res = 0; 37528c2ecf20Sopenharmony_ci dst = NULL; 37538c2ecf20Sopenharmony_ci } 37548c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 37558c2ecf20Sopenharmony_ci return res; 37568c2ecf20Sopenharmony_ci} 37578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xfrm_route_forward); 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci/* Optimize later using cookies and generation ids. */ 37608c2ecf20Sopenharmony_ci 37618c2ecf20Sopenharmony_cistatic struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie) 37628c2ecf20Sopenharmony_ci{ 37638c2ecf20Sopenharmony_ci /* Code (such as __xfrm4_bundle_create()) sets dst->obsolete 37648c2ecf20Sopenharmony_ci * to DST_OBSOLETE_FORCE_CHK to force all XFRM destinations to 37658c2ecf20Sopenharmony_ci * get validated by dst_ops->check on every use. We do this 37668c2ecf20Sopenharmony_ci * because when a normal route referenced by an XFRM dst is 37678c2ecf20Sopenharmony_ci * obsoleted we do not go looking around for all parent 37688c2ecf20Sopenharmony_ci * referencing XFRM dsts so that we can invalidate them. It 37698c2ecf20Sopenharmony_ci * is just too much work. Instead we make the checks here on 37708c2ecf20Sopenharmony_ci * every use. For example: 37718c2ecf20Sopenharmony_ci * 37728c2ecf20Sopenharmony_ci * XFRM dst A --> IPv4 dst X 37738c2ecf20Sopenharmony_ci * 37748c2ecf20Sopenharmony_ci * X is the "xdst->route" of A (X is also the "dst->path" of A 37758c2ecf20Sopenharmony_ci * in this example). If X is marked obsolete, "A" will not 37768c2ecf20Sopenharmony_ci * notice. That's what we are validating here via the 37778c2ecf20Sopenharmony_ci * stale_bundle() check. 37788c2ecf20Sopenharmony_ci * 37798c2ecf20Sopenharmony_ci * When a dst is removed from the fib tree, DST_OBSOLETE_DEAD will 37808c2ecf20Sopenharmony_ci * be marked on it. 37818c2ecf20Sopenharmony_ci * This will force stale_bundle() to fail on any xdst bundle with 37828c2ecf20Sopenharmony_ci * this dst linked in it. 37838c2ecf20Sopenharmony_ci */ 37848c2ecf20Sopenharmony_ci if (dst->obsolete < 0 && !stale_bundle(dst)) 37858c2ecf20Sopenharmony_ci return dst; 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci return NULL; 37888c2ecf20Sopenharmony_ci} 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_cistatic int stale_bundle(struct dst_entry *dst) 37918c2ecf20Sopenharmony_ci{ 37928c2ecf20Sopenharmony_ci return !xfrm_bundle_ok((struct xfrm_dst *)dst); 37938c2ecf20Sopenharmony_ci} 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_civoid xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev) 37968c2ecf20Sopenharmony_ci{ 37978c2ecf20Sopenharmony_ci while ((dst = xfrm_dst_child(dst)) && dst->xfrm && dst->dev == dev) { 37988c2ecf20Sopenharmony_ci dst->dev = dev_net(dev)->loopback_dev; 37998c2ecf20Sopenharmony_ci dev_hold(dst->dev); 38008c2ecf20Sopenharmony_ci dev_put(dev); 38018c2ecf20Sopenharmony_ci } 38028c2ecf20Sopenharmony_ci} 38038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_dst_ifdown); 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_cistatic void xfrm_link_failure(struct sk_buff *skb) 38068c2ecf20Sopenharmony_ci{ 38078c2ecf20Sopenharmony_ci /* Impossible. Such dst must be popped before reaches point of failure. */ 38088c2ecf20Sopenharmony_ci} 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_cistatic void xfrm_negative_advice(struct sock *sk, struct dst_entry *dst) 38118c2ecf20Sopenharmony_ci{ 38128c2ecf20Sopenharmony_ci if (dst->obsolete) 38138c2ecf20Sopenharmony_ci sk_dst_reset(sk); 38148c2ecf20Sopenharmony_ci} 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_cistatic void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr) 38178c2ecf20Sopenharmony_ci{ 38188c2ecf20Sopenharmony_ci while (nr--) { 38198c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = bundle[nr]; 38208c2ecf20Sopenharmony_ci u32 pmtu, route_mtu_cached; 38218c2ecf20Sopenharmony_ci struct dst_entry *dst; 38228c2ecf20Sopenharmony_ci 38238c2ecf20Sopenharmony_ci dst = &xdst->u.dst; 38248c2ecf20Sopenharmony_ci pmtu = dst_mtu(xfrm_dst_child(dst)); 38258c2ecf20Sopenharmony_ci xdst->child_mtu_cached = pmtu; 38268c2ecf20Sopenharmony_ci 38278c2ecf20Sopenharmony_ci pmtu = xfrm_state_mtu(dst->xfrm, pmtu); 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci route_mtu_cached = dst_mtu(xdst->route); 38308c2ecf20Sopenharmony_ci xdst->route_mtu_cached = route_mtu_cached; 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci if (pmtu > route_mtu_cached) 38338c2ecf20Sopenharmony_ci pmtu = route_mtu_cached; 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci dst_metric_set(dst, RTAX_MTU, pmtu); 38368c2ecf20Sopenharmony_ci } 38378c2ecf20Sopenharmony_ci} 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci/* Check that the bundle accepts the flow and its components are 38408c2ecf20Sopenharmony_ci * still valid. 38418c2ecf20Sopenharmony_ci */ 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_cistatic int xfrm_bundle_ok(struct xfrm_dst *first) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci struct xfrm_dst *bundle[XFRM_MAX_DEPTH]; 38468c2ecf20Sopenharmony_ci struct dst_entry *dst = &first->u.dst; 38478c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 38488c2ecf20Sopenharmony_ci int start_from, nr; 38498c2ecf20Sopenharmony_ci u32 mtu; 38508c2ecf20Sopenharmony_ci 38518c2ecf20Sopenharmony_ci if (!dst_check(xfrm_dst_path(dst), ((struct xfrm_dst *)dst)->path_cookie) || 38528c2ecf20Sopenharmony_ci (dst->dev && !netif_running(dst->dev))) 38538c2ecf20Sopenharmony_ci return 0; 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci if (dst->flags & DST_XFRM_QUEUE) 38568c2ecf20Sopenharmony_ci return 1; 38578c2ecf20Sopenharmony_ci 38588c2ecf20Sopenharmony_ci start_from = nr = 0; 38598c2ecf20Sopenharmony_ci do { 38608c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci if (dst->xfrm->km.state != XFRM_STATE_VALID) 38638c2ecf20Sopenharmony_ci return 0; 38648c2ecf20Sopenharmony_ci if (xdst->xfrm_genid != dst->xfrm->genid) 38658c2ecf20Sopenharmony_ci return 0; 38668c2ecf20Sopenharmony_ci if (xdst->num_pols > 0 && 38678c2ecf20Sopenharmony_ci xdst->policy_genid != atomic_read(&xdst->pols[0]->genid)) 38688c2ecf20Sopenharmony_ci return 0; 38698c2ecf20Sopenharmony_ci 38708c2ecf20Sopenharmony_ci bundle[nr++] = xdst; 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_ci mtu = dst_mtu(xfrm_dst_child(dst)); 38738c2ecf20Sopenharmony_ci if (xdst->child_mtu_cached != mtu) { 38748c2ecf20Sopenharmony_ci start_from = nr; 38758c2ecf20Sopenharmony_ci xdst->child_mtu_cached = mtu; 38768c2ecf20Sopenharmony_ci } 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci if (!dst_check(xdst->route, xdst->route_cookie)) 38798c2ecf20Sopenharmony_ci return 0; 38808c2ecf20Sopenharmony_ci mtu = dst_mtu(xdst->route); 38818c2ecf20Sopenharmony_ci if (xdst->route_mtu_cached != mtu) { 38828c2ecf20Sopenharmony_ci start_from = nr; 38838c2ecf20Sopenharmony_ci xdst->route_mtu_cached = mtu; 38848c2ecf20Sopenharmony_ci } 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci dst = xfrm_dst_child(dst); 38878c2ecf20Sopenharmony_ci } while (dst->xfrm); 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci if (likely(!start_from)) 38908c2ecf20Sopenharmony_ci return 1; 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_ci xdst = bundle[start_from - 1]; 38938c2ecf20Sopenharmony_ci mtu = xdst->child_mtu_cached; 38948c2ecf20Sopenharmony_ci while (start_from--) { 38958c2ecf20Sopenharmony_ci dst = &xdst->u.dst; 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci mtu = xfrm_state_mtu(dst->xfrm, mtu); 38988c2ecf20Sopenharmony_ci if (mtu > xdst->route_mtu_cached) 38998c2ecf20Sopenharmony_ci mtu = xdst->route_mtu_cached; 39008c2ecf20Sopenharmony_ci dst_metric_set(dst, RTAX_MTU, mtu); 39018c2ecf20Sopenharmony_ci if (!start_from) 39028c2ecf20Sopenharmony_ci break; 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci xdst = bundle[start_from - 1]; 39058c2ecf20Sopenharmony_ci xdst->child_mtu_cached = mtu; 39068c2ecf20Sopenharmony_ci } 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_ci return 1; 39098c2ecf20Sopenharmony_ci} 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_cistatic unsigned int xfrm_default_advmss(const struct dst_entry *dst) 39128c2ecf20Sopenharmony_ci{ 39138c2ecf20Sopenharmony_ci return dst_metric_advmss(xfrm_dst_path(dst)); 39148c2ecf20Sopenharmony_ci} 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_cistatic unsigned int xfrm_mtu(const struct dst_entry *dst) 39178c2ecf20Sopenharmony_ci{ 39188c2ecf20Sopenharmony_ci unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci return mtu ? : dst_mtu(xfrm_dst_path(dst)); 39218c2ecf20Sopenharmony_ci} 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_cistatic const void *xfrm_get_dst_nexthop(const struct dst_entry *dst, 39248c2ecf20Sopenharmony_ci const void *daddr) 39258c2ecf20Sopenharmony_ci{ 39268c2ecf20Sopenharmony_ci while (dst->xfrm) { 39278c2ecf20Sopenharmony_ci const struct xfrm_state *xfrm = dst->xfrm; 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci dst = xfrm_dst_child(dst); 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci if (xfrm->props.mode == XFRM_MODE_TRANSPORT) 39328c2ecf20Sopenharmony_ci continue; 39338c2ecf20Sopenharmony_ci if (xfrm->type->flags & XFRM_TYPE_REMOTE_COADDR) 39348c2ecf20Sopenharmony_ci daddr = xfrm->coaddr; 39358c2ecf20Sopenharmony_ci else if (!(xfrm->type->flags & XFRM_TYPE_LOCAL_COADDR)) 39368c2ecf20Sopenharmony_ci daddr = &xfrm->id.daddr; 39378c2ecf20Sopenharmony_ci } 39388c2ecf20Sopenharmony_ci return daddr; 39398c2ecf20Sopenharmony_ci} 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_cistatic struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst, 39428c2ecf20Sopenharmony_ci struct sk_buff *skb, 39438c2ecf20Sopenharmony_ci const void *daddr) 39448c2ecf20Sopenharmony_ci{ 39458c2ecf20Sopenharmony_ci const struct dst_entry *path = xfrm_dst_path(dst); 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci if (!skb) 39488c2ecf20Sopenharmony_ci daddr = xfrm_get_dst_nexthop(dst, daddr); 39498c2ecf20Sopenharmony_ci return path->ops->neigh_lookup(path, skb, daddr); 39508c2ecf20Sopenharmony_ci} 39518c2ecf20Sopenharmony_ci 39528c2ecf20Sopenharmony_cistatic void xfrm_confirm_neigh(const struct dst_entry *dst, const void *daddr) 39538c2ecf20Sopenharmony_ci{ 39548c2ecf20Sopenharmony_ci const struct dst_entry *path = xfrm_dst_path(dst); 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci daddr = xfrm_get_dst_nexthop(dst, daddr); 39578c2ecf20Sopenharmony_ci path->ops->confirm_neigh(path, daddr); 39588c2ecf20Sopenharmony_ci} 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ciint xfrm_policy_register_afinfo(const struct xfrm_policy_afinfo *afinfo, int family) 39618c2ecf20Sopenharmony_ci{ 39628c2ecf20Sopenharmony_ci int err = 0; 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci if (WARN_ON(family >= ARRAY_SIZE(xfrm_policy_afinfo))) 39658c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci spin_lock(&xfrm_policy_afinfo_lock); 39688c2ecf20Sopenharmony_ci if (unlikely(xfrm_policy_afinfo[family] != NULL)) 39698c2ecf20Sopenharmony_ci err = -EEXIST; 39708c2ecf20Sopenharmony_ci else { 39718c2ecf20Sopenharmony_ci struct dst_ops *dst_ops = afinfo->dst_ops; 39728c2ecf20Sopenharmony_ci if (likely(dst_ops->kmem_cachep == NULL)) 39738c2ecf20Sopenharmony_ci dst_ops->kmem_cachep = xfrm_dst_cache; 39748c2ecf20Sopenharmony_ci if (likely(dst_ops->check == NULL)) 39758c2ecf20Sopenharmony_ci dst_ops->check = xfrm_dst_check; 39768c2ecf20Sopenharmony_ci if (likely(dst_ops->default_advmss == NULL)) 39778c2ecf20Sopenharmony_ci dst_ops->default_advmss = xfrm_default_advmss; 39788c2ecf20Sopenharmony_ci if (likely(dst_ops->mtu == NULL)) 39798c2ecf20Sopenharmony_ci dst_ops->mtu = xfrm_mtu; 39808c2ecf20Sopenharmony_ci if (likely(dst_ops->negative_advice == NULL)) 39818c2ecf20Sopenharmony_ci dst_ops->negative_advice = xfrm_negative_advice; 39828c2ecf20Sopenharmony_ci if (likely(dst_ops->link_failure == NULL)) 39838c2ecf20Sopenharmony_ci dst_ops->link_failure = xfrm_link_failure; 39848c2ecf20Sopenharmony_ci if (likely(dst_ops->neigh_lookup == NULL)) 39858c2ecf20Sopenharmony_ci dst_ops->neigh_lookup = xfrm_neigh_lookup; 39868c2ecf20Sopenharmony_ci if (likely(!dst_ops->confirm_neigh)) 39878c2ecf20Sopenharmony_ci dst_ops->confirm_neigh = xfrm_confirm_neigh; 39888c2ecf20Sopenharmony_ci rcu_assign_pointer(xfrm_policy_afinfo[family], afinfo); 39898c2ecf20Sopenharmony_ci } 39908c2ecf20Sopenharmony_ci spin_unlock(&xfrm_policy_afinfo_lock); 39918c2ecf20Sopenharmony_ci 39928c2ecf20Sopenharmony_ci return err; 39938c2ecf20Sopenharmony_ci} 39948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_register_afinfo); 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_civoid xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo) 39978c2ecf20Sopenharmony_ci{ 39988c2ecf20Sopenharmony_ci struct dst_ops *dst_ops = afinfo->dst_ops; 39998c2ecf20Sopenharmony_ci int i; 40008c2ecf20Sopenharmony_ci 40018c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xfrm_policy_afinfo); i++) { 40028c2ecf20Sopenharmony_ci if (xfrm_policy_afinfo[i] != afinfo) 40038c2ecf20Sopenharmony_ci continue; 40048c2ecf20Sopenharmony_ci RCU_INIT_POINTER(xfrm_policy_afinfo[i], NULL); 40058c2ecf20Sopenharmony_ci break; 40068c2ecf20Sopenharmony_ci } 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci synchronize_rcu(); 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci dst_ops->kmem_cachep = NULL; 40118c2ecf20Sopenharmony_ci dst_ops->check = NULL; 40128c2ecf20Sopenharmony_ci dst_ops->negative_advice = NULL; 40138c2ecf20Sopenharmony_ci dst_ops->link_failure = NULL; 40148c2ecf20Sopenharmony_ci} 40158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_policy_unregister_afinfo); 40168c2ecf20Sopenharmony_ci 40178c2ecf20Sopenharmony_civoid xfrm_if_register_cb(const struct xfrm_if_cb *ifcb) 40188c2ecf20Sopenharmony_ci{ 40198c2ecf20Sopenharmony_ci spin_lock(&xfrm_if_cb_lock); 40208c2ecf20Sopenharmony_ci rcu_assign_pointer(xfrm_if_cb, ifcb); 40218c2ecf20Sopenharmony_ci spin_unlock(&xfrm_if_cb_lock); 40228c2ecf20Sopenharmony_ci} 40238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_if_register_cb); 40248c2ecf20Sopenharmony_ci 40258c2ecf20Sopenharmony_civoid xfrm_if_unregister_cb(void) 40268c2ecf20Sopenharmony_ci{ 40278c2ecf20Sopenharmony_ci RCU_INIT_POINTER(xfrm_if_cb, NULL); 40288c2ecf20Sopenharmony_ci synchronize_rcu(); 40298c2ecf20Sopenharmony_ci} 40308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_if_unregister_cb); 40318c2ecf20Sopenharmony_ci 40328c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_STATISTICS 40338c2ecf20Sopenharmony_cistatic int __net_init xfrm_statistics_init(struct net *net) 40348c2ecf20Sopenharmony_ci{ 40358c2ecf20Sopenharmony_ci int rv; 40368c2ecf20Sopenharmony_ci net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib); 40378c2ecf20Sopenharmony_ci if (!net->mib.xfrm_statistics) 40388c2ecf20Sopenharmony_ci return -ENOMEM; 40398c2ecf20Sopenharmony_ci rv = xfrm_proc_init(net); 40408c2ecf20Sopenharmony_ci if (rv < 0) 40418c2ecf20Sopenharmony_ci free_percpu(net->mib.xfrm_statistics); 40428c2ecf20Sopenharmony_ci return rv; 40438c2ecf20Sopenharmony_ci} 40448c2ecf20Sopenharmony_ci 40458c2ecf20Sopenharmony_cistatic void xfrm_statistics_fini(struct net *net) 40468c2ecf20Sopenharmony_ci{ 40478c2ecf20Sopenharmony_ci xfrm_proc_fini(net); 40488c2ecf20Sopenharmony_ci free_percpu(net->mib.xfrm_statistics); 40498c2ecf20Sopenharmony_ci} 40508c2ecf20Sopenharmony_ci#else 40518c2ecf20Sopenharmony_cistatic int __net_init xfrm_statistics_init(struct net *net) 40528c2ecf20Sopenharmony_ci{ 40538c2ecf20Sopenharmony_ci return 0; 40548c2ecf20Sopenharmony_ci} 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_cistatic void xfrm_statistics_fini(struct net *net) 40578c2ecf20Sopenharmony_ci{ 40588c2ecf20Sopenharmony_ci} 40598c2ecf20Sopenharmony_ci#endif 40608c2ecf20Sopenharmony_ci 40618c2ecf20Sopenharmony_cistatic int __net_init xfrm_policy_init(struct net *net) 40628c2ecf20Sopenharmony_ci{ 40638c2ecf20Sopenharmony_ci unsigned int hmask, sz; 40648c2ecf20Sopenharmony_ci int dir, err; 40658c2ecf20Sopenharmony_ci 40668c2ecf20Sopenharmony_ci if (net_eq(net, &init_net)) { 40678c2ecf20Sopenharmony_ci xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache", 40688c2ecf20Sopenharmony_ci sizeof(struct xfrm_dst), 40698c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, 40708c2ecf20Sopenharmony_ci NULL); 40718c2ecf20Sopenharmony_ci err = rhashtable_init(&xfrm_policy_inexact_table, 40728c2ecf20Sopenharmony_ci &xfrm_pol_inexact_params); 40738c2ecf20Sopenharmony_ci BUG_ON(err); 40748c2ecf20Sopenharmony_ci } 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci hmask = 8 - 1; 40778c2ecf20Sopenharmony_ci sz = (hmask+1) * sizeof(struct hlist_head); 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci net->xfrm.policy_byidx = xfrm_hash_alloc(sz); 40808c2ecf20Sopenharmony_ci if (!net->xfrm.policy_byidx) 40818c2ecf20Sopenharmony_ci goto out_byidx; 40828c2ecf20Sopenharmony_ci net->xfrm.policy_idx_hmask = hmask; 40838c2ecf20Sopenharmony_ci 40848c2ecf20Sopenharmony_ci for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { 40858c2ecf20Sopenharmony_ci struct xfrm_policy_hash *htab; 40868c2ecf20Sopenharmony_ci 40878c2ecf20Sopenharmony_ci net->xfrm.policy_count[dir] = 0; 40888c2ecf20Sopenharmony_ci net->xfrm.policy_count[XFRM_POLICY_MAX + dir] = 0; 40898c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]); 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci htab = &net->xfrm.policy_bydst[dir]; 40928c2ecf20Sopenharmony_ci htab->table = xfrm_hash_alloc(sz); 40938c2ecf20Sopenharmony_ci if (!htab->table) 40948c2ecf20Sopenharmony_ci goto out_bydst; 40958c2ecf20Sopenharmony_ci htab->hmask = hmask; 40968c2ecf20Sopenharmony_ci htab->dbits4 = 32; 40978c2ecf20Sopenharmony_ci htab->sbits4 = 32; 40988c2ecf20Sopenharmony_ci htab->dbits6 = 128; 40998c2ecf20Sopenharmony_ci htab->sbits6 = 128; 41008c2ecf20Sopenharmony_ci } 41018c2ecf20Sopenharmony_ci net->xfrm.policy_hthresh.lbits4 = 32; 41028c2ecf20Sopenharmony_ci net->xfrm.policy_hthresh.rbits4 = 32; 41038c2ecf20Sopenharmony_ci net->xfrm.policy_hthresh.lbits6 = 128; 41048c2ecf20Sopenharmony_ci net->xfrm.policy_hthresh.rbits6 = 128; 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_ci seqlock_init(&net->xfrm.policy_hthresh.lock); 41078c2ecf20Sopenharmony_ci 41088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->xfrm.policy_all); 41098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->xfrm.inexact_bins); 41108c2ecf20Sopenharmony_ci INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); 41118c2ecf20Sopenharmony_ci INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild); 41128c2ecf20Sopenharmony_ci return 0; 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ciout_bydst: 41158c2ecf20Sopenharmony_ci for (dir--; dir >= 0; dir--) { 41168c2ecf20Sopenharmony_ci struct xfrm_policy_hash *htab; 41178c2ecf20Sopenharmony_ci 41188c2ecf20Sopenharmony_ci htab = &net->xfrm.policy_bydst[dir]; 41198c2ecf20Sopenharmony_ci xfrm_hash_free(htab->table, sz); 41208c2ecf20Sopenharmony_ci } 41218c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.policy_byidx, sz); 41228c2ecf20Sopenharmony_ciout_byidx: 41238c2ecf20Sopenharmony_ci return -ENOMEM; 41248c2ecf20Sopenharmony_ci} 41258c2ecf20Sopenharmony_ci 41268c2ecf20Sopenharmony_cistatic void xfrm_policy_fini(struct net *net) 41278c2ecf20Sopenharmony_ci{ 41288c2ecf20Sopenharmony_ci struct xfrm_pol_inexact_bin *b, *t; 41298c2ecf20Sopenharmony_ci unsigned int sz; 41308c2ecf20Sopenharmony_ci int dir; 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci flush_work(&net->xfrm.policy_hash_work); 41338c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_SUB_POLICY 41348c2ecf20Sopenharmony_ci xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false); 41358c2ecf20Sopenharmony_ci#endif 41368c2ecf20Sopenharmony_ci xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false); 41378c2ecf20Sopenharmony_ci 41388c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&net->xfrm.policy_all)); 41398c2ecf20Sopenharmony_ci 41408c2ecf20Sopenharmony_ci for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { 41418c2ecf20Sopenharmony_ci struct xfrm_policy_hash *htab; 41428c2ecf20Sopenharmony_ci 41438c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir])); 41448c2ecf20Sopenharmony_ci 41458c2ecf20Sopenharmony_ci htab = &net->xfrm.policy_bydst[dir]; 41468c2ecf20Sopenharmony_ci sz = (htab->hmask + 1) * sizeof(struct hlist_head); 41478c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(htab->table)); 41488c2ecf20Sopenharmony_ci xfrm_hash_free(htab->table, sz); 41498c2ecf20Sopenharmony_ci } 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_ci sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head); 41528c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(net->xfrm.policy_byidx)); 41538c2ecf20Sopenharmony_ci xfrm_hash_free(net->xfrm.policy_byidx, sz); 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 41568c2ecf20Sopenharmony_ci list_for_each_entry_safe(b, t, &net->xfrm.inexact_bins, inexact_bins) 41578c2ecf20Sopenharmony_ci __xfrm_policy_inexact_prune_bin(b, true); 41588c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 41598c2ecf20Sopenharmony_ci} 41608c2ecf20Sopenharmony_ci 41618c2ecf20Sopenharmony_cistatic int __net_init xfrm_net_init(struct net *net) 41628c2ecf20Sopenharmony_ci{ 41638c2ecf20Sopenharmony_ci int rv; 41648c2ecf20Sopenharmony_ci 41658c2ecf20Sopenharmony_ci /* Initialize the per-net locks here */ 41668c2ecf20Sopenharmony_ci spin_lock_init(&net->xfrm.xfrm_state_lock); 41678c2ecf20Sopenharmony_ci spin_lock_init(&net->xfrm.xfrm_policy_lock); 41688c2ecf20Sopenharmony_ci seqcount_spinlock_init(&net->xfrm.xfrm_policy_hash_generation, &net->xfrm.xfrm_policy_lock); 41698c2ecf20Sopenharmony_ci mutex_init(&net->xfrm.xfrm_cfg_mutex); 41708c2ecf20Sopenharmony_ci net->xfrm.policy_default[XFRM_POLICY_IN] = XFRM_USERPOLICY_ACCEPT; 41718c2ecf20Sopenharmony_ci net->xfrm.policy_default[XFRM_POLICY_FWD] = XFRM_USERPOLICY_ACCEPT; 41728c2ecf20Sopenharmony_ci net->xfrm.policy_default[XFRM_POLICY_OUT] = XFRM_USERPOLICY_ACCEPT; 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci rv = xfrm_statistics_init(net); 41758c2ecf20Sopenharmony_ci if (rv < 0) 41768c2ecf20Sopenharmony_ci goto out_statistics; 41778c2ecf20Sopenharmony_ci rv = xfrm_state_init(net); 41788c2ecf20Sopenharmony_ci if (rv < 0) 41798c2ecf20Sopenharmony_ci goto out_state; 41808c2ecf20Sopenharmony_ci rv = xfrm_policy_init(net); 41818c2ecf20Sopenharmony_ci if (rv < 0) 41828c2ecf20Sopenharmony_ci goto out_policy; 41838c2ecf20Sopenharmony_ci rv = xfrm_sysctl_init(net); 41848c2ecf20Sopenharmony_ci if (rv < 0) 41858c2ecf20Sopenharmony_ci goto out_sysctl; 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci return 0; 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_ciout_sysctl: 41908c2ecf20Sopenharmony_ci xfrm_policy_fini(net); 41918c2ecf20Sopenharmony_ciout_policy: 41928c2ecf20Sopenharmony_ci xfrm_state_fini(net); 41938c2ecf20Sopenharmony_ciout_state: 41948c2ecf20Sopenharmony_ci xfrm_statistics_fini(net); 41958c2ecf20Sopenharmony_ciout_statistics: 41968c2ecf20Sopenharmony_ci return rv; 41978c2ecf20Sopenharmony_ci} 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_cistatic void __net_exit xfrm_net_exit(struct net *net) 42008c2ecf20Sopenharmony_ci{ 42018c2ecf20Sopenharmony_ci xfrm_sysctl_fini(net); 42028c2ecf20Sopenharmony_ci xfrm_policy_fini(net); 42038c2ecf20Sopenharmony_ci xfrm_state_fini(net); 42048c2ecf20Sopenharmony_ci xfrm_statistics_fini(net); 42058c2ecf20Sopenharmony_ci} 42068c2ecf20Sopenharmony_ci 42078c2ecf20Sopenharmony_cistatic struct pernet_operations __net_initdata xfrm_net_ops = { 42088c2ecf20Sopenharmony_ci .init = xfrm_net_init, 42098c2ecf20Sopenharmony_ci .exit = xfrm_net_exit, 42108c2ecf20Sopenharmony_ci}; 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_civoid __init xfrm_init(void) 42138c2ecf20Sopenharmony_ci{ 42148c2ecf20Sopenharmony_ci register_pernet_subsys(&xfrm_net_ops); 42158c2ecf20Sopenharmony_ci xfrm_dev_init(); 42168c2ecf20Sopenharmony_ci xfrm_input_init(); 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_ESPINTCP 42198c2ecf20Sopenharmony_ci espintcp_init(); 42208c2ecf20Sopenharmony_ci#endif 42218c2ecf20Sopenharmony_ci 42228c2ecf20Sopenharmony_ci RCU_INIT_POINTER(xfrm_if_cb, NULL); 42238c2ecf20Sopenharmony_ci synchronize_rcu(); 42248c2ecf20Sopenharmony_ci} 42258c2ecf20Sopenharmony_ci 42268c2ecf20Sopenharmony_ci#ifdef CONFIG_AUDITSYSCALL 42278c2ecf20Sopenharmony_cistatic void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, 42288c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf) 42298c2ecf20Sopenharmony_ci{ 42308c2ecf20Sopenharmony_ci struct xfrm_sec_ctx *ctx = xp->security; 42318c2ecf20Sopenharmony_ci struct xfrm_selector *sel = &xp->selector; 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci if (ctx) 42348c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s", 42358c2ecf20Sopenharmony_ci ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str); 42368c2ecf20Sopenharmony_ci 42378c2ecf20Sopenharmony_ci switch (sel->family) { 42388c2ecf20Sopenharmony_ci case AF_INET: 42398c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4); 42408c2ecf20Sopenharmony_ci if (sel->prefixlen_s != 32) 42418c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src_prefixlen=%d", 42428c2ecf20Sopenharmony_ci sel->prefixlen_s); 42438c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " dst=%pI4", &sel->daddr.a4); 42448c2ecf20Sopenharmony_ci if (sel->prefixlen_d != 32) 42458c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " dst_prefixlen=%d", 42468c2ecf20Sopenharmony_ci sel->prefixlen_d); 42478c2ecf20Sopenharmony_ci break; 42488c2ecf20Sopenharmony_ci case AF_INET6: 42498c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src=%pI6", sel->saddr.a6); 42508c2ecf20Sopenharmony_ci if (sel->prefixlen_s != 128) 42518c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " src_prefixlen=%d", 42528c2ecf20Sopenharmony_ci sel->prefixlen_s); 42538c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " dst=%pI6", sel->daddr.a6); 42548c2ecf20Sopenharmony_ci if (sel->prefixlen_d != 128) 42558c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " dst_prefixlen=%d", 42568c2ecf20Sopenharmony_ci sel->prefixlen_d); 42578c2ecf20Sopenharmony_ci break; 42588c2ecf20Sopenharmony_ci } 42598c2ecf20Sopenharmony_ci} 42608c2ecf20Sopenharmony_ci 42618c2ecf20Sopenharmony_civoid xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid) 42628c2ecf20Sopenharmony_ci{ 42638c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 42648c2ecf20Sopenharmony_ci 42658c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SPD-add"); 42668c2ecf20Sopenharmony_ci if (audit_buf == NULL) 42678c2ecf20Sopenharmony_ci return; 42688c2ecf20Sopenharmony_ci xfrm_audit_helper_usrinfo(task_valid, audit_buf); 42698c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " res=%u", result); 42708c2ecf20Sopenharmony_ci xfrm_audit_common_policyinfo(xp, audit_buf); 42718c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 42728c2ecf20Sopenharmony_ci} 42738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_policy_add); 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_civoid xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, 42768c2ecf20Sopenharmony_ci bool task_valid) 42778c2ecf20Sopenharmony_ci{ 42788c2ecf20Sopenharmony_ci struct audit_buffer *audit_buf; 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci audit_buf = xfrm_audit_start("SPD-delete"); 42818c2ecf20Sopenharmony_ci if (audit_buf == NULL) 42828c2ecf20Sopenharmony_ci return; 42838c2ecf20Sopenharmony_ci xfrm_audit_helper_usrinfo(task_valid, audit_buf); 42848c2ecf20Sopenharmony_ci audit_log_format(audit_buf, " res=%u", result); 42858c2ecf20Sopenharmony_ci xfrm_audit_common_policyinfo(xp, audit_buf); 42868c2ecf20Sopenharmony_ci audit_log_end(audit_buf); 42878c2ecf20Sopenharmony_ci} 42888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_audit_policy_delete); 42898c2ecf20Sopenharmony_ci#endif 42908c2ecf20Sopenharmony_ci 42918c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_MIGRATE 42928c2ecf20Sopenharmony_cistatic bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp, 42938c2ecf20Sopenharmony_ci const struct xfrm_selector *sel_tgt) 42948c2ecf20Sopenharmony_ci{ 42958c2ecf20Sopenharmony_ci if (sel_cmp->proto == IPSEC_ULPROTO_ANY) { 42968c2ecf20Sopenharmony_ci if (sel_tgt->family == sel_cmp->family && 42978c2ecf20Sopenharmony_ci xfrm_addr_equal(&sel_tgt->daddr, &sel_cmp->daddr, 42988c2ecf20Sopenharmony_ci sel_cmp->family) && 42998c2ecf20Sopenharmony_ci xfrm_addr_equal(&sel_tgt->saddr, &sel_cmp->saddr, 43008c2ecf20Sopenharmony_ci sel_cmp->family) && 43018c2ecf20Sopenharmony_ci sel_tgt->prefixlen_d == sel_cmp->prefixlen_d && 43028c2ecf20Sopenharmony_ci sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) { 43038c2ecf20Sopenharmony_ci return true; 43048c2ecf20Sopenharmony_ci } 43058c2ecf20Sopenharmony_ci } else { 43068c2ecf20Sopenharmony_ci if (memcmp(sel_tgt, sel_cmp, sizeof(*sel_tgt)) == 0) { 43078c2ecf20Sopenharmony_ci return true; 43088c2ecf20Sopenharmony_ci } 43098c2ecf20Sopenharmony_ci } 43108c2ecf20Sopenharmony_ci return false; 43118c2ecf20Sopenharmony_ci} 43128c2ecf20Sopenharmony_ci 43138c2ecf20Sopenharmony_cistatic struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel, 43148c2ecf20Sopenharmony_ci u8 dir, u8 type, struct net *net, u32 if_id) 43158c2ecf20Sopenharmony_ci{ 43168c2ecf20Sopenharmony_ci struct xfrm_policy *pol, *ret = NULL; 43178c2ecf20Sopenharmony_ci struct hlist_head *chain; 43188c2ecf20Sopenharmony_ci u32 priority = ~0U; 43198c2ecf20Sopenharmony_ci 43208c2ecf20Sopenharmony_ci spin_lock_bh(&net->xfrm.xfrm_policy_lock); 43218c2ecf20Sopenharmony_ci chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir); 43228c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst) { 43238c2ecf20Sopenharmony_ci if ((if_id == 0 || pol->if_id == if_id) && 43248c2ecf20Sopenharmony_ci xfrm_migrate_selector_match(sel, &pol->selector) && 43258c2ecf20Sopenharmony_ci pol->type == type) { 43268c2ecf20Sopenharmony_ci ret = pol; 43278c2ecf20Sopenharmony_ci priority = ret->priority; 43288c2ecf20Sopenharmony_ci break; 43298c2ecf20Sopenharmony_ci } 43308c2ecf20Sopenharmony_ci } 43318c2ecf20Sopenharmony_ci chain = &net->xfrm.policy_inexact[dir]; 43328c2ecf20Sopenharmony_ci hlist_for_each_entry(pol, chain, bydst_inexact_list) { 43338c2ecf20Sopenharmony_ci if ((pol->priority >= priority) && ret) 43348c2ecf20Sopenharmony_ci break; 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci if ((if_id == 0 || pol->if_id == if_id) && 43378c2ecf20Sopenharmony_ci xfrm_migrate_selector_match(sel, &pol->selector) && 43388c2ecf20Sopenharmony_ci pol->type == type) { 43398c2ecf20Sopenharmony_ci ret = pol; 43408c2ecf20Sopenharmony_ci break; 43418c2ecf20Sopenharmony_ci } 43428c2ecf20Sopenharmony_ci } 43438c2ecf20Sopenharmony_ci 43448c2ecf20Sopenharmony_ci xfrm_pol_hold(ret); 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_ci spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 43478c2ecf20Sopenharmony_ci 43488c2ecf20Sopenharmony_ci return ret; 43498c2ecf20Sopenharmony_ci} 43508c2ecf20Sopenharmony_ci 43518c2ecf20Sopenharmony_cistatic int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tmpl *t) 43528c2ecf20Sopenharmony_ci{ 43538c2ecf20Sopenharmony_ci int match = 0; 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_ci if (t->mode == m->mode && t->id.proto == m->proto && 43568c2ecf20Sopenharmony_ci (m->reqid == 0 || t->reqid == m->reqid)) { 43578c2ecf20Sopenharmony_ci switch (t->mode) { 43588c2ecf20Sopenharmony_ci case XFRM_MODE_TUNNEL: 43598c2ecf20Sopenharmony_ci case XFRM_MODE_BEET: 43608c2ecf20Sopenharmony_ci if (xfrm_addr_equal(&t->id.daddr, &m->old_daddr, 43618c2ecf20Sopenharmony_ci m->old_family) && 43628c2ecf20Sopenharmony_ci xfrm_addr_equal(&t->saddr, &m->old_saddr, 43638c2ecf20Sopenharmony_ci m->old_family)) { 43648c2ecf20Sopenharmony_ci match = 1; 43658c2ecf20Sopenharmony_ci } 43668c2ecf20Sopenharmony_ci break; 43678c2ecf20Sopenharmony_ci case XFRM_MODE_TRANSPORT: 43688c2ecf20Sopenharmony_ci /* in case of transport mode, template does not store 43698c2ecf20Sopenharmony_ci any IP addresses, hence we just compare mode and 43708c2ecf20Sopenharmony_ci protocol */ 43718c2ecf20Sopenharmony_ci match = 1; 43728c2ecf20Sopenharmony_ci break; 43738c2ecf20Sopenharmony_ci default: 43748c2ecf20Sopenharmony_ci break; 43758c2ecf20Sopenharmony_ci } 43768c2ecf20Sopenharmony_ci } 43778c2ecf20Sopenharmony_ci return match; 43788c2ecf20Sopenharmony_ci} 43798c2ecf20Sopenharmony_ci 43808c2ecf20Sopenharmony_ci/* update endpoint address(es) of template(s) */ 43818c2ecf20Sopenharmony_cistatic int xfrm_policy_migrate(struct xfrm_policy *pol, 43828c2ecf20Sopenharmony_ci struct xfrm_migrate *m, int num_migrate) 43838c2ecf20Sopenharmony_ci{ 43848c2ecf20Sopenharmony_ci struct xfrm_migrate *mp; 43858c2ecf20Sopenharmony_ci int i, j, n = 0; 43868c2ecf20Sopenharmony_ci 43878c2ecf20Sopenharmony_ci write_lock_bh(&pol->lock); 43888c2ecf20Sopenharmony_ci if (unlikely(pol->walk.dead)) { 43898c2ecf20Sopenharmony_ci /* target policy has been deleted */ 43908c2ecf20Sopenharmony_ci write_unlock_bh(&pol->lock); 43918c2ecf20Sopenharmony_ci return -ENOENT; 43928c2ecf20Sopenharmony_ci } 43938c2ecf20Sopenharmony_ci 43948c2ecf20Sopenharmony_ci for (i = 0; i < pol->xfrm_nr; i++) { 43958c2ecf20Sopenharmony_ci for (j = 0, mp = m; j < num_migrate; j++, mp++) { 43968c2ecf20Sopenharmony_ci if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i])) 43978c2ecf20Sopenharmony_ci continue; 43988c2ecf20Sopenharmony_ci n++; 43998c2ecf20Sopenharmony_ci if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL && 44008c2ecf20Sopenharmony_ci pol->xfrm_vec[i].mode != XFRM_MODE_BEET) 44018c2ecf20Sopenharmony_ci continue; 44028c2ecf20Sopenharmony_ci /* update endpoints */ 44038c2ecf20Sopenharmony_ci memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr, 44048c2ecf20Sopenharmony_ci sizeof(pol->xfrm_vec[i].id.daddr)); 44058c2ecf20Sopenharmony_ci memcpy(&pol->xfrm_vec[i].saddr, &mp->new_saddr, 44068c2ecf20Sopenharmony_ci sizeof(pol->xfrm_vec[i].saddr)); 44078c2ecf20Sopenharmony_ci pol->xfrm_vec[i].encap_family = mp->new_family; 44088c2ecf20Sopenharmony_ci /* flush bundles */ 44098c2ecf20Sopenharmony_ci atomic_inc(&pol->genid); 44108c2ecf20Sopenharmony_ci } 44118c2ecf20Sopenharmony_ci } 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci write_unlock_bh(&pol->lock); 44148c2ecf20Sopenharmony_ci 44158c2ecf20Sopenharmony_ci if (!n) 44168c2ecf20Sopenharmony_ci return -ENODATA; 44178c2ecf20Sopenharmony_ci 44188c2ecf20Sopenharmony_ci return 0; 44198c2ecf20Sopenharmony_ci} 44208c2ecf20Sopenharmony_ci 44218c2ecf20Sopenharmony_cistatic int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) 44228c2ecf20Sopenharmony_ci{ 44238c2ecf20Sopenharmony_ci int i, j; 44248c2ecf20Sopenharmony_ci 44258c2ecf20Sopenharmony_ci if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) 44268c2ecf20Sopenharmony_ci return -EINVAL; 44278c2ecf20Sopenharmony_ci 44288c2ecf20Sopenharmony_ci for (i = 0; i < num_migrate; i++) { 44298c2ecf20Sopenharmony_ci if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) || 44308c2ecf20Sopenharmony_ci xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) 44318c2ecf20Sopenharmony_ci return -EINVAL; 44328c2ecf20Sopenharmony_ci 44338c2ecf20Sopenharmony_ci /* check if there is any duplicated entry */ 44348c2ecf20Sopenharmony_ci for (j = i + 1; j < num_migrate; j++) { 44358c2ecf20Sopenharmony_ci if (!memcmp(&m[i].old_daddr, &m[j].old_daddr, 44368c2ecf20Sopenharmony_ci sizeof(m[i].old_daddr)) && 44378c2ecf20Sopenharmony_ci !memcmp(&m[i].old_saddr, &m[j].old_saddr, 44388c2ecf20Sopenharmony_ci sizeof(m[i].old_saddr)) && 44398c2ecf20Sopenharmony_ci m[i].proto == m[j].proto && 44408c2ecf20Sopenharmony_ci m[i].mode == m[j].mode && 44418c2ecf20Sopenharmony_ci m[i].reqid == m[j].reqid && 44428c2ecf20Sopenharmony_ci m[i].old_family == m[j].old_family) 44438c2ecf20Sopenharmony_ci return -EINVAL; 44448c2ecf20Sopenharmony_ci } 44458c2ecf20Sopenharmony_ci } 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_ci return 0; 44488c2ecf20Sopenharmony_ci} 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_ciint xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 44518c2ecf20Sopenharmony_ci struct xfrm_migrate *m, int num_migrate, 44528c2ecf20Sopenharmony_ci struct xfrm_kmaddress *k, struct net *net, 44538c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap, u32 if_id) 44548c2ecf20Sopenharmony_ci{ 44558c2ecf20Sopenharmony_ci int i, err, nx_cur = 0, nx_new = 0; 44568c2ecf20Sopenharmony_ci struct xfrm_policy *pol = NULL; 44578c2ecf20Sopenharmony_ci struct xfrm_state *x, *xc; 44588c2ecf20Sopenharmony_ci struct xfrm_state *x_cur[XFRM_MAX_DEPTH]; 44598c2ecf20Sopenharmony_ci struct xfrm_state *x_new[XFRM_MAX_DEPTH]; 44608c2ecf20Sopenharmony_ci struct xfrm_migrate *mp; 44618c2ecf20Sopenharmony_ci 44628c2ecf20Sopenharmony_ci /* Stage 0 - sanity checks */ 44638c2ecf20Sopenharmony_ci if ((err = xfrm_migrate_check(m, num_migrate)) < 0) 44648c2ecf20Sopenharmony_ci goto out; 44658c2ecf20Sopenharmony_ci 44668c2ecf20Sopenharmony_ci if (dir >= XFRM_POLICY_MAX) { 44678c2ecf20Sopenharmony_ci err = -EINVAL; 44688c2ecf20Sopenharmony_ci goto out; 44698c2ecf20Sopenharmony_ci } 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci /* Stage 1 - find policy */ 44728c2ecf20Sopenharmony_ci if ((pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id)) == NULL) { 44738c2ecf20Sopenharmony_ci err = -ENOENT; 44748c2ecf20Sopenharmony_ci goto out; 44758c2ecf20Sopenharmony_ci } 44768c2ecf20Sopenharmony_ci 44778c2ecf20Sopenharmony_ci /* Stage 2 - find and update state(s) */ 44788c2ecf20Sopenharmony_ci for (i = 0, mp = m; i < num_migrate; i++, mp++) { 44798c2ecf20Sopenharmony_ci if ((x = xfrm_migrate_state_find(mp, net, if_id))) { 44808c2ecf20Sopenharmony_ci x_cur[nx_cur] = x; 44818c2ecf20Sopenharmony_ci nx_cur++; 44828c2ecf20Sopenharmony_ci xc = xfrm_state_migrate(x, mp, encap); 44838c2ecf20Sopenharmony_ci if (xc) { 44848c2ecf20Sopenharmony_ci x_new[nx_new] = xc; 44858c2ecf20Sopenharmony_ci nx_new++; 44868c2ecf20Sopenharmony_ci } else { 44878c2ecf20Sopenharmony_ci err = -ENODATA; 44888c2ecf20Sopenharmony_ci goto restore_state; 44898c2ecf20Sopenharmony_ci } 44908c2ecf20Sopenharmony_ci } 44918c2ecf20Sopenharmony_ci } 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci /* Stage 3 - update policy */ 44948c2ecf20Sopenharmony_ci if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0) 44958c2ecf20Sopenharmony_ci goto restore_state; 44968c2ecf20Sopenharmony_ci 44978c2ecf20Sopenharmony_ci /* Stage 4 - delete old state(s) */ 44988c2ecf20Sopenharmony_ci if (nx_cur) { 44998c2ecf20Sopenharmony_ci xfrm_states_put(x_cur, nx_cur); 45008c2ecf20Sopenharmony_ci xfrm_states_delete(x_cur, nx_cur); 45018c2ecf20Sopenharmony_ci } 45028c2ecf20Sopenharmony_ci 45038c2ecf20Sopenharmony_ci /* Stage 5 - announce */ 45048c2ecf20Sopenharmony_ci km_migrate(sel, dir, type, m, num_migrate, k, encap); 45058c2ecf20Sopenharmony_ci 45068c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 45078c2ecf20Sopenharmony_ci 45088c2ecf20Sopenharmony_ci return 0; 45098c2ecf20Sopenharmony_ciout: 45108c2ecf20Sopenharmony_ci return err; 45118c2ecf20Sopenharmony_ci 45128c2ecf20Sopenharmony_cirestore_state: 45138c2ecf20Sopenharmony_ci if (pol) 45148c2ecf20Sopenharmony_ci xfrm_pol_put(pol); 45158c2ecf20Sopenharmony_ci if (nx_cur) 45168c2ecf20Sopenharmony_ci xfrm_states_put(x_cur, nx_cur); 45178c2ecf20Sopenharmony_ci if (nx_new) 45188c2ecf20Sopenharmony_ci xfrm_states_delete(x_new, nx_new); 45198c2ecf20Sopenharmony_ci 45208c2ecf20Sopenharmony_ci return err; 45218c2ecf20Sopenharmony_ci} 45228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_migrate); 45238c2ecf20Sopenharmony_ci#endif 4524