17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5bffb04cfSmarkfen * Common Development and Distribution License (the "License"). 6bffb04cfSmarkfen * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 224ba231ceSKacheong Poon * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * IPsec Security Policy Database. 287c478bd9Sstevel@tonic-gate * 297c478bd9Sstevel@tonic-gate * This module maintains the SPD and provides routines used by ip and ip6 307c478bd9Sstevel@tonic-gate * to apply IPsec policy to inbound and outbound datagrams. 317c478bd9Sstevel@tonic-gate */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <sys/stream.h> 357c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 367c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 377c478bd9Sstevel@tonic-gate #include <sys/strsubr.h> 385d3b8cb7SBill Sommerfeld #include <sys/strsun.h> 397c478bd9Sstevel@tonic-gate #include <sys/strlog.h> 40bd670b35SErik Nordmark #include <sys/strsun.h> 417c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 427c478bd9Sstevel@tonic-gate #include <sys/zone.h> 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate #include <sys/systm.h> 457c478bd9Sstevel@tonic-gate #include <sys/param.h> 467c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 478810c16bSdanmcd #include <sys/ddi.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include <sys/crypto/api.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #include <inet/common.h> 527c478bd9Sstevel@tonic-gate #include <inet/mi.h> 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate #include <netinet/ip6.h> 557c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h> 567c478bd9Sstevel@tonic-gate #include <netinet/udp.h> 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate #include <inet/ip.h> 597c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h> 627c478bd9Sstevel@tonic-gate #include <net/pfpolicy.h> 637c478bd9Sstevel@tonic-gate #include <inet/sadb.h> 647c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h> 6532350c00Sdanmcd 6632350c00Sdanmcd #include <inet/ip_impl.h> /* For IP_MOD_ID */ 6732350c00Sdanmcd 687c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h> 697c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h> 707c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h> 717c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h> 722b24ab6bSSebastien Roy #include <inet/iptun.h> 732b24ab6bSSebastien Roy #include <inet/iptun/iptun_impl.h> 747c478bd9Sstevel@tonic-gate 75f4b3ec61Sdh155122 static void ipsec_update_present_flags(ipsec_stack_t *); 76f4b3ec61Sdh155122 static ipsec_act_t *ipsec_act_wildcard_expand(ipsec_act_t *, uint_t *, 77f4b3ec61Sdh155122 netstack_t *); 7832350c00Sdanmcd static mblk_t *ipsec_check_ipsecin_policy(mblk_t *, ipsec_policy_t *, 79bd670b35SErik Nordmark ipha_t *, ip6_t *, uint64_t, ip_recv_attr_t *, netstack_t *); 80f4b3ec61Sdh155122 static void ipsec_action_free_table(ipsec_action_t *); 817c478bd9Sstevel@tonic-gate static void ipsec_action_reclaim(void *); 824ba231ceSKacheong Poon static void ipsec_action_reclaim_stack(ipsec_stack_t *); 83f4b3ec61Sdh155122 static void ipsid_init(netstack_t *); 84f4b3ec61Sdh155122 static void ipsid_fini(netstack_t *); 858810c16bSdanmcd 868810c16bSdanmcd /* sel_flags values for ipsec_init_inbound_sel(). */ 878810c16bSdanmcd #define SEL_NONE 0x0000 888810c16bSdanmcd #define SEL_PORT_POLICY 0x0001 898810c16bSdanmcd #define SEL_IS_ICMP 0x0002 908810c16bSdanmcd #define SEL_TUNNEL_MODE 0x0004 912b24ab6bSSebastien Roy #define SEL_POST_FRAG 0x0008 928810c16bSdanmcd 938810c16bSdanmcd /* Return values for ipsec_init_inbound_sel(). */ 948810c16bSdanmcd typedef enum { SELRET_NOMEM, SELRET_BADPKT, SELRET_SUCCESS, SELRET_TUNFRAG} 958810c16bSdanmcd selret_t; 968810c16bSdanmcd 978810c16bSdanmcd static selret_t ipsec_init_inbound_sel(ipsec_selector_t *, mblk_t *, 988810c16bSdanmcd ipha_t *, ip6_t *, uint8_t); 998810c16bSdanmcd 100bd670b35SErik Nordmark static boolean_t ipsec_check_ipsecin_action(ip_recv_attr_t *, mblk_t *, 1017c478bd9Sstevel@tonic-gate struct ipsec_action_s *, ipha_t *ipha, ip6_t *ip6h, const char **, 102bd670b35SErik Nordmark kstat_named_t **, netstack_t *); 1037c478bd9Sstevel@tonic-gate static void ipsec_unregister_prov_update(void); 104f4b3ec61Sdh155122 static void ipsec_prov_update_callback_stack(uint32_t, void *, netstack_t *); 1056a182920Ssommerfe static boolean_t ipsec_compare_action(ipsec_policy_t *, ipsec_policy_t *); 1068810c16bSdanmcd static uint32_t selector_hash(ipsec_selector_t *, ipsec_policy_root_t *); 107f4b3ec61Sdh155122 static boolean_t ipsec_kstat_init(ipsec_stack_t *); 108f4b3ec61Sdh155122 static void ipsec_kstat_destroy(ipsec_stack_t *); 109f4b3ec61Sdh155122 static int ipsec_free_tables(ipsec_stack_t *); 1108810c16bSdanmcd static int tunnel_compare(const void *, const void *); 1118810c16bSdanmcd static void ipsec_freemsg_chain(mblk_t *); 112bd670b35SErik Nordmark static void ip_drop_packet_chain(mblk_t *, boolean_t, ill_t *, 1138810c16bSdanmcd struct kstat_named *, ipdropper_t *); 114f4b3ec61Sdh155122 static boolean_t ipsec_kstat_init(ipsec_stack_t *); 115f4b3ec61Sdh155122 static void ipsec_kstat_destroy(ipsec_stack_t *); 116f4b3ec61Sdh155122 static int ipsec_free_tables(ipsec_stack_t *); 117f4b3ec61Sdh155122 static int tunnel_compare(const void *, const void *); 118f4b3ec61Sdh155122 static void ipsec_freemsg_chain(mblk_t *); 1196a182920Ssommerfe 1206a182920Ssommerfe /* 1216a182920Ssommerfe * Selector hash table is statically sized at module load time. 1226a182920Ssommerfe * we default to 251 buckets, which is the largest prime number under 255 1236a182920Ssommerfe */ 1246a182920Ssommerfe 1256a182920Ssommerfe #define IPSEC_SPDHASH_DEFAULT 251 1266a182920Ssommerfe 1278810c16bSdanmcd /* SPD hash-size tunable per tunnel. */ 1288810c16bSdanmcd #define TUN_SPDHASH_DEFAULT 5 1298810c16bSdanmcd 130daa41a61Sdanmcd uint32_t ipsec_spd_hashsize; 131daa41a61Sdanmcd uint32_t tun_spd_hashsize; 1328810c16bSdanmcd 1336a182920Ssommerfe #define IPSEC_SEL_NOHASH ((uint32_t)(~0)) 1347c478bd9Sstevel@tonic-gate 135f4b3ec61Sdh155122 /* 136f4b3ec61Sdh155122 * Handle global across all stack instances 137f4b3ec61Sdh155122 */ 138f4b3ec61Sdh155122 static crypto_notify_handle_t prov_update_handle = NULL; 1396a182920Ssommerfe 1407c478bd9Sstevel@tonic-gate static kmem_cache_t *ipsec_action_cache; 1417c478bd9Sstevel@tonic-gate static kmem_cache_t *ipsec_sel_cache; 1427c478bd9Sstevel@tonic-gate static kmem_cache_t *ipsec_pol_cache; 1437c478bd9Sstevel@tonic-gate 1448810c16bSdanmcd /* Frag cache prototypes */ 145bd670b35SErik Nordmark static void ipsec_fragcache_clean(ipsec_fragcache_t *, ipsec_stack_t *); 1468810c16bSdanmcd static ipsec_fragcache_entry_t *fragcache_delentry(int, 147bd670b35SErik Nordmark ipsec_fragcache_entry_t *, ipsec_fragcache_t *, ipsec_stack_t *); 1488810c16bSdanmcd boolean_t ipsec_fragcache_init(ipsec_fragcache_t *); 149bd670b35SErik Nordmark void ipsec_fragcache_uninit(ipsec_fragcache_t *, ipsec_stack_t *ipss); 150bd670b35SErik Nordmark mblk_t *ipsec_fragcache_add(ipsec_fragcache_t *, mblk_t *, mblk_t *, 151bd670b35SErik Nordmark int, ipsec_stack_t *); 1527c478bd9Sstevel@tonic-gate 15307b56925Ssommerfe int ipsec_hdr_pullup_needed = 0; 15407b56925Ssommerfe int ipsec_weird_null_inbound_policy = 0; 15507b56925Ssommerfe 1567c478bd9Sstevel@tonic-gate #define ALGBITS_ROUND_DOWN(x, align) (((x)/(align))*(align)) 1577c478bd9Sstevel@tonic-gate #define ALGBITS_ROUND_UP(x, align) ALGBITS_ROUND_DOWN((x)+(align)-1, align) 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate /* 1607c478bd9Sstevel@tonic-gate * Inbound traffic should have matching identities for both SA's. 1617c478bd9Sstevel@tonic-gate */ 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate #define SA_IDS_MATCH(sa1, sa2) \ 1647c478bd9Sstevel@tonic-gate (((sa1) == NULL) || ((sa2) == NULL) || \ 1657c478bd9Sstevel@tonic-gate (((sa1)->ipsa_src_cid == (sa2)->ipsa_src_cid) && \ 1667c478bd9Sstevel@tonic-gate (((sa1)->ipsa_dst_cid == (sa2)->ipsa_dst_cid)))) 1676a182920Ssommerfe 1688810c16bSdanmcd /* 1698810c16bSdanmcd * IPv6 Fragments 1708810c16bSdanmcd */ 1718810c16bSdanmcd #define IS_V6_FRAGMENT(ipp) (ipp.ipp_fields & IPPF_FRAGHDR) 1726a182920Ssommerfe 1737c478bd9Sstevel@tonic-gate /* 1747c478bd9Sstevel@tonic-gate * Policy failure messages. 1757c478bd9Sstevel@tonic-gate */ 1767c478bd9Sstevel@tonic-gate static char *ipsec_policy_failure_msgs[] = { 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* IPSEC_POLICY_NOT_NEEDED */ 1797c478bd9Sstevel@tonic-gate "%s: Dropping the datagram because the incoming packet " 1807c478bd9Sstevel@tonic-gate "is %s, but the recipient expects clear; Source %s, " 1817c478bd9Sstevel@tonic-gate "Destination %s.\n", 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* IPSEC_POLICY_MISMATCH */ 1847c478bd9Sstevel@tonic-gate "%s: Policy Failure for the incoming packet (%s); Source %s, " 1857c478bd9Sstevel@tonic-gate "Destination %s.\n", 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate /* IPSEC_POLICY_AUTH_NOT_NEEDED */ 1887c478bd9Sstevel@tonic-gate "%s: Authentication present while not expected in the " 1897c478bd9Sstevel@tonic-gate "incoming %s packet; Source %s, Destination %s.\n", 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* IPSEC_POLICY_ENCR_NOT_NEEDED */ 1927c478bd9Sstevel@tonic-gate "%s: Encryption present while not expected in the " 1937c478bd9Sstevel@tonic-gate "incoming %s packet; Source %s, Destination %s.\n", 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate /* IPSEC_POLICY_SE_NOT_NEEDED */ 1967c478bd9Sstevel@tonic-gate "%s: Self-Encapsulation present while not expected in the " 1977c478bd9Sstevel@tonic-gate "incoming %s packet; Source %s, Destination %s.\n", 1987c478bd9Sstevel@tonic-gate }; 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate /* 2017c478bd9Sstevel@tonic-gate * General overviews: 2027c478bd9Sstevel@tonic-gate * 2037c478bd9Sstevel@tonic-gate * Locking: 2047c478bd9Sstevel@tonic-gate * 2057c478bd9Sstevel@tonic-gate * All of the system policy structures are protected by a single 206f4b3ec61Sdh155122 * rwlock. These structures are threaded in a 2077c478bd9Sstevel@tonic-gate * fairly complex fashion and are not expected to change on a 2087c478bd9Sstevel@tonic-gate * regular basis, so this should not cause scaling/contention 2097c478bd9Sstevel@tonic-gate * problems. As a result, policy checks should (hopefully) be MT-hot. 2107c478bd9Sstevel@tonic-gate * 2117c478bd9Sstevel@tonic-gate * Allocation policy: 2127c478bd9Sstevel@tonic-gate * 2137c478bd9Sstevel@tonic-gate * We use custom kmem cache types for the various 2147c478bd9Sstevel@tonic-gate * bits & pieces of the policy data structures. All allocations 2157c478bd9Sstevel@tonic-gate * use KM_NOSLEEP instead of KM_SLEEP for policy allocation. The 2167c478bd9Sstevel@tonic-gate * policy table is of potentially unbounded size, so we don't 2177c478bd9Sstevel@tonic-gate * want to provide a way to hog all system memory with policy 2187c478bd9Sstevel@tonic-gate * entries.. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate 2218810c16bSdanmcd /* Convenient functions for freeing or dropping a b_next linked mblk chain */ 2228810c16bSdanmcd 2238810c16bSdanmcd /* Free all messages in an mblk chain */ 2248810c16bSdanmcd static void 2258810c16bSdanmcd ipsec_freemsg_chain(mblk_t *mp) 2268810c16bSdanmcd { 2278810c16bSdanmcd mblk_t *mpnext; 2288810c16bSdanmcd while (mp != NULL) { 2298810c16bSdanmcd ASSERT(mp->b_prev == NULL); 2308810c16bSdanmcd mpnext = mp->b_next; 2318810c16bSdanmcd mp->b_next = NULL; 232bd670b35SErik Nordmark freemsg(mp); 2338810c16bSdanmcd mp = mpnext; 2348810c16bSdanmcd } 2358810c16bSdanmcd } 2368810c16bSdanmcd 237bd670b35SErik Nordmark /* 238bd670b35SErik Nordmark * ip_drop all messages in an mblk chain 239bd670b35SErik Nordmark * Can handle a b_next chain of ip_recv_attr_t mblks, or just a b_next chain 240bd670b35SErik Nordmark * of data. 241bd670b35SErik Nordmark */ 2428810c16bSdanmcd static void 243bd670b35SErik Nordmark ip_drop_packet_chain(mblk_t *mp, boolean_t inbound, ill_t *ill, 244bd670b35SErik Nordmark struct kstat_named *counter, ipdropper_t *who_called) 2458810c16bSdanmcd { 2468810c16bSdanmcd mblk_t *mpnext; 2478810c16bSdanmcd while (mp != NULL) { 2488810c16bSdanmcd ASSERT(mp->b_prev == NULL); 2498810c16bSdanmcd mpnext = mp->b_next; 2508810c16bSdanmcd mp->b_next = NULL; 251bd670b35SErik Nordmark if (ip_recv_attr_is_mblk(mp)) 252bd670b35SErik Nordmark mp = ip_recv_attr_free_mblk(mp); 253bd670b35SErik Nordmark ip_drop_packet(mp, inbound, ill, counter, who_called); 2548810c16bSdanmcd mp = mpnext; 2558810c16bSdanmcd } 2568810c16bSdanmcd } 2576a182920Ssommerfe 2586a182920Ssommerfe /* 2596a182920Ssommerfe * AVL tree comparison function. 2606a182920Ssommerfe * the in-kernel avl assumes unique keys for all objects. 2616a182920Ssommerfe * Since sometimes policy will duplicate rules, we may insert 2626a182920Ssommerfe * multiple rules with the same rule id, so we need a tie-breaker. 2636a182920Ssommerfe */ 2646a182920Ssommerfe static int 2656a182920Ssommerfe ipsec_policy_cmpbyid(const void *a, const void *b) 2666a182920Ssommerfe { 2676a182920Ssommerfe const ipsec_policy_t *ipa, *ipb; 2686a182920Ssommerfe uint64_t idxa, idxb; 2696a182920Ssommerfe 2706a182920Ssommerfe ipa = (const ipsec_policy_t *)a; 2716a182920Ssommerfe ipb = (const ipsec_policy_t *)b; 2726a182920Ssommerfe idxa = ipa->ipsp_index; 2736a182920Ssommerfe idxb = ipb->ipsp_index; 2746a182920Ssommerfe 2756a182920Ssommerfe if (idxa < idxb) 2766a182920Ssommerfe return (-1); 2776a182920Ssommerfe if (idxa > idxb) 2786a182920Ssommerfe return (1); 2796a182920Ssommerfe /* 2806a182920Ssommerfe * Tie-breaker #1: All installed policy rules have a non-NULL 2816a182920Ssommerfe * ipsl_sel (selector set), so an entry with a NULL ipsp_sel is not 2826a182920Ssommerfe * actually in-tree but rather a template node being used in 2836a182920Ssommerfe * an avl_find query; see ipsec_policy_delete(). This gives us 284bd670b35SErik Nordmark * a placeholder in the ordering just before the first entry with 2856a182920Ssommerfe * a key >= the one we're looking for, so we can walk forward from 2866a182920Ssommerfe * that point to get the remaining entries with the same id. 2876a182920Ssommerfe */ 2886a182920Ssommerfe if ((ipa->ipsp_sel == NULL) && (ipb->ipsp_sel != NULL)) 2896a182920Ssommerfe return (-1); 2906a182920Ssommerfe if ((ipb->ipsp_sel == NULL) && (ipa->ipsp_sel != NULL)) 2916a182920Ssommerfe return (1); 2926a182920Ssommerfe /* 2936a182920Ssommerfe * At most one of the arguments to the comparison should have a 2946a182920Ssommerfe * NULL selector pointer; if not, the tree is broken. 2956a182920Ssommerfe */ 2966a182920Ssommerfe ASSERT(ipa->ipsp_sel != NULL); 2976a182920Ssommerfe ASSERT(ipb->ipsp_sel != NULL); 2986a182920Ssommerfe /* 2996a182920Ssommerfe * Tie-breaker #2: use the virtual address of the policy node 3006a182920Ssommerfe * to arbitrarily break ties. Since we use the new tree node in 3016a182920Ssommerfe * the avl_find() in ipsec_insert_always, the new node will be 3026a182920Ssommerfe * inserted into the tree in the right place in the sequence. 3036a182920Ssommerfe */ 3046a182920Ssommerfe if (ipa < ipb) 3056a182920Ssommerfe return (-1); 3066a182920Ssommerfe if (ipa > ipb) 3076a182920Ssommerfe return (1); 3086a182920Ssommerfe return (0); 3096a182920Ssommerfe } 3106a182920Ssommerfe 311f4b3ec61Sdh155122 /* 312f4b3ec61Sdh155122 * Free what ipsec_alloc_table allocated. 313f4b3ec61Sdh155122 */ 3148810c16bSdanmcd void 3156a182920Ssommerfe ipsec_polhead_free_table(ipsec_policy_head_t *iph) 3166a182920Ssommerfe { 3178810c16bSdanmcd int dir; 318f4b3ec61Sdh155122 int i; 3196a182920Ssommerfe 3206a182920Ssommerfe for (dir = 0; dir < IPSEC_NTYPES; dir++) { 3216a182920Ssommerfe ipsec_policy_root_t *ipr = &iph->iph_root[dir]; 3226a182920Ssommerfe 3236a182920Ssommerfe if (ipr->ipr_hash == NULL) 3246a182920Ssommerfe continue; 3256a182920Ssommerfe 326f4b3ec61Sdh155122 for (i = 0; i < ipr->ipr_nchains; i++) { 327f4b3ec61Sdh155122 ASSERT(ipr->ipr_hash[i].hash_head == NULL); 328f4b3ec61Sdh155122 } 3298810c16bSdanmcd kmem_free(ipr->ipr_hash, ipr->ipr_nchains * 3306a182920Ssommerfe sizeof (ipsec_policy_hash_t)); 331f4b3ec61Sdh155122 ipr->ipr_hash = NULL; 3326a182920Ssommerfe } 3336a182920Ssommerfe } 3346a182920Ssommerfe 3358810c16bSdanmcd void 3366a182920Ssommerfe ipsec_polhead_destroy(ipsec_policy_head_t *iph) 3376a182920Ssommerfe { 3386a182920Ssommerfe int dir; 3396a182920Ssommerfe 3406a182920Ssommerfe avl_destroy(&iph->iph_rulebyid); 3416a182920Ssommerfe rw_destroy(&iph->iph_lock); 3426a182920Ssommerfe 3436a182920Ssommerfe for (dir = 0; dir < IPSEC_NTYPES; dir++) { 3446a182920Ssommerfe ipsec_policy_root_t *ipr = &iph->iph_root[dir]; 3456a182920Ssommerfe int chain; 3466a182920Ssommerfe 3478810c16bSdanmcd for (chain = 0; chain < ipr->ipr_nchains; chain++) 3486a182920Ssommerfe mutex_destroy(&(ipr->ipr_hash[chain].hash_lock)); 3496a182920Ssommerfe 3506a182920Ssommerfe } 3516a182920Ssommerfe ipsec_polhead_free_table(iph); 3526a182920Ssommerfe } 3536a182920Ssommerfe 3547c478bd9Sstevel@tonic-gate /* 355f4b3ec61Sdh155122 * Free the IPsec stack instance. 3567c478bd9Sstevel@tonic-gate */ 357f4b3ec61Sdh155122 /* ARGSUSED */ 358f4b3ec61Sdh155122 static void 359f4b3ec61Sdh155122 ipsec_stack_fini(netstackid_t stackid, void *arg) 3607c478bd9Sstevel@tonic-gate { 361f4b3ec61Sdh155122 ipsec_stack_t *ipss = (ipsec_stack_t *)arg; 3628810c16bSdanmcd void *cookie; 3638810c16bSdanmcd ipsec_tun_pol_t *node; 364f4b3ec61Sdh155122 netstack_t *ns = ipss->ipsec_netstack; 365f4b3ec61Sdh155122 int i; 366f4b3ec61Sdh155122 ipsec_algtype_t algtype; 3677c478bd9Sstevel@tonic-gate 368f4b3ec61Sdh155122 ipsec_loader_destroy(ipss); 3697c478bd9Sstevel@tonic-gate 370f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_WRITER); 3718810c16bSdanmcd /* 3728810c16bSdanmcd * It's possible we can just ASSERT() the tree is empty. After all, 3738810c16bSdanmcd * we aren't called until IP is ready to unload (and presumably all 3748810c16bSdanmcd * tunnels have been unplumbed). But we'll play it safe for now, the 3758810c16bSdanmcd * loop will just exit immediately if it's empty. 3768810c16bSdanmcd */ 3778810c16bSdanmcd cookie = NULL; 3788810c16bSdanmcd while ((node = (ipsec_tun_pol_t *) 379f4b3ec61Sdh155122 avl_destroy_nodes(&ipss->ipsec_tunnel_policies, 380f4b3ec61Sdh155122 &cookie)) != NULL) { 381f4b3ec61Sdh155122 ITP_REFRELE(node, ns); 3828810c16bSdanmcd } 383f4b3ec61Sdh155122 avl_destroy(&ipss->ipsec_tunnel_policies); 384f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 385f4b3ec61Sdh155122 rw_destroy(&ipss->ipsec_tunnel_policy_lock); 3867c478bd9Sstevel@tonic-gate 387f4b3ec61Sdh155122 ipsec_config_flush(ns); 3887c478bd9Sstevel@tonic-gate 389f4b3ec61Sdh155122 ipsec_kstat_destroy(ipss); 3907c478bd9Sstevel@tonic-gate 391f4b3ec61Sdh155122 ip_drop_unregister(&ipss->ipsec_dropper); 3927c478bd9Sstevel@tonic-gate 393f4b3ec61Sdh155122 ip_drop_unregister(&ipss->ipsec_spd_dropper); 394f4b3ec61Sdh155122 ip_drop_destroy(ipss); 395f4b3ec61Sdh155122 /* 396f4b3ec61Sdh155122 * Globals start with ref == 1 to prevent IPPH_REFRELE() from 397f4b3ec61Sdh155122 * attempting to free them, hence they should have 1 now. 398f4b3ec61Sdh155122 */ 399f4b3ec61Sdh155122 ipsec_polhead_destroy(&ipss->ipsec_system_policy); 400f4b3ec61Sdh155122 ASSERT(ipss->ipsec_system_policy.iph_refs == 1); 401f4b3ec61Sdh155122 ipsec_polhead_destroy(&ipss->ipsec_inactive_policy); 402f4b3ec61Sdh155122 ASSERT(ipss->ipsec_inactive_policy.iph_refs == 1); 4037c478bd9Sstevel@tonic-gate 404f4b3ec61Sdh155122 for (i = 0; i < IPSEC_ACTION_HASH_SIZE; i++) { 405f4b3ec61Sdh155122 ipsec_action_free_table(ipss->ipsec_action_hash[i].hash_head); 406f4b3ec61Sdh155122 ipss->ipsec_action_hash[i].hash_head = NULL; 407f4b3ec61Sdh155122 mutex_destroy(&(ipss->ipsec_action_hash[i].hash_lock)); 408f4b3ec61Sdh155122 } 409f4b3ec61Sdh155122 410f4b3ec61Sdh155122 for (i = 0; i < ipss->ipsec_spd_hashsize; i++) { 411f4b3ec61Sdh155122 ASSERT(ipss->ipsec_sel_hash[i].hash_head == NULL); 412f4b3ec61Sdh155122 mutex_destroy(&(ipss->ipsec_sel_hash[i].hash_lock)); 413f4b3ec61Sdh155122 } 414f4b3ec61Sdh155122 415f4b3ec61Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 416f4b3ec61Sdh155122 for (algtype = 0; algtype < IPSEC_NALGTYPES; algtype ++) { 417f4b3ec61Sdh155122 int nalgs = ipss->ipsec_nalgs[algtype]; 418f4b3ec61Sdh155122 419f4b3ec61Sdh155122 for (i = 0; i < nalgs; i++) { 420f4b3ec61Sdh155122 if (ipss->ipsec_alglists[algtype][i] != NULL) 421f4b3ec61Sdh155122 ipsec_alg_unreg(algtype, i, ns); 422f4b3ec61Sdh155122 } 423f4b3ec61Sdh155122 } 424f4b3ec61Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 425f4b3ec61Sdh155122 mutex_destroy(&ipss->ipsec_alg_lock); 426f4b3ec61Sdh155122 427f4b3ec61Sdh155122 ipsid_gc(ns); 428f4b3ec61Sdh155122 ipsid_fini(ns); 429f4b3ec61Sdh155122 430f4b3ec61Sdh155122 (void) ipsec_free_tables(ipss); 431f4b3ec61Sdh155122 kmem_free(ipss, sizeof (*ipss)); 432f4b3ec61Sdh155122 } 433f4b3ec61Sdh155122 434f4b3ec61Sdh155122 void 435f4b3ec61Sdh155122 ipsec_policy_g_destroy(void) 436f4b3ec61Sdh155122 { 4377c478bd9Sstevel@tonic-gate kmem_cache_destroy(ipsec_action_cache); 4387c478bd9Sstevel@tonic-gate kmem_cache_destroy(ipsec_sel_cache); 4397c478bd9Sstevel@tonic-gate kmem_cache_destroy(ipsec_pol_cache); 440f4b3ec61Sdh155122 441f4b3ec61Sdh155122 ipsec_unregister_prov_update(); 442f4b3ec61Sdh155122 443f4b3ec61Sdh155122 netstack_unregister(NS_IPSEC); 4447c478bd9Sstevel@tonic-gate } 4457c478bd9Sstevel@tonic-gate 4466a182920Ssommerfe 4476a182920Ssommerfe /* 448f4b3ec61Sdh155122 * Free what ipsec_alloc_tables allocated. 4496a182920Ssommerfe * Called when table allocation fails to free the table. 4506a182920Ssommerfe */ 4516a182920Ssommerfe static int 452f4b3ec61Sdh155122 ipsec_free_tables(ipsec_stack_t *ipss) 4536a182920Ssommerfe { 454f4b3ec61Sdh155122 int i; 455f4b3ec61Sdh155122 456f4b3ec61Sdh155122 if (ipss->ipsec_sel_hash != NULL) { 457f4b3ec61Sdh155122 for (i = 0; i < ipss->ipsec_spd_hashsize; i++) { 458f4b3ec61Sdh155122 ASSERT(ipss->ipsec_sel_hash[i].hash_head == NULL); 4596a182920Ssommerfe } 460f4b3ec61Sdh155122 kmem_free(ipss->ipsec_sel_hash, ipss->ipsec_spd_hashsize * 461f4b3ec61Sdh155122 sizeof (*ipss->ipsec_sel_hash)); 462f4b3ec61Sdh155122 ipss->ipsec_sel_hash = NULL; 463f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize = 0; 464f4b3ec61Sdh155122 } 465f4b3ec61Sdh155122 ipsec_polhead_free_table(&ipss->ipsec_system_policy); 466f4b3ec61Sdh155122 ipsec_polhead_free_table(&ipss->ipsec_inactive_policy); 4676a182920Ssommerfe 4686a182920Ssommerfe return (ENOMEM); 4696a182920Ssommerfe } 4706a182920Ssommerfe 4716a182920Ssommerfe /* 4726a182920Ssommerfe * Attempt to allocate the tables in a single policy head. 4736a182920Ssommerfe * Return nonzero on failure after cleaning up any work in progress. 4746a182920Ssommerfe */ 4758810c16bSdanmcd int 4768810c16bSdanmcd ipsec_alloc_table(ipsec_policy_head_t *iph, int nchains, int kmflag, 477f4b3ec61Sdh155122 boolean_t global_cleanup, netstack_t *ns) 4786a182920Ssommerfe { 4798810c16bSdanmcd int dir; 4806a182920Ssommerfe 4816a182920Ssommerfe for (dir = 0; dir < IPSEC_NTYPES; dir++) { 4826a182920Ssommerfe ipsec_policy_root_t *ipr = &iph->iph_root[dir]; 4836a182920Ssommerfe 4848810c16bSdanmcd ipr->ipr_nchains = nchains; 4856a182920Ssommerfe ipr->ipr_hash = kmem_zalloc(nchains * 4866a182920Ssommerfe sizeof (ipsec_policy_hash_t), kmflag); 4876a182920Ssommerfe if (ipr->ipr_hash == NULL) 488f4b3ec61Sdh155122 return (global_cleanup ? 489f4b3ec61Sdh155122 ipsec_free_tables(ns->netstack_ipsec) : 4908810c16bSdanmcd ENOMEM); 4916a182920Ssommerfe } 4926a182920Ssommerfe return (0); 4936a182920Ssommerfe } 4946a182920Ssommerfe 4956a182920Ssommerfe /* 4966a182920Ssommerfe * Attempt to allocate the various tables. Return nonzero on failure 4976a182920Ssommerfe * after cleaning up any work in progress. 4986a182920Ssommerfe */ 4996a182920Ssommerfe static int 500f4b3ec61Sdh155122 ipsec_alloc_tables(int kmflag, netstack_t *ns) 5016a182920Ssommerfe { 5026a182920Ssommerfe int error; 503f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 5046a182920Ssommerfe 505f4b3ec61Sdh155122 error = ipsec_alloc_table(&ipss->ipsec_system_policy, 506f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize, kmflag, B_TRUE, ns); 5076a182920Ssommerfe if (error != 0) 5086a182920Ssommerfe return (error); 5096a182920Ssommerfe 510f4b3ec61Sdh155122 error = ipsec_alloc_table(&ipss->ipsec_inactive_policy, 511f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize, kmflag, B_TRUE, ns); 5126a182920Ssommerfe if (error != 0) 5136a182920Ssommerfe return (error); 5146a182920Ssommerfe 515f4b3ec61Sdh155122 ipss->ipsec_sel_hash = kmem_zalloc(ipss->ipsec_spd_hashsize * 516f4b3ec61Sdh155122 sizeof (*ipss->ipsec_sel_hash), kmflag); 5176a182920Ssommerfe 518f4b3ec61Sdh155122 if (ipss->ipsec_sel_hash == NULL) 519f4b3ec61Sdh155122 return (ipsec_free_tables(ipss)); 5206a182920Ssommerfe 5216a182920Ssommerfe return (0); 5226a182920Ssommerfe } 5236a182920Ssommerfe 5246a182920Ssommerfe /* 5256a182920Ssommerfe * After table allocation, initialize a policy head. 5266a182920Ssommerfe */ 5278810c16bSdanmcd void 5288810c16bSdanmcd ipsec_polhead_init(ipsec_policy_head_t *iph, int nchains) 5296a182920Ssommerfe { 5308810c16bSdanmcd int dir, chain; 5316a182920Ssommerfe 5326a182920Ssommerfe rw_init(&iph->iph_lock, NULL, RW_DEFAULT, NULL); 5336a182920Ssommerfe avl_create(&iph->iph_rulebyid, ipsec_policy_cmpbyid, 5346a182920Ssommerfe sizeof (ipsec_policy_t), offsetof(ipsec_policy_t, ipsp_byid)); 5356a182920Ssommerfe 5366a182920Ssommerfe for (dir = 0; dir < IPSEC_NTYPES; dir++) { 5376a182920Ssommerfe ipsec_policy_root_t *ipr = &iph->iph_root[dir]; 5386a182920Ssommerfe ipr->ipr_nchains = nchains; 5396a182920Ssommerfe 5406a182920Ssommerfe for (chain = 0; chain < nchains; chain++) { 5416a182920Ssommerfe mutex_init(&(ipr->ipr_hash[chain].hash_lock), 5426a182920Ssommerfe NULL, MUTEX_DEFAULT, NULL); 5436a182920Ssommerfe } 5446a182920Ssommerfe } 5456a182920Ssommerfe } 5466a182920Ssommerfe 547f4b3ec61Sdh155122 static boolean_t 548f4b3ec61Sdh155122 ipsec_kstat_init(ipsec_stack_t *ipss) 5497c478bd9Sstevel@tonic-gate { 550f4b3ec61Sdh155122 ipss->ipsec_ksp = kstat_create_netstack("ip", 0, "ipsec_stat", "net", 551f4b3ec61Sdh155122 KSTAT_TYPE_NAMED, sizeof (ipsec_kstats_t) / sizeof (kstat_named_t), 552f4b3ec61Sdh155122 KSTAT_FLAG_PERSISTENT, ipss->ipsec_netstack->netstack_stackid); 553f4b3ec61Sdh155122 554f4b3ec61Sdh155122 if (ipss->ipsec_ksp == NULL || ipss->ipsec_ksp->ks_data == NULL) 555f4b3ec61Sdh155122 return (B_FALSE); 556f4b3ec61Sdh155122 557f4b3ec61Sdh155122 ipss->ipsec_kstats = ipss->ipsec_ksp->ks_data; 558f4b3ec61Sdh155122 559f4b3ec61Sdh155122 #define KI(x) kstat_named_init(&ipss->ipsec_kstats->x, #x, KSTAT_DATA_UINT64) 560f4b3ec61Sdh155122 KI(esp_stat_in_requests); 561f4b3ec61Sdh155122 KI(esp_stat_in_discards); 562f4b3ec61Sdh155122 KI(esp_stat_lookup_failure); 563f4b3ec61Sdh155122 KI(ah_stat_in_requests); 564f4b3ec61Sdh155122 KI(ah_stat_in_discards); 565f4b3ec61Sdh155122 KI(ah_stat_lookup_failure); 566f4b3ec61Sdh155122 KI(sadb_acquire_maxpackets); 567f4b3ec61Sdh155122 KI(sadb_acquire_qhiwater); 568f4b3ec61Sdh155122 #undef KI 569f4b3ec61Sdh155122 570f4b3ec61Sdh155122 kstat_install(ipss->ipsec_ksp); 571f4b3ec61Sdh155122 return (B_TRUE); 572f4b3ec61Sdh155122 } 573f4b3ec61Sdh155122 574f4b3ec61Sdh155122 static void 575f4b3ec61Sdh155122 ipsec_kstat_destroy(ipsec_stack_t *ipss) 576f4b3ec61Sdh155122 { 577f4b3ec61Sdh155122 kstat_delete_netstack(ipss->ipsec_ksp, 578f4b3ec61Sdh155122 ipss->ipsec_netstack->netstack_stackid); 579f4b3ec61Sdh155122 ipss->ipsec_kstats = NULL; 580f4b3ec61Sdh155122 581f4b3ec61Sdh155122 } 582f4b3ec61Sdh155122 583f4b3ec61Sdh155122 /* 584f4b3ec61Sdh155122 * Initialize the IPsec stack instance. 585f4b3ec61Sdh155122 */ 586f4b3ec61Sdh155122 /* ARGSUSED */ 587f4b3ec61Sdh155122 static void * 588f4b3ec61Sdh155122 ipsec_stack_init(netstackid_t stackid, netstack_t *ns) 589f4b3ec61Sdh155122 { 590f4b3ec61Sdh155122 ipsec_stack_t *ipss; 5917c478bd9Sstevel@tonic-gate int i; 5927c478bd9Sstevel@tonic-gate 593f4b3ec61Sdh155122 ipss = (ipsec_stack_t *)kmem_zalloc(sizeof (*ipss), KM_SLEEP); 594f4b3ec61Sdh155122 ipss->ipsec_netstack = ns; 595f4b3ec61Sdh155122 596f4b3ec61Sdh155122 /* 597f4b3ec61Sdh155122 * FIXME: netstack_ipsec is used by some of the routines we call 598f4b3ec61Sdh155122 * below, but it isn't set until this routine returns. 599f4b3ec61Sdh155122 * Either we introduce optional xxx_stack_alloc() functions 600f4b3ec61Sdh155122 * that will be called by the netstack framework before xxx_stack_init, 601f4b3ec61Sdh155122 * or we switch spd.c and sadb.c to operate on ipsec_stack_t 602f4b3ec61Sdh155122 * (latter has some include file order issues for sadb.h, but makes 603f4b3ec61Sdh155122 * sense if we merge some of the ipsec related stack_t's together. 604f4b3ec61Sdh155122 */ 605f4b3ec61Sdh155122 ns->netstack_ipsec = ipss; 606f4b3ec61Sdh155122 6076a182920Ssommerfe /* 6086a182920Ssommerfe * Make two attempts to allocate policy hash tables; try it at 6096a182920Ssommerfe * the "preferred" size (may be set in /etc/system) first, 6106a182920Ssommerfe * then fall back to the default size. 6116a182920Ssommerfe */ 612daa41a61Sdanmcd ipss->ipsec_spd_hashsize = (ipsec_spd_hashsize == 0) ? 613daa41a61Sdanmcd IPSEC_SPDHASH_DEFAULT : ipsec_spd_hashsize; 6146a182920Ssommerfe 615f4b3ec61Sdh155122 if (ipsec_alloc_tables(KM_NOSLEEP, ns) != 0) { 6166a182920Ssommerfe cmn_err(CE_WARN, 6176a182920Ssommerfe "Unable to allocate %d entry IPsec policy hash table", 618f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize); 619f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize = IPSEC_SPDHASH_DEFAULT; 6206a182920Ssommerfe cmn_err(CE_WARN, "Falling back to %d entries", 621f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize); 622f4b3ec61Sdh155122 (void) ipsec_alloc_tables(KM_SLEEP, ns); 6236a182920Ssommerfe } 6246a182920Ssommerfe 6258810c16bSdanmcd /* Just set a default for tunnels. */ 626daa41a61Sdanmcd ipss->ipsec_tun_spd_hashsize = (tun_spd_hashsize == 0) ? 627daa41a61Sdanmcd TUN_SPDHASH_DEFAULT : tun_spd_hashsize; 6288810c16bSdanmcd 629f4b3ec61Sdh155122 ipsid_init(ns); 6308810c16bSdanmcd /* 6318810c16bSdanmcd * Globals need ref == 1 to prevent IPPH_REFRELE() from attempting 6328810c16bSdanmcd * to free them. 6338810c16bSdanmcd */ 634f4b3ec61Sdh155122 ipss->ipsec_system_policy.iph_refs = 1; 635f4b3ec61Sdh155122 ipss->ipsec_inactive_policy.iph_refs = 1; 636f4b3ec61Sdh155122 ipsec_polhead_init(&ipss->ipsec_system_policy, 637f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize); 638f4b3ec61Sdh155122 ipsec_polhead_init(&ipss->ipsec_inactive_policy, 639f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize); 640f4b3ec61Sdh155122 rw_init(&ipss->ipsec_tunnel_policy_lock, NULL, RW_DEFAULT, NULL); 641f4b3ec61Sdh155122 avl_create(&ipss->ipsec_tunnel_policies, tunnel_compare, 642f4b3ec61Sdh155122 sizeof (ipsec_tun_pol_t), 0); 643f4b3ec61Sdh155122 644f4b3ec61Sdh155122 ipss->ipsec_next_policy_index = 1; 645f4b3ec61Sdh155122 646f4b3ec61Sdh155122 rw_init(&ipss->ipsec_system_policy.iph_lock, NULL, RW_DEFAULT, NULL); 647f4b3ec61Sdh155122 rw_init(&ipss->ipsec_inactive_policy.iph_lock, NULL, RW_DEFAULT, NULL); 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate for (i = 0; i < IPSEC_ACTION_HASH_SIZE; i++) 650f4b3ec61Sdh155122 mutex_init(&(ipss->ipsec_action_hash[i].hash_lock), 6517c478bd9Sstevel@tonic-gate NULL, MUTEX_DEFAULT, NULL); 6527c478bd9Sstevel@tonic-gate 653f4b3ec61Sdh155122 for (i = 0; i < ipss->ipsec_spd_hashsize; i++) 654f4b3ec61Sdh155122 mutex_init(&(ipss->ipsec_sel_hash[i].hash_lock), 6557c478bd9Sstevel@tonic-gate NULL, MUTEX_DEFAULT, NULL); 6567c478bd9Sstevel@tonic-gate 657f4b3ec61Sdh155122 mutex_init(&ipss->ipsec_alg_lock, NULL, MUTEX_DEFAULT, NULL); 658f4b3ec61Sdh155122 for (i = 0; i < IPSEC_NALGTYPES; i++) { 659f4b3ec61Sdh155122 ipss->ipsec_nalgs[i] = 0; 660f4b3ec61Sdh155122 } 6617c478bd9Sstevel@tonic-gate 662f4b3ec61Sdh155122 ip_drop_init(ipss); 663f4b3ec61Sdh155122 ip_drop_register(&ipss->ipsec_spd_dropper, "IPsec SPD"); 6647c478bd9Sstevel@tonic-gate 665f4b3ec61Sdh155122 /* IP's IPsec code calls the packet dropper */ 666f4b3ec61Sdh155122 ip_drop_register(&ipss->ipsec_dropper, "IP IPsec processing"); 667f4b3ec61Sdh155122 668f4b3ec61Sdh155122 (void) ipsec_kstat_init(ipss); 669f4b3ec61Sdh155122 670f4b3ec61Sdh155122 ipsec_loader_init(ipss); 671f4b3ec61Sdh155122 ipsec_loader_start(ipss); 672f4b3ec61Sdh155122 673f4b3ec61Sdh155122 return (ipss); 674f4b3ec61Sdh155122 } 675f4b3ec61Sdh155122 676f4b3ec61Sdh155122 /* Global across all stack instances */ 677f4b3ec61Sdh155122 void 678f4b3ec61Sdh155122 ipsec_policy_g_init(void) 679f4b3ec61Sdh155122 { 6807c478bd9Sstevel@tonic-gate ipsec_action_cache = kmem_cache_create("ipsec_actions", 6817c478bd9Sstevel@tonic-gate sizeof (ipsec_action_t), _POINTER_ALIGNMENT, NULL, NULL, 6827c478bd9Sstevel@tonic-gate ipsec_action_reclaim, NULL, NULL, 0); 6837c478bd9Sstevel@tonic-gate ipsec_sel_cache = kmem_cache_create("ipsec_selectors", 6847c478bd9Sstevel@tonic-gate sizeof (ipsec_sel_t), _POINTER_ALIGNMENT, NULL, NULL, 6857c478bd9Sstevel@tonic-gate NULL, NULL, NULL, 0); 6867c478bd9Sstevel@tonic-gate ipsec_pol_cache = kmem_cache_create("ipsec_policy", 6877c478bd9Sstevel@tonic-gate sizeof (ipsec_policy_t), _POINTER_ALIGNMENT, NULL, NULL, 6887c478bd9Sstevel@tonic-gate NULL, NULL, NULL, 0); 6897c478bd9Sstevel@tonic-gate 690f4b3ec61Sdh155122 /* 691f4b3ec61Sdh155122 * We want to be informed each time a stack is created or 692f4b3ec61Sdh155122 * destroyed in the kernel, so we can maintain the 693f4b3ec61Sdh155122 * set of ipsec_stack_t's. 694f4b3ec61Sdh155122 */ 695f4b3ec61Sdh155122 netstack_register(NS_IPSEC, ipsec_stack_init, NULL, ipsec_stack_fini); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate /* 6997c478bd9Sstevel@tonic-gate * Sort algorithm lists. 700a86080f9Sdanmcd * 7017c478bd9Sstevel@tonic-gate * I may need to split this based on 7027c478bd9Sstevel@tonic-gate * authentication/encryption, and I may wish to have an administrator 7037c478bd9Sstevel@tonic-gate * configure this list. Hold on to some NDD variables... 7047c478bd9Sstevel@tonic-gate * 7057c478bd9Sstevel@tonic-gate * XXX For now, sort on minimum key size (GAG!). While minimum key size is 706a86080f9Sdanmcd * not the ideal metric, it's the only quantifiable measure available. 707a86080f9Sdanmcd * We need a better metric for sorting algorithms by preference. 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate static void 710f4b3ec61Sdh155122 alg_insert_sortlist(enum ipsec_algtype at, uint8_t algid, netstack_t *ns) 7117c478bd9Sstevel@tonic-gate { 712f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 713f4b3ec61Sdh155122 ipsec_alginfo_t *ai = ipss->ipsec_alglists[at][algid]; 7147c478bd9Sstevel@tonic-gate uint8_t holder, swap; 7157c478bd9Sstevel@tonic-gate uint_t i; 716f4b3ec61Sdh155122 uint_t count = ipss->ipsec_nalgs[at]; 7177c478bd9Sstevel@tonic-gate ASSERT(ai != NULL); 7187c478bd9Sstevel@tonic-gate ASSERT(algid == ai->alg_id); 7197c478bd9Sstevel@tonic-gate 720f4b3ec61Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate holder = algid; 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate for (i = 0; i < count - 1; i++) { 7257c478bd9Sstevel@tonic-gate ipsec_alginfo_t *alt; 7267c478bd9Sstevel@tonic-gate 727f4b3ec61Sdh155122 alt = ipss->ipsec_alglists[at][ipss->ipsec_sortlist[at][i]]; 7287c478bd9Sstevel@tonic-gate /* 7297c478bd9Sstevel@tonic-gate * If you want to give precedence to newly added algs, 7307c478bd9Sstevel@tonic-gate * add the = in the > comparison. 7317c478bd9Sstevel@tonic-gate */ 7327c478bd9Sstevel@tonic-gate if ((holder != algid) || (ai->alg_minbits > alt->alg_minbits)) { 7337c478bd9Sstevel@tonic-gate /* Swap sortlist[i] and holder. */ 734f4b3ec61Sdh155122 swap = ipss->ipsec_sortlist[at][i]; 735f4b3ec61Sdh155122 ipss->ipsec_sortlist[at][i] = holder; 7367c478bd9Sstevel@tonic-gate holder = swap; 7377c478bd9Sstevel@tonic-gate ai = alt; 7387c478bd9Sstevel@tonic-gate } /* Else just continue. */ 7397c478bd9Sstevel@tonic-gate } 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate /* Store holder in last slot. */ 742f4b3ec61Sdh155122 ipss->ipsec_sortlist[at][i] = holder; 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate /* 7467c478bd9Sstevel@tonic-gate * Remove an algorithm from a sorted algorithm list. 7477c478bd9Sstevel@tonic-gate * This should be considerably easier, even with complex sorting. 7487c478bd9Sstevel@tonic-gate */ 7497c478bd9Sstevel@tonic-gate static void 750f4b3ec61Sdh155122 alg_remove_sortlist(enum ipsec_algtype at, uint8_t algid, netstack_t *ns) 7517c478bd9Sstevel@tonic-gate { 7527c478bd9Sstevel@tonic-gate boolean_t copyback = B_FALSE; 7537c478bd9Sstevel@tonic-gate int i; 754f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 755f4b3ec61Sdh155122 int newcount = ipss->ipsec_nalgs[at]; 7567c478bd9Sstevel@tonic-gate 757f4b3ec61Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate for (i = 0; i <= newcount; i++) { 760f4b3ec61Sdh155122 if (copyback) { 761f4b3ec61Sdh155122 ipss->ipsec_sortlist[at][i-1] = 762f4b3ec61Sdh155122 ipss->ipsec_sortlist[at][i]; 763f4b3ec61Sdh155122 } else if (ipss->ipsec_sortlist[at][i] == algid) { 7647c478bd9Sstevel@tonic-gate copyback = B_TRUE; 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate } 767f4b3ec61Sdh155122 } 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate /* 7707c478bd9Sstevel@tonic-gate * Add the specified algorithm to the algorithm tables. 7717c478bd9Sstevel@tonic-gate * Must be called while holding the algorithm table writer lock. 7727c478bd9Sstevel@tonic-gate */ 7737c478bd9Sstevel@tonic-gate void 774f4b3ec61Sdh155122 ipsec_alg_reg(ipsec_algtype_t algtype, ipsec_alginfo_t *alg, netstack_t *ns) 7757c478bd9Sstevel@tonic-gate { 776f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 7777c478bd9Sstevel@tonic-gate 778f4b3ec61Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 7797c478bd9Sstevel@tonic-gate 780f4b3ec61Sdh155122 ASSERT(ipss->ipsec_alglists[algtype][alg->alg_id] == NULL); 781f4b3ec61Sdh155122 ipsec_alg_fix_min_max(alg, algtype, ns); 782f4b3ec61Sdh155122 ipss->ipsec_alglists[algtype][alg->alg_id] = alg; 783f4b3ec61Sdh155122 784f4b3ec61Sdh155122 ipss->ipsec_nalgs[algtype]++; 785f4b3ec61Sdh155122 alg_insert_sortlist(algtype, alg->alg_id, ns); 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate /* 7897c478bd9Sstevel@tonic-gate * Remove the specified algorithm from the algorithm tables. 7907c478bd9Sstevel@tonic-gate * Must be called while holding the algorithm table writer lock. 7917c478bd9Sstevel@tonic-gate */ 7927c478bd9Sstevel@tonic-gate void 793f4b3ec61Sdh155122 ipsec_alg_unreg(ipsec_algtype_t algtype, uint8_t algid, netstack_t *ns) 7947c478bd9Sstevel@tonic-gate { 795f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 7967c478bd9Sstevel@tonic-gate 797f4b3ec61Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 7987c478bd9Sstevel@tonic-gate 799f4b3ec61Sdh155122 ASSERT(ipss->ipsec_alglists[algtype][algid] != NULL); 800f4b3ec61Sdh155122 ipsec_alg_free(ipss->ipsec_alglists[algtype][algid]); 801f4b3ec61Sdh155122 ipss->ipsec_alglists[algtype][algid] = NULL; 802f4b3ec61Sdh155122 803f4b3ec61Sdh155122 ipss->ipsec_nalgs[algtype]--; 804f4b3ec61Sdh155122 alg_remove_sortlist(algtype, algid, ns); 8057c478bd9Sstevel@tonic-gate } 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate /* 8087c478bd9Sstevel@tonic-gate * Hooks for spdsock to get a grip on system policy. 8097c478bd9Sstevel@tonic-gate */ 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate ipsec_policy_head_t * 812f4b3ec61Sdh155122 ipsec_system_policy(netstack_t *ns) 8137c478bd9Sstevel@tonic-gate { 814f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 815f4b3ec61Sdh155122 ipsec_policy_head_t *h = &ipss->ipsec_system_policy; 816f4b3ec61Sdh155122 8177c478bd9Sstevel@tonic-gate IPPH_REFHOLD(h); 8187c478bd9Sstevel@tonic-gate return (h); 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate ipsec_policy_head_t * 822f4b3ec61Sdh155122 ipsec_inactive_policy(netstack_t *ns) 8237c478bd9Sstevel@tonic-gate { 824f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 825f4b3ec61Sdh155122 ipsec_policy_head_t *h = &ipss->ipsec_inactive_policy; 826f4b3ec61Sdh155122 8277c478bd9Sstevel@tonic-gate IPPH_REFHOLD(h); 8287c478bd9Sstevel@tonic-gate return (h); 8297c478bd9Sstevel@tonic-gate } 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate /* 8327c478bd9Sstevel@tonic-gate * Lock inactive policy, then active policy, then exchange policy root 8337c478bd9Sstevel@tonic-gate * pointers. 8347c478bd9Sstevel@tonic-gate */ 8357c478bd9Sstevel@tonic-gate void 836f4b3ec61Sdh155122 ipsec_swap_policy(ipsec_policy_head_t *active, ipsec_policy_head_t *inactive, 837f4b3ec61Sdh155122 netstack_t *ns) 8387c478bd9Sstevel@tonic-gate { 8397c478bd9Sstevel@tonic-gate int af, dir; 8406a182920Ssommerfe avl_tree_t r1, r2; 8417c478bd9Sstevel@tonic-gate 8428810c16bSdanmcd rw_enter(&inactive->iph_lock, RW_WRITER); 8438810c16bSdanmcd rw_enter(&active->iph_lock, RW_WRITER); 8446a182920Ssommerfe 8458810c16bSdanmcd r1 = active->iph_rulebyid; 8468810c16bSdanmcd r2 = inactive->iph_rulebyid; 8478810c16bSdanmcd active->iph_rulebyid = r2; 8488810c16bSdanmcd inactive->iph_rulebyid = r1; 8496a182920Ssommerfe 8507c478bd9Sstevel@tonic-gate for (dir = 0; dir < IPSEC_NTYPES; dir++) { 8516a182920Ssommerfe ipsec_policy_hash_t *h1, *h2; 8526a182920Ssommerfe 8538810c16bSdanmcd h1 = active->iph_root[dir].ipr_hash; 8548810c16bSdanmcd h2 = inactive->iph_root[dir].ipr_hash; 8558810c16bSdanmcd active->iph_root[dir].ipr_hash = h2; 8568810c16bSdanmcd inactive->iph_root[dir].ipr_hash = h1; 8576a182920Ssommerfe 8587c478bd9Sstevel@tonic-gate for (af = 0; af < IPSEC_NAF; af++) { 8596a182920Ssommerfe ipsec_policy_t *t1, *t2; 8606a182920Ssommerfe 8618810c16bSdanmcd t1 = active->iph_root[dir].ipr_nonhash[af]; 8628810c16bSdanmcd t2 = inactive->iph_root[dir].ipr_nonhash[af]; 8638810c16bSdanmcd active->iph_root[dir].ipr_nonhash[af] = t2; 8648810c16bSdanmcd inactive->iph_root[dir].ipr_nonhash[af] = t1; 8656a182920Ssommerfe if (t1 != NULL) { 8666a182920Ssommerfe t1->ipsp_hash.hash_pp = 8678810c16bSdanmcd &(inactive->iph_root[dir].ipr_nonhash[af]); 8686a182920Ssommerfe } 8696a182920Ssommerfe if (t2 != NULL) { 8706a182920Ssommerfe t2->ipsp_hash.hash_pp = 8718810c16bSdanmcd &(active->iph_root[dir].ipr_nonhash[af]); 8726a182920Ssommerfe } 8736a182920Ssommerfe 8747c478bd9Sstevel@tonic-gate } 8757c478bd9Sstevel@tonic-gate } 8768810c16bSdanmcd active->iph_gen++; 8778810c16bSdanmcd inactive->iph_gen++; 878f4b3ec61Sdh155122 ipsec_update_present_flags(ns->netstack_ipsec); 8798810c16bSdanmcd rw_exit(&active->iph_lock); 8808810c16bSdanmcd rw_exit(&inactive->iph_lock); 8818810c16bSdanmcd } 8828810c16bSdanmcd 8838810c16bSdanmcd /* 8848810c16bSdanmcd * Swap global policy primary/secondary. 8858810c16bSdanmcd */ 8868810c16bSdanmcd void 887f4b3ec61Sdh155122 ipsec_swap_global_policy(netstack_t *ns) 8888810c16bSdanmcd { 889f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 890f4b3ec61Sdh155122 891f4b3ec61Sdh155122 ipsec_swap_policy(&ipss->ipsec_system_policy, 892f4b3ec61Sdh155122 &ipss->ipsec_inactive_policy, ns); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate /* 8967c478bd9Sstevel@tonic-gate * Clone one policy rule.. 8977c478bd9Sstevel@tonic-gate */ 8987c478bd9Sstevel@tonic-gate static ipsec_policy_t * 8997c478bd9Sstevel@tonic-gate ipsec_copy_policy(const ipsec_policy_t *src) 9007c478bd9Sstevel@tonic-gate { 9017c478bd9Sstevel@tonic-gate ipsec_policy_t *dst = kmem_cache_alloc(ipsec_pol_cache, KM_NOSLEEP); 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate if (dst == NULL) 9047c478bd9Sstevel@tonic-gate return (NULL); 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate /* 9077c478bd9Sstevel@tonic-gate * Adjust refcounts of cloned state. 9087c478bd9Sstevel@tonic-gate */ 9097c478bd9Sstevel@tonic-gate IPACT_REFHOLD(src->ipsp_act); 9107c478bd9Sstevel@tonic-gate src->ipsp_sel->ipsl_refs++; 9117c478bd9Sstevel@tonic-gate 9126a182920Ssommerfe HASH_NULL(dst, ipsp_hash); 913bd670b35SErik Nordmark dst->ipsp_netstack = src->ipsp_netstack; 9147c478bd9Sstevel@tonic-gate dst->ipsp_refs = 1; 9157c478bd9Sstevel@tonic-gate dst->ipsp_sel = src->ipsp_sel; 9167c478bd9Sstevel@tonic-gate dst->ipsp_act = src->ipsp_act; 9177c478bd9Sstevel@tonic-gate dst->ipsp_prio = src->ipsp_prio; 9187c478bd9Sstevel@tonic-gate dst->ipsp_index = src->ipsp_index; 9197c478bd9Sstevel@tonic-gate 9207c478bd9Sstevel@tonic-gate return (dst); 9217c478bd9Sstevel@tonic-gate } 9227c478bd9Sstevel@tonic-gate 9236a182920Ssommerfe void 9246a182920Ssommerfe ipsec_insert_always(avl_tree_t *tree, void *new_node) 9256a182920Ssommerfe { 9266a182920Ssommerfe void *node; 9276a182920Ssommerfe avl_index_t where; 9286a182920Ssommerfe 9296a182920Ssommerfe node = avl_find(tree, new_node, &where); 9306a182920Ssommerfe ASSERT(node == NULL); 9316a182920Ssommerfe avl_insert(tree, new_node, where); 9326a182920Ssommerfe } 9336a182920Ssommerfe 9346a182920Ssommerfe 9356a182920Ssommerfe static int 9366a182920Ssommerfe ipsec_copy_chain(ipsec_policy_head_t *dph, ipsec_policy_t *src, 9376a182920Ssommerfe ipsec_policy_t **dstp) 9386a182920Ssommerfe { 9396a182920Ssommerfe for (; src != NULL; src = src->ipsp_hash.hash_next) { 9406a182920Ssommerfe ipsec_policy_t *dst = ipsec_copy_policy(src); 9416a182920Ssommerfe if (dst == NULL) 9426a182920Ssommerfe return (ENOMEM); 9436a182920Ssommerfe 9446a182920Ssommerfe HASHLIST_INSERT(dst, ipsp_hash, *dstp); 9456a182920Ssommerfe ipsec_insert_always(&dph->iph_rulebyid, dst); 9466a182920Ssommerfe } 9476a182920Ssommerfe return (0); 9486a182920Ssommerfe } 9496a182920Ssommerfe 9506a182920Ssommerfe 9516a182920Ssommerfe 9527c478bd9Sstevel@tonic-gate /* 9537c478bd9Sstevel@tonic-gate * Make one policy head look exactly like another. 9547c478bd9Sstevel@tonic-gate * 9557c478bd9Sstevel@tonic-gate * As with ipsec_swap_policy, we lock the destination policy head first, then 9567c478bd9Sstevel@tonic-gate * the source policy head. Note that we only need to read-lock the source 9577c478bd9Sstevel@tonic-gate * policy head as we are not changing it. 9587c478bd9Sstevel@tonic-gate */ 9598810c16bSdanmcd int 960f4b3ec61Sdh155122 ipsec_copy_polhead(ipsec_policy_head_t *sph, ipsec_policy_head_t *dph, 961f4b3ec61Sdh155122 netstack_t *ns) 9627c478bd9Sstevel@tonic-gate { 9636a182920Ssommerfe int af, dir, chain, nchains; 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate rw_enter(&dph->iph_lock, RW_WRITER); 9667c478bd9Sstevel@tonic-gate 967f4b3ec61Sdh155122 ipsec_polhead_flush(dph, ns); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate rw_enter(&sph->iph_lock, RW_READER); 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate for (dir = 0; dir < IPSEC_NTYPES; dir++) { 9726a182920Ssommerfe ipsec_policy_root_t *dpr = &dph->iph_root[dir]; 9736a182920Ssommerfe ipsec_policy_root_t *spr = &sph->iph_root[dir]; 9746a182920Ssommerfe nchains = dpr->ipr_nchains; 9756a182920Ssommerfe 9766a182920Ssommerfe ASSERT(dpr->ipr_nchains == spr->ipr_nchains); 9776a182920Ssommerfe 9787c478bd9Sstevel@tonic-gate for (af = 0; af < IPSEC_NAF; af++) { 9796a182920Ssommerfe if (ipsec_copy_chain(dph, spr->ipr_nonhash[af], 9806a182920Ssommerfe &dpr->ipr_nonhash[af])) 9816a182920Ssommerfe goto abort_copy; 9827c478bd9Sstevel@tonic-gate } 9836a182920Ssommerfe 9846a182920Ssommerfe for (chain = 0; chain < nchains; chain++) { 9856a182920Ssommerfe if (ipsec_copy_chain(dph, 9866a182920Ssommerfe spr->ipr_hash[chain].hash_head, 9876a182920Ssommerfe &dpr->ipr_hash[chain].hash_head)) 9886a182920Ssommerfe goto abort_copy; 9897c478bd9Sstevel@tonic-gate } 9907c478bd9Sstevel@tonic-gate } 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate dph->iph_gen++; 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate rw_exit(&sph->iph_lock); 9957c478bd9Sstevel@tonic-gate rw_exit(&dph->iph_lock); 9967c478bd9Sstevel@tonic-gate return (0); 9976a182920Ssommerfe 9986a182920Ssommerfe abort_copy: 999f4b3ec61Sdh155122 ipsec_polhead_flush(dph, ns); 10006a182920Ssommerfe rw_exit(&sph->iph_lock); 10016a182920Ssommerfe rw_exit(&dph->iph_lock); 10026a182920Ssommerfe return (ENOMEM); 10037c478bd9Sstevel@tonic-gate } 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate /* 10067c478bd9Sstevel@tonic-gate * Clone currently active policy to the inactive policy list. 10077c478bd9Sstevel@tonic-gate */ 10087c478bd9Sstevel@tonic-gate int 1009f4b3ec61Sdh155122 ipsec_clone_system_policy(netstack_t *ns) 10107c478bd9Sstevel@tonic-gate { 1011f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 1012f4b3ec61Sdh155122 1013f4b3ec61Sdh155122 return (ipsec_copy_polhead(&ipss->ipsec_system_policy, 1014f4b3ec61Sdh155122 &ipss->ipsec_inactive_policy, ns)); 10157c478bd9Sstevel@tonic-gate } 10167c478bd9Sstevel@tonic-gate 10178810c16bSdanmcd /* 10187c478bd9Sstevel@tonic-gate * Extract the string from ipsec_policy_failure_msgs[type] and 10197c478bd9Sstevel@tonic-gate * log it. 10207c478bd9Sstevel@tonic-gate * 10217c478bd9Sstevel@tonic-gate */ 10227c478bd9Sstevel@tonic-gate void 102332350c00Sdanmcd ipsec_log_policy_failure(int type, char *func_name, ipha_t *ipha, ip6_t *ip6h, 1024f4b3ec61Sdh155122 boolean_t secure, netstack_t *ns) 10257c478bd9Sstevel@tonic-gate { 10267c478bd9Sstevel@tonic-gate char sbuf[INET6_ADDRSTRLEN]; 10277c478bd9Sstevel@tonic-gate char dbuf[INET6_ADDRSTRLEN]; 10287c478bd9Sstevel@tonic-gate char *s; 10297c478bd9Sstevel@tonic-gate char *d; 1030f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 10317c478bd9Sstevel@tonic-gate 10327c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 10337c478bd9Sstevel@tonic-gate (ip6h == NULL && ipha != NULL)); 10347c478bd9Sstevel@tonic-gate 10357c478bd9Sstevel@tonic-gate if (ipha != NULL) { 10367c478bd9Sstevel@tonic-gate s = inet_ntop(AF_INET, &ipha->ipha_src, sbuf, sizeof (sbuf)); 10377c478bd9Sstevel@tonic-gate d = inet_ntop(AF_INET, &ipha->ipha_dst, dbuf, sizeof (dbuf)); 10387c478bd9Sstevel@tonic-gate } else { 10397c478bd9Sstevel@tonic-gate s = inet_ntop(AF_INET6, &ip6h->ip6_src, sbuf, sizeof (sbuf)); 10407c478bd9Sstevel@tonic-gate d = inet_ntop(AF_INET6, &ip6h->ip6_dst, dbuf, sizeof (dbuf)); 10417c478bd9Sstevel@tonic-gate 10427c478bd9Sstevel@tonic-gate } 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate /* Always bump the policy failure counter. */ 1045f4b3ec61Sdh155122 ipss->ipsec_policy_failure_count[type]++; 10467c478bd9Sstevel@tonic-gate 1047f4b3ec61Sdh155122 ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, SL_ERROR|SL_WARN|SL_CONSOLE, 104832350c00Sdanmcd ipsec_policy_failure_msgs[type], func_name, 10497c478bd9Sstevel@tonic-gate (secure ? "secure" : "not secure"), s, d); 1050bffb04cfSmarkfen } 1051bffb04cfSmarkfen 1052bffb04cfSmarkfen /* 1053bffb04cfSmarkfen * Rate-limiting front-end to strlog() for AH and ESP. Uses the ndd variables 1054bffb04cfSmarkfen * in /dev/ip and the same rate-limiting clock so that there's a single 1055bffb04cfSmarkfen * knob to turn to throttle the rate of messages. 1056bffb04cfSmarkfen */ 1057bffb04cfSmarkfen void 1058f4b3ec61Sdh155122 ipsec_rl_strlog(netstack_t *ns, short mid, short sid, char level, ushort_t sl, 1059f4b3ec61Sdh155122 char *fmt, ...) 1060bffb04cfSmarkfen { 1061bffb04cfSmarkfen va_list adx; 1062bffb04cfSmarkfen hrtime_t current = gethrtime(); 1063f4b3ec61Sdh155122 ip_stack_t *ipst = ns->netstack_ip; 1064f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 1065bffb04cfSmarkfen 1066bffb04cfSmarkfen sl |= SL_CONSOLE; 1067bffb04cfSmarkfen /* 1068bffb04cfSmarkfen * Throttle logging to stop syslog from being swamped. If variable 1069bffb04cfSmarkfen * 'ipsec_policy_log_interval' is zero, don't log any messages at 1070bffb04cfSmarkfen * all, otherwise log only one message every 'ipsec_policy_log_interval' 1071bffb04cfSmarkfen * msec. Convert interval (in msec) to hrtime (in nsec). 1072bffb04cfSmarkfen */ 1073bffb04cfSmarkfen 1074f4b3ec61Sdh155122 if (ipst->ips_ipsec_policy_log_interval) { 1075f4b3ec61Sdh155122 if (ipss->ipsec_policy_failure_last + 1076cf0ee00aSDan McDonald MSEC2NSEC(ipst->ips_ipsec_policy_log_interval) <= current) { 1077bffb04cfSmarkfen va_start(adx, fmt); 1078bffb04cfSmarkfen (void) vstrlog(mid, sid, level, sl, fmt, adx); 1079bffb04cfSmarkfen va_end(adx); 1080f4b3ec61Sdh155122 ipss->ipsec_policy_failure_last = current; 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate } 1083bffb04cfSmarkfen } 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate void 1086f4b3ec61Sdh155122 ipsec_config_flush(netstack_t *ns) 10877c478bd9Sstevel@tonic-gate { 1088f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 1089f4b3ec61Sdh155122 1090f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_system_policy.iph_lock, RW_WRITER); 1091f4b3ec61Sdh155122 ipsec_polhead_flush(&ipss->ipsec_system_policy, ns); 1092f4b3ec61Sdh155122 ipss->ipsec_next_policy_index = 1; 1093f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_system_policy.iph_lock); 10944ba231ceSKacheong Poon ipsec_action_reclaim_stack(ipss); 10957c478bd9Sstevel@tonic-gate } 10967c478bd9Sstevel@tonic-gate 10977c478bd9Sstevel@tonic-gate /* 10987c478bd9Sstevel@tonic-gate * Clip a policy's min/max keybits vs. the capabilities of the 10997c478bd9Sstevel@tonic-gate * algorithm. 11007c478bd9Sstevel@tonic-gate */ 11017c478bd9Sstevel@tonic-gate static void 11027c478bd9Sstevel@tonic-gate act_alg_adjust(uint_t algtype, uint_t algid, 1103f4b3ec61Sdh155122 uint16_t *minbits, uint16_t *maxbits, netstack_t *ns) 11047c478bd9Sstevel@tonic-gate { 1105f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 1106f4b3ec61Sdh155122 ipsec_alginfo_t *algp = ipss->ipsec_alglists[algtype][algid]; 1107f4b3ec61Sdh155122 11087c478bd9Sstevel@tonic-gate if (algp != NULL) { 11097c478bd9Sstevel@tonic-gate /* 11107c478bd9Sstevel@tonic-gate * If passed-in minbits is zero, we assume the caller trusts 11117c478bd9Sstevel@tonic-gate * us with setting the minimum key size. We pick the 11127c478bd9Sstevel@tonic-gate * algorithms DEFAULT key size for the minimum in this case. 11137c478bd9Sstevel@tonic-gate */ 11147c478bd9Sstevel@tonic-gate if (*minbits == 0) { 11157c478bd9Sstevel@tonic-gate *minbits = algp->alg_default_bits; 11167c478bd9Sstevel@tonic-gate ASSERT(*minbits >= algp->alg_minbits); 11177c478bd9Sstevel@tonic-gate } else { 11188810c16bSdanmcd *minbits = MAX(MIN(*minbits, algp->alg_maxbits), 11198810c16bSdanmcd algp->alg_minbits); 11207c478bd9Sstevel@tonic-gate } 11217c478bd9Sstevel@tonic-gate if (*maxbits == 0) 11227c478bd9Sstevel@tonic-gate *maxbits = algp->alg_maxbits; 11237c478bd9Sstevel@tonic-gate else 11248810c16bSdanmcd *maxbits = MIN(MAX(*maxbits, algp->alg_minbits), 11258810c16bSdanmcd algp->alg_maxbits); 11267c478bd9Sstevel@tonic-gate ASSERT(*minbits <= *maxbits); 11277c478bd9Sstevel@tonic-gate } else { 11287c478bd9Sstevel@tonic-gate *minbits = 0; 11297c478bd9Sstevel@tonic-gate *maxbits = 0; 11307c478bd9Sstevel@tonic-gate } 11317c478bd9Sstevel@tonic-gate } 11327c478bd9Sstevel@tonic-gate 11337c478bd9Sstevel@tonic-gate /* 11347c478bd9Sstevel@tonic-gate * Check an action's requested algorithms against the algorithms currently 11357c478bd9Sstevel@tonic-gate * loaded in the system. 11367c478bd9Sstevel@tonic-gate */ 11377c478bd9Sstevel@tonic-gate boolean_t 1138f4b3ec61Sdh155122 ipsec_check_action(ipsec_act_t *act, int *diag, netstack_t *ns) 11397c478bd9Sstevel@tonic-gate { 11407c478bd9Sstevel@tonic-gate ipsec_prot_t *ipp; 1141f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 11427c478bd9Sstevel@tonic-gate 11437c478bd9Sstevel@tonic-gate ipp = &act->ipa_apply; 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate if (ipp->ipp_use_ah && 1146f4b3ec61Sdh155122 ipss->ipsec_alglists[IPSEC_ALG_AUTH][ipp->ipp_auth_alg] == NULL) { 11477c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_AH_ALG; 11487c478bd9Sstevel@tonic-gate return (B_FALSE); 11497c478bd9Sstevel@tonic-gate } 11507c478bd9Sstevel@tonic-gate if (ipp->ipp_use_espa && 1151f4b3ec61Sdh155122 ipss->ipsec_alglists[IPSEC_ALG_AUTH][ipp->ipp_esp_auth_alg] == 1152f4b3ec61Sdh155122 NULL) { 11537c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_ALG; 11547c478bd9Sstevel@tonic-gate return (B_FALSE); 11557c478bd9Sstevel@tonic-gate } 11567c478bd9Sstevel@tonic-gate if (ipp->ipp_use_esp && 1157f4b3ec61Sdh155122 ipss->ipsec_alglists[IPSEC_ALG_ENCR][ipp->ipp_encr_alg] == NULL) { 11587c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_ALG; 11597c478bd9Sstevel@tonic-gate return (B_FALSE); 11607c478bd9Sstevel@tonic-gate } 11617c478bd9Sstevel@tonic-gate 11627c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_AUTH, ipp->ipp_auth_alg, 1163f4b3ec61Sdh155122 &ipp->ipp_ah_minbits, &ipp->ipp_ah_maxbits, ns); 11647c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_AUTH, ipp->ipp_esp_auth_alg, 1165f4b3ec61Sdh155122 &ipp->ipp_espa_minbits, &ipp->ipp_espa_maxbits, ns); 11667c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_ENCR, ipp->ipp_encr_alg, 1167f4b3ec61Sdh155122 &ipp->ipp_espe_minbits, &ipp->ipp_espe_maxbits, ns); 11687c478bd9Sstevel@tonic-gate 11697c478bd9Sstevel@tonic-gate if (ipp->ipp_ah_minbits > ipp->ipp_ah_maxbits) { 11707c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_AH_KEYSIZE; 11717c478bd9Sstevel@tonic-gate return (B_FALSE); 11727c478bd9Sstevel@tonic-gate } 11737c478bd9Sstevel@tonic-gate if (ipp->ipp_espa_minbits > ipp->ipp_espa_maxbits) { 11747c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_KEYSIZE; 11757c478bd9Sstevel@tonic-gate return (B_FALSE); 11767c478bd9Sstevel@tonic-gate } 11777c478bd9Sstevel@tonic-gate if (ipp->ipp_espe_minbits > ipp->ipp_espe_maxbits) { 11787c478bd9Sstevel@tonic-gate *diag = SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_KEYSIZE; 11797c478bd9Sstevel@tonic-gate return (B_FALSE); 11807c478bd9Sstevel@tonic-gate } 11817c478bd9Sstevel@tonic-gate /* TODO: sanity check lifetimes */ 11827c478bd9Sstevel@tonic-gate return (B_TRUE); 11837c478bd9Sstevel@tonic-gate } 11847c478bd9Sstevel@tonic-gate 11857c478bd9Sstevel@tonic-gate /* 11867c478bd9Sstevel@tonic-gate * Set up a single action during wildcard expansion.. 11877c478bd9Sstevel@tonic-gate */ 11887c478bd9Sstevel@tonic-gate static void 11897c478bd9Sstevel@tonic-gate ipsec_setup_act(ipsec_act_t *outact, ipsec_act_t *act, 1190f4b3ec61Sdh155122 uint_t auth_alg, uint_t encr_alg, uint_t eauth_alg, netstack_t *ns) 11917c478bd9Sstevel@tonic-gate { 11927c478bd9Sstevel@tonic-gate ipsec_prot_t *ipp; 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate *outact = *act; 11957c478bd9Sstevel@tonic-gate ipp = &outact->ipa_apply; 11967c478bd9Sstevel@tonic-gate ipp->ipp_auth_alg = (uint8_t)auth_alg; 11977c478bd9Sstevel@tonic-gate ipp->ipp_encr_alg = (uint8_t)encr_alg; 11987c478bd9Sstevel@tonic-gate ipp->ipp_esp_auth_alg = (uint8_t)eauth_alg; 11997c478bd9Sstevel@tonic-gate 12007c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_AUTH, auth_alg, 1201f4b3ec61Sdh155122 &ipp->ipp_ah_minbits, &ipp->ipp_ah_maxbits, ns); 12027c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_AUTH, eauth_alg, 1203f4b3ec61Sdh155122 &ipp->ipp_espa_minbits, &ipp->ipp_espa_maxbits, ns); 12047c478bd9Sstevel@tonic-gate act_alg_adjust(IPSEC_ALG_ENCR, encr_alg, 1205f4b3ec61Sdh155122 &ipp->ipp_espe_minbits, &ipp->ipp_espe_maxbits, ns); 12067c478bd9Sstevel@tonic-gate } 12077c478bd9Sstevel@tonic-gate 12087c478bd9Sstevel@tonic-gate /* 12097c478bd9Sstevel@tonic-gate * combinatoric expansion time: expand a wildcarded action into an 12107c478bd9Sstevel@tonic-gate * array of wildcarded actions; we return the exploded action list, 12117c478bd9Sstevel@tonic-gate * and return a count in *nact (output only). 12127c478bd9Sstevel@tonic-gate */ 12137c478bd9Sstevel@tonic-gate static ipsec_act_t * 1214f4b3ec61Sdh155122 ipsec_act_wildcard_expand(ipsec_act_t *act, uint_t *nact, netstack_t *ns) 12157c478bd9Sstevel@tonic-gate { 12167c478bd9Sstevel@tonic-gate boolean_t use_ah, use_esp, use_espa; 12177c478bd9Sstevel@tonic-gate boolean_t wild_auth, wild_encr, wild_eauth; 12187c478bd9Sstevel@tonic-gate uint_t auth_alg, auth_idx, auth_min, auth_max; 12197c478bd9Sstevel@tonic-gate uint_t eauth_alg, eauth_idx, eauth_min, eauth_max; 12207c478bd9Sstevel@tonic-gate uint_t encr_alg, encr_idx, encr_min, encr_max; 12217c478bd9Sstevel@tonic-gate uint_t action_count, ai; 12227c478bd9Sstevel@tonic-gate ipsec_act_t *outact; 1223f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 12247c478bd9Sstevel@tonic-gate 12257c478bd9Sstevel@tonic-gate if (act->ipa_type != IPSEC_ACT_APPLY) { 12267c478bd9Sstevel@tonic-gate outact = kmem_alloc(sizeof (*act), KM_NOSLEEP); 12277c478bd9Sstevel@tonic-gate *nact = 1; 12287c478bd9Sstevel@tonic-gate if (outact != NULL) 12297c478bd9Sstevel@tonic-gate bcopy(act, outact, sizeof (*act)); 12307c478bd9Sstevel@tonic-gate return (outact); 12317c478bd9Sstevel@tonic-gate } 12327c478bd9Sstevel@tonic-gate /* 12337c478bd9Sstevel@tonic-gate * compute the combinatoric explosion.. 12347c478bd9Sstevel@tonic-gate * 12357c478bd9Sstevel@tonic-gate * we assume a request for encr if esp_req is PREF_REQUIRED 12367c478bd9Sstevel@tonic-gate * we assume a request for ah auth if ah_req is PREF_REQUIRED. 12377c478bd9Sstevel@tonic-gate * we assume a request for esp auth if !ah and esp_req is PREF_REQUIRED 12387c478bd9Sstevel@tonic-gate */ 12397c478bd9Sstevel@tonic-gate 12407c478bd9Sstevel@tonic-gate use_ah = act->ipa_apply.ipp_use_ah; 12417c478bd9Sstevel@tonic-gate use_esp = act->ipa_apply.ipp_use_esp; 12427c478bd9Sstevel@tonic-gate use_espa = act->ipa_apply.ipp_use_espa; 12437c478bd9Sstevel@tonic-gate auth_alg = act->ipa_apply.ipp_auth_alg; 12447c478bd9Sstevel@tonic-gate eauth_alg = act->ipa_apply.ipp_esp_auth_alg; 12457c478bd9Sstevel@tonic-gate encr_alg = act->ipa_apply.ipp_encr_alg; 12467c478bd9Sstevel@tonic-gate 12477c478bd9Sstevel@tonic-gate wild_auth = use_ah && (auth_alg == 0); 12487c478bd9Sstevel@tonic-gate wild_eauth = use_espa && (eauth_alg == 0); 12497c478bd9Sstevel@tonic-gate wild_encr = use_esp && (encr_alg == 0); 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate action_count = 1; 12527c478bd9Sstevel@tonic-gate auth_min = auth_max = auth_alg; 12537c478bd9Sstevel@tonic-gate eauth_min = eauth_max = eauth_alg; 12547c478bd9Sstevel@tonic-gate encr_min = encr_max = encr_alg; 12557c478bd9Sstevel@tonic-gate 12567c478bd9Sstevel@tonic-gate /* 12577c478bd9Sstevel@tonic-gate * set up for explosion.. for each dimension, expand output 12587c478bd9Sstevel@tonic-gate * size by the explosion factor. 12597c478bd9Sstevel@tonic-gate * 12607c478bd9Sstevel@tonic-gate * Don't include the "any" algorithms, if defined, as no 12617c478bd9Sstevel@tonic-gate * kernel policies should be set for these algorithms. 12627c478bd9Sstevel@tonic-gate */ 12637c478bd9Sstevel@tonic-gate 1264f4b3ec61Sdh155122 #define SET_EXP_MINMAX(type, wild, alg, min, max, ipss) \ 1265f4b3ec61Sdh155122 if (wild) { \ 1266f4b3ec61Sdh155122 int nalgs = ipss->ipsec_nalgs[type]; \ 1267f4b3ec61Sdh155122 if (ipss->ipsec_alglists[type][alg] != NULL) \ 12687c478bd9Sstevel@tonic-gate nalgs--; \ 12697c478bd9Sstevel@tonic-gate action_count *= nalgs; \ 12707c478bd9Sstevel@tonic-gate min = 0; \ 1271f4b3ec61Sdh155122 max = ipss->ipsec_nalgs[type] - 1; \ 12727c478bd9Sstevel@tonic-gate } 12737c478bd9Sstevel@tonic-gate 12747c478bd9Sstevel@tonic-gate SET_EXP_MINMAX(IPSEC_ALG_AUTH, wild_auth, SADB_AALG_NONE, 1275f4b3ec61Sdh155122 auth_min, auth_max, ipss); 12767c478bd9Sstevel@tonic-gate SET_EXP_MINMAX(IPSEC_ALG_AUTH, wild_eauth, SADB_AALG_NONE, 1277f4b3ec61Sdh155122 eauth_min, eauth_max, ipss); 12787c478bd9Sstevel@tonic-gate SET_EXP_MINMAX(IPSEC_ALG_ENCR, wild_encr, SADB_EALG_NONE, 1279f4b3ec61Sdh155122 encr_min, encr_max, ipss); 12807c478bd9Sstevel@tonic-gate 12817c478bd9Sstevel@tonic-gate #undef SET_EXP_MINMAX 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate /* 12847c478bd9Sstevel@tonic-gate * ok, allocate the whole mess.. 12857c478bd9Sstevel@tonic-gate */ 12867c478bd9Sstevel@tonic-gate 12877c478bd9Sstevel@tonic-gate outact = kmem_alloc(sizeof (*outact) * action_count, KM_NOSLEEP); 12887c478bd9Sstevel@tonic-gate if (outact == NULL) 12897c478bd9Sstevel@tonic-gate return (NULL); 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate /* 12927c478bd9Sstevel@tonic-gate * Now compute all combinations. Note that non-wildcarded 12937c478bd9Sstevel@tonic-gate * dimensions just get a single value from auth_min, while 12947c478bd9Sstevel@tonic-gate * wildcarded dimensions indirect through the sortlist. 12957c478bd9Sstevel@tonic-gate * 12967c478bd9Sstevel@tonic-gate * We do encryption outermost since, at this time, there's 12977c478bd9Sstevel@tonic-gate * greater difference in security and performance between 12987c478bd9Sstevel@tonic-gate * encryption algorithms vs. authentication algorithms. 12997c478bd9Sstevel@tonic-gate */ 13007c478bd9Sstevel@tonic-gate 13017c478bd9Sstevel@tonic-gate ai = 0; 13027c478bd9Sstevel@tonic-gate 1303f4b3ec61Sdh155122 #define WHICH_ALG(type, wild, idx, ipss) \ 1304f4b3ec61Sdh155122 ((wild)?(ipss->ipsec_sortlist[type][idx]):(idx)) 13057c478bd9Sstevel@tonic-gate 13067c478bd9Sstevel@tonic-gate for (encr_idx = encr_min; encr_idx <= encr_max; encr_idx++) { 1307f4b3ec61Sdh155122 encr_alg = WHICH_ALG(IPSEC_ALG_ENCR, wild_encr, encr_idx, ipss); 13087c478bd9Sstevel@tonic-gate if (wild_encr && encr_alg == SADB_EALG_NONE) 13097c478bd9Sstevel@tonic-gate continue; 13107c478bd9Sstevel@tonic-gate for (auth_idx = auth_min; auth_idx <= auth_max; auth_idx++) { 13117c478bd9Sstevel@tonic-gate auth_alg = WHICH_ALG(IPSEC_ALG_AUTH, wild_auth, 1312f4b3ec61Sdh155122 auth_idx, ipss); 13137c478bd9Sstevel@tonic-gate if (wild_auth && auth_alg == SADB_AALG_NONE) 13147c478bd9Sstevel@tonic-gate continue; 13157c478bd9Sstevel@tonic-gate for (eauth_idx = eauth_min; eauth_idx <= eauth_max; 13167c478bd9Sstevel@tonic-gate eauth_idx++) { 13177c478bd9Sstevel@tonic-gate eauth_alg = WHICH_ALG(IPSEC_ALG_AUTH, 1318f4b3ec61Sdh155122 wild_eauth, eauth_idx, ipss); 13197c478bd9Sstevel@tonic-gate if (wild_eauth && eauth_alg == SADB_AALG_NONE) 13207c478bd9Sstevel@tonic-gate continue; 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate ipsec_setup_act(&outact[ai], act, 1323f4b3ec61Sdh155122 auth_alg, encr_alg, eauth_alg, ns); 13247c478bd9Sstevel@tonic-gate ai++; 13257c478bd9Sstevel@tonic-gate } 13267c478bd9Sstevel@tonic-gate } 13277c478bd9Sstevel@tonic-gate } 13287c478bd9Sstevel@tonic-gate 13297c478bd9Sstevel@tonic-gate #undef WHICH_ALG 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate ASSERT(ai == action_count); 13327c478bd9Sstevel@tonic-gate *nact = action_count; 13337c478bd9Sstevel@tonic-gate return (outact); 13347c478bd9Sstevel@tonic-gate } 13357c478bd9Sstevel@tonic-gate 13367c478bd9Sstevel@tonic-gate /* 13377c478bd9Sstevel@tonic-gate * Extract the parts of an ipsec_prot_t from an old-style ipsec_req_t. 13387c478bd9Sstevel@tonic-gate */ 13397c478bd9Sstevel@tonic-gate static void 13402b24ab6bSSebastien Roy ipsec_prot_from_req(const ipsec_req_t *req, ipsec_prot_t *ipp) 13417c478bd9Sstevel@tonic-gate { 13427c478bd9Sstevel@tonic-gate bzero(ipp, sizeof (*ipp)); 13437c478bd9Sstevel@tonic-gate /* 13447c478bd9Sstevel@tonic-gate * ipp_use_* are bitfields. Look at "!!" in the following as a 13457c478bd9Sstevel@tonic-gate * "boolean canonicalization" operator. 13467c478bd9Sstevel@tonic-gate */ 13477c478bd9Sstevel@tonic-gate ipp->ipp_use_ah = !!(req->ipsr_ah_req & IPSEC_PREF_REQUIRED); 13487c478bd9Sstevel@tonic-gate ipp->ipp_use_esp = !!(req->ipsr_esp_req & IPSEC_PREF_REQUIRED); 1349d2f8a3dfSpwernau ipp->ipp_use_espa = !!(req->ipsr_esp_auth_alg); 13507c478bd9Sstevel@tonic-gate ipp->ipp_use_se = !!(req->ipsr_self_encap_req & IPSEC_PREF_REQUIRED); 13517c478bd9Sstevel@tonic-gate ipp->ipp_use_unique = !!((req->ipsr_ah_req|req->ipsr_esp_req) & 13527c478bd9Sstevel@tonic-gate IPSEC_PREF_UNIQUE); 13537c478bd9Sstevel@tonic-gate ipp->ipp_encr_alg = req->ipsr_esp_alg; 1354d2f8a3dfSpwernau /* 1355d2f8a3dfSpwernau * SADB_AALG_ANY is a placeholder to distinguish "any" from 1356d2f8a3dfSpwernau * "none" above. If auth is required, as determined above, 1357d2f8a3dfSpwernau * SADB_AALG_ANY becomes 0, which is the representation 1358d2f8a3dfSpwernau * of "any" and "none" in PF_KEY v2. 1359d2f8a3dfSpwernau */ 1360d2f8a3dfSpwernau ipp->ipp_auth_alg = (req->ipsr_auth_alg != SADB_AALG_ANY) ? 1361d2f8a3dfSpwernau req->ipsr_auth_alg : 0; 1362d2f8a3dfSpwernau ipp->ipp_esp_auth_alg = (req->ipsr_esp_auth_alg != SADB_AALG_ANY) ? 1363d2f8a3dfSpwernau req->ipsr_esp_auth_alg : 0; 13647c478bd9Sstevel@tonic-gate } 13657c478bd9Sstevel@tonic-gate 13667c478bd9Sstevel@tonic-gate /* 13677c478bd9Sstevel@tonic-gate * Extract a new-style action from a request. 13687c478bd9Sstevel@tonic-gate */ 13697c478bd9Sstevel@tonic-gate void 13702b24ab6bSSebastien Roy ipsec_actvec_from_req(const ipsec_req_t *req, ipsec_act_t **actp, uint_t *nactp, 1371f4b3ec61Sdh155122 netstack_t *ns) 13727c478bd9Sstevel@tonic-gate { 13737c478bd9Sstevel@tonic-gate struct ipsec_act act; 1374f4b3ec61Sdh155122 13757c478bd9Sstevel@tonic-gate bzero(&act, sizeof (act)); 13767c478bd9Sstevel@tonic-gate if ((req->ipsr_ah_req & IPSEC_PREF_NEVER) && 13777c478bd9Sstevel@tonic-gate (req->ipsr_esp_req & IPSEC_PREF_NEVER)) { 13787c478bd9Sstevel@tonic-gate act.ipa_type = IPSEC_ACT_BYPASS; 13797c478bd9Sstevel@tonic-gate } else { 13807c478bd9Sstevel@tonic-gate act.ipa_type = IPSEC_ACT_APPLY; 13817c478bd9Sstevel@tonic-gate ipsec_prot_from_req(req, &act.ipa_apply); 13827c478bd9Sstevel@tonic-gate } 1383f4b3ec61Sdh155122 *actp = ipsec_act_wildcard_expand(&act, nactp, ns); 13847c478bd9Sstevel@tonic-gate } 13857c478bd9Sstevel@tonic-gate 13867c478bd9Sstevel@tonic-gate /* 13877c478bd9Sstevel@tonic-gate * Convert a new-style "prot" back to an ipsec_req_t (more backwards compat). 13887c478bd9Sstevel@tonic-gate * We assume caller has already zero'ed *req for us. 13897c478bd9Sstevel@tonic-gate */ 13907c478bd9Sstevel@tonic-gate static int 13917c478bd9Sstevel@tonic-gate ipsec_req_from_prot(ipsec_prot_t *ipp, ipsec_req_t *req) 13927c478bd9Sstevel@tonic-gate { 13937c478bd9Sstevel@tonic-gate req->ipsr_esp_alg = ipp->ipp_encr_alg; 13947c478bd9Sstevel@tonic-gate req->ipsr_auth_alg = ipp->ipp_auth_alg; 13957c478bd9Sstevel@tonic-gate req->ipsr_esp_auth_alg = ipp->ipp_esp_auth_alg; 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate if (ipp->ipp_use_unique) { 13987c478bd9Sstevel@tonic-gate req->ipsr_ah_req |= IPSEC_PREF_UNIQUE; 13997c478bd9Sstevel@tonic-gate req->ipsr_esp_req |= IPSEC_PREF_UNIQUE; 14007c478bd9Sstevel@tonic-gate } 14017c478bd9Sstevel@tonic-gate if (ipp->ipp_use_se) 14027c478bd9Sstevel@tonic-gate req->ipsr_self_encap_req |= IPSEC_PREF_REQUIRED; 14037c478bd9Sstevel@tonic-gate if (ipp->ipp_use_ah) 14047c478bd9Sstevel@tonic-gate req->ipsr_ah_req |= IPSEC_PREF_REQUIRED; 14057c478bd9Sstevel@tonic-gate if (ipp->ipp_use_esp) 14067c478bd9Sstevel@tonic-gate req->ipsr_esp_req |= IPSEC_PREF_REQUIRED; 14077c478bd9Sstevel@tonic-gate return (sizeof (*req)); 14087c478bd9Sstevel@tonic-gate } 14097c478bd9Sstevel@tonic-gate 14107c478bd9Sstevel@tonic-gate /* 14117c478bd9Sstevel@tonic-gate * Convert a new-style action back to an ipsec_req_t (more backwards compat). 14127c478bd9Sstevel@tonic-gate * We assume caller has already zero'ed *req for us. 14137c478bd9Sstevel@tonic-gate */ 14147c478bd9Sstevel@tonic-gate static int 14157c478bd9Sstevel@tonic-gate ipsec_req_from_act(ipsec_action_t *ap, ipsec_req_t *req) 14167c478bd9Sstevel@tonic-gate { 14177c478bd9Sstevel@tonic-gate switch (ap->ipa_act.ipa_type) { 14187c478bd9Sstevel@tonic-gate case IPSEC_ACT_BYPASS: 14197c478bd9Sstevel@tonic-gate req->ipsr_ah_req = IPSEC_PREF_NEVER; 14207c478bd9Sstevel@tonic-gate req->ipsr_esp_req = IPSEC_PREF_NEVER; 14217c478bd9Sstevel@tonic-gate return (sizeof (*req)); 14227c478bd9Sstevel@tonic-gate case IPSEC_ACT_APPLY: 14237c478bd9Sstevel@tonic-gate return (ipsec_req_from_prot(&ap->ipa_act.ipa_apply, req)); 14247c478bd9Sstevel@tonic-gate } 14257c478bd9Sstevel@tonic-gate return (sizeof (*req)); 14267c478bd9Sstevel@tonic-gate } 14277c478bd9Sstevel@tonic-gate 14287c478bd9Sstevel@tonic-gate /* 14297c478bd9Sstevel@tonic-gate * Convert a new-style action back to an ipsec_req_t (more backwards compat). 14307c478bd9Sstevel@tonic-gate * We assume caller has already zero'ed *req for us. 14317c478bd9Sstevel@tonic-gate */ 14328810c16bSdanmcd int 14337c478bd9Sstevel@tonic-gate ipsec_req_from_head(ipsec_policy_head_t *ph, ipsec_req_t *req, int af) 14347c478bd9Sstevel@tonic-gate { 14357c478bd9Sstevel@tonic-gate ipsec_policy_t *p; 14367c478bd9Sstevel@tonic-gate 14376a182920Ssommerfe /* 14386a182920Ssommerfe * FULL-PERSOCK: consult hash table, too? 14396a182920Ssommerfe */ 14406a182920Ssommerfe for (p = ph->iph_root[IPSEC_INBOUND].ipr_nonhash[af]; 14417c478bd9Sstevel@tonic-gate p != NULL; 14426a182920Ssommerfe p = p->ipsp_hash.hash_next) { 14437c478bd9Sstevel@tonic-gate if ((p->ipsp_sel->ipsl_key.ipsl_valid & IPSL_WILDCARD) == 0) 14447c478bd9Sstevel@tonic-gate return (ipsec_req_from_act(p->ipsp_act, req)); 14457c478bd9Sstevel@tonic-gate } 14467c478bd9Sstevel@tonic-gate return (sizeof (*req)); 14477c478bd9Sstevel@tonic-gate } 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate /* 14507c478bd9Sstevel@tonic-gate * Based on per-socket or latched policy, convert to an appropriate 14517c478bd9Sstevel@tonic-gate * IP_SEC_OPT ipsec_req_t for the socket option; return size so we can 14527c478bd9Sstevel@tonic-gate * be tail-called from ip. 14537c478bd9Sstevel@tonic-gate */ 14547c478bd9Sstevel@tonic-gate int 14557c478bd9Sstevel@tonic-gate ipsec_req_from_conn(conn_t *connp, ipsec_req_t *req, int af) 14567c478bd9Sstevel@tonic-gate { 14577c478bd9Sstevel@tonic-gate ipsec_latch_t *ipl; 14587c478bd9Sstevel@tonic-gate int rv = sizeof (ipsec_req_t); 14597c478bd9Sstevel@tonic-gate 14607c478bd9Sstevel@tonic-gate bzero(req, sizeof (*req)); 14617c478bd9Sstevel@tonic-gate 1462bd670b35SErik Nordmark ASSERT(MUTEX_HELD(&connp->conn_lock)); 14637c478bd9Sstevel@tonic-gate ipl = connp->conn_latch; 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate /* 14667c478bd9Sstevel@tonic-gate * Find appropriate policy. First choice is latched action; 14677c478bd9Sstevel@tonic-gate * failing that, see latched policy; failing that, 14687c478bd9Sstevel@tonic-gate * look at configured policy. 14697c478bd9Sstevel@tonic-gate */ 14707c478bd9Sstevel@tonic-gate if (ipl != NULL) { 1471bd670b35SErik Nordmark if (connp->conn_latch_in_action != NULL) { 1472bd670b35SErik Nordmark rv = ipsec_req_from_act(connp->conn_latch_in_action, 1473bd670b35SErik Nordmark req); 14747c478bd9Sstevel@tonic-gate goto done; 14757c478bd9Sstevel@tonic-gate } 1476bd670b35SErik Nordmark if (connp->conn_latch_in_policy != NULL) { 1477bd670b35SErik Nordmark rv = ipsec_req_from_act( 1478bd670b35SErik Nordmark connp->conn_latch_in_policy->ipsp_act, req); 14797c478bd9Sstevel@tonic-gate goto done; 14807c478bd9Sstevel@tonic-gate } 14817c478bd9Sstevel@tonic-gate } 14827c478bd9Sstevel@tonic-gate if (connp->conn_policy != NULL) 14837c478bd9Sstevel@tonic-gate rv = ipsec_req_from_head(connp->conn_policy, req, af); 14847c478bd9Sstevel@tonic-gate done: 14857c478bd9Sstevel@tonic-gate return (rv); 14867c478bd9Sstevel@tonic-gate } 14877c478bd9Sstevel@tonic-gate 14887c478bd9Sstevel@tonic-gate void 14897c478bd9Sstevel@tonic-gate ipsec_actvec_free(ipsec_act_t *act, uint_t nact) 14907c478bd9Sstevel@tonic-gate { 14917c478bd9Sstevel@tonic-gate kmem_free(act, nact * sizeof (*act)); 14927c478bd9Sstevel@tonic-gate } 14937c478bd9Sstevel@tonic-gate 14947c478bd9Sstevel@tonic-gate /* 14957c478bd9Sstevel@tonic-gate * Consumes a reference to ipsp. 14967c478bd9Sstevel@tonic-gate */ 14977c478bd9Sstevel@tonic-gate static mblk_t * 1498bd670b35SErik Nordmark ipsec_check_loopback_policy(mblk_t *data_mp, ip_recv_attr_t *ira, 149932350c00Sdanmcd ipsec_policy_t *ipsp) 15007c478bd9Sstevel@tonic-gate { 1501bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) 1502bd670b35SErik Nordmark return (data_mp); 15037c478bd9Sstevel@tonic-gate 1504bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_LOOPBACK); 15057c478bd9Sstevel@tonic-gate 1506bd670b35SErik Nordmark IPPOL_REFRELE(ipsp); 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate /* 15097c478bd9Sstevel@tonic-gate * We should do an actual policy check here. Revisit this 151032350c00Sdanmcd * when we revisit the IPsec API. (And pass a conn_t in when we 151132350c00Sdanmcd * get there.) 15127c478bd9Sstevel@tonic-gate */ 15137c478bd9Sstevel@tonic-gate 1514bd670b35SErik Nordmark return (data_mp); 15157c478bd9Sstevel@tonic-gate } 15167c478bd9Sstevel@tonic-gate 151707b56925Ssommerfe /* 151807b56925Ssommerfe * Check that packet's inbound ports & proto match the selectors 151907b56925Ssommerfe * expected by the SAs it traversed on the way in. 152007b56925Ssommerfe */ 152107b56925Ssommerfe static boolean_t 1522bd670b35SErik Nordmark ipsec_check_ipsecin_unique(ip_recv_attr_t *ira, const char **reason, 1523bd670b35SErik Nordmark kstat_named_t **counter, uint64_t pkt_unique, netstack_t *ns) 152407b56925Ssommerfe { 15258810c16bSdanmcd uint64_t ah_mask, esp_mask; 15269c7c47efSsommerfe ipsa_t *ah_assoc; 15279c7c47efSsommerfe ipsa_t *esp_assoc; 1528f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 152907b56925Ssommerfe 1530bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 1531bd670b35SErik Nordmark ASSERT(!(ira->ira_flags & IRAF_LOOPBACK)); 15329c7c47efSsommerfe 1533bd670b35SErik Nordmark ah_assoc = ira->ira_ipsec_ah_sa; 1534bd670b35SErik Nordmark esp_assoc = ira->ira_ipsec_esp_sa; 153507b56925Ssommerfe ASSERT((ah_assoc != NULL) || (esp_assoc != NULL)); 153607b56925Ssommerfe 153707b56925Ssommerfe ah_mask = (ah_assoc != NULL) ? ah_assoc->ipsa_unique_mask : 0; 153807b56925Ssommerfe esp_mask = (esp_assoc != NULL) ? esp_assoc->ipsa_unique_mask : 0; 153907b56925Ssommerfe 154007b56925Ssommerfe if ((ah_mask == 0) && (esp_mask == 0)) 154107b56925Ssommerfe return (B_TRUE); 154207b56925Ssommerfe 154307b56925Ssommerfe /* 15448810c16bSdanmcd * The pkt_unique check will also check for tunnel mode on the SA 15458810c16bSdanmcd * vs. the tunneled_packet boolean. "Be liberal in what you receive" 15468810c16bSdanmcd * should not apply in this case. ;) 154707b56925Ssommerfe */ 154807b56925Ssommerfe 15498810c16bSdanmcd if (ah_mask != 0 && 15508810c16bSdanmcd ah_assoc->ipsa_unique_id != (pkt_unique & ah_mask)) { 155107b56925Ssommerfe *reason = "AH inner header mismatch"; 1552f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_ah_innermismatch); 155307b56925Ssommerfe return (B_FALSE); 155407b56925Ssommerfe } 15558810c16bSdanmcd if (esp_mask != 0 && 15568810c16bSdanmcd esp_assoc->ipsa_unique_id != (pkt_unique & esp_mask)) { 155707b56925Ssommerfe *reason = "ESP inner header mismatch"; 1558f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_esp_innermismatch); 155907b56925Ssommerfe return (B_FALSE); 156007b56925Ssommerfe } 156107b56925Ssommerfe return (B_TRUE); 156207b56925Ssommerfe } 156307b56925Ssommerfe 15647c478bd9Sstevel@tonic-gate static boolean_t 1565bd670b35SErik Nordmark ipsec_check_ipsecin_action(ip_recv_attr_t *ira, mblk_t *mp, ipsec_action_t *ap, 1566bd670b35SErik Nordmark ipha_t *ipha, ip6_t *ip6h, const char **reason, kstat_named_t **counter, 1567bd670b35SErik Nordmark netstack_t *ns) 15687c478bd9Sstevel@tonic-gate { 15697c478bd9Sstevel@tonic-gate boolean_t ret = B_TRUE; 15707c478bd9Sstevel@tonic-gate ipsec_prot_t *ipp; 15717c478bd9Sstevel@tonic-gate ipsa_t *ah_assoc; 15727c478bd9Sstevel@tonic-gate ipsa_t *esp_assoc; 15737c478bd9Sstevel@tonic-gate boolean_t decaps; 1574f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 15757c478bd9Sstevel@tonic-gate 15767c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 15777c478bd9Sstevel@tonic-gate (ip6h == NULL && ipha != NULL)); 15787c478bd9Sstevel@tonic-gate 1579bd670b35SErik Nordmark if (ira->ira_flags & IRAF_LOOPBACK) { 15807c478bd9Sstevel@tonic-gate /* 15817c478bd9Sstevel@tonic-gate * Besides accepting pointer-equivalent actions, we also 15827c478bd9Sstevel@tonic-gate * accept any ICMP errors we generated for ourselves, 15837c478bd9Sstevel@tonic-gate * regardless of policy. If we do not wish to make this 15847c478bd9Sstevel@tonic-gate * assumption in the future, check here, and where 1585bd670b35SErik Nordmark * IXAF_TRUSTED_ICMP is initialized in ip.c and ip6.c. 15867c478bd9Sstevel@tonic-gate */ 1587bd670b35SErik Nordmark if (ap == ira->ira_ipsec_action || 1588bd670b35SErik Nordmark (ira->ira_flags & IRAF_TRUSTED_ICMP)) 15897c478bd9Sstevel@tonic-gate return (B_TRUE); 15907c478bd9Sstevel@tonic-gate 15917c478bd9Sstevel@tonic-gate /* Deep compare necessary here?? */ 1592f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_loopback_mismatch); 15937c478bd9Sstevel@tonic-gate *reason = "loopback policy mismatch"; 15947c478bd9Sstevel@tonic-gate return (B_FALSE); 15957c478bd9Sstevel@tonic-gate } 1596bd670b35SErik Nordmark ASSERT(!(ira->ira_flags & IRAF_TRUSTED_ICMP)); 1597bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 15987c478bd9Sstevel@tonic-gate 1599bd670b35SErik Nordmark ah_assoc = ira->ira_ipsec_ah_sa; 1600bd670b35SErik Nordmark esp_assoc = ira->ira_ipsec_esp_sa; 16017c478bd9Sstevel@tonic-gate 1602bd670b35SErik Nordmark decaps = (ira->ira_flags & IRAF_IPSEC_DECAPS); 16037c478bd9Sstevel@tonic-gate 16047c478bd9Sstevel@tonic-gate switch (ap->ipa_act.ipa_type) { 16057c478bd9Sstevel@tonic-gate case IPSEC_ACT_DISCARD: 16067c478bd9Sstevel@tonic-gate case IPSEC_ACT_REJECT: 16077c478bd9Sstevel@tonic-gate /* Should "fail hard" */ 1608f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_explicit); 16097c478bd9Sstevel@tonic-gate *reason = "blocked by policy"; 16107c478bd9Sstevel@tonic-gate return (B_FALSE); 16117c478bd9Sstevel@tonic-gate 16127c478bd9Sstevel@tonic-gate case IPSEC_ACT_BYPASS: 16137c478bd9Sstevel@tonic-gate case IPSEC_ACT_CLEAR: 1614f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_secure); 16157c478bd9Sstevel@tonic-gate *reason = "expected clear, got protected"; 16167c478bd9Sstevel@tonic-gate return (B_FALSE); 16177c478bd9Sstevel@tonic-gate 16187c478bd9Sstevel@tonic-gate case IPSEC_ACT_APPLY: 16197c478bd9Sstevel@tonic-gate ipp = &ap->ipa_act.ipa_apply; 16207c478bd9Sstevel@tonic-gate /* 16217c478bd9Sstevel@tonic-gate * As of now we do the simple checks of whether 16227c478bd9Sstevel@tonic-gate * the datagram has gone through the required IPSEC 16237c478bd9Sstevel@tonic-gate * protocol constraints or not. We might have more 16247c478bd9Sstevel@tonic-gate * in the future like sensitive levels, key bits, etc. 16257c478bd9Sstevel@tonic-gate * If it fails the constraints, check whether we would 16267c478bd9Sstevel@tonic-gate * have accepted this if it had come in clear. 16277c478bd9Sstevel@tonic-gate */ 16287c478bd9Sstevel@tonic-gate if (ipp->ipp_use_ah) { 16297c478bd9Sstevel@tonic-gate if (ah_assoc == NULL) { 16307c478bd9Sstevel@tonic-gate ret = ipsec_inbound_accept_clear(mp, ipha, 16317c478bd9Sstevel@tonic-gate ip6h); 1632f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_clear); 16337c478bd9Sstevel@tonic-gate *reason = "unprotected not accepted"; 16347c478bd9Sstevel@tonic-gate break; 16357c478bd9Sstevel@tonic-gate } 16367c478bd9Sstevel@tonic-gate ASSERT(ah_assoc != NULL); 16377c478bd9Sstevel@tonic-gate ASSERT(ipp->ipp_auth_alg != 0); 16387c478bd9Sstevel@tonic-gate 16397c478bd9Sstevel@tonic-gate if (ah_assoc->ipsa_auth_alg != 16407c478bd9Sstevel@tonic-gate ipp->ipp_auth_alg) { 1641f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_bad_ahalg); 16427c478bd9Sstevel@tonic-gate *reason = "unacceptable ah alg"; 16437c478bd9Sstevel@tonic-gate ret = B_FALSE; 16447c478bd9Sstevel@tonic-gate break; 16457c478bd9Sstevel@tonic-gate } 16467c478bd9Sstevel@tonic-gate } else if (ah_assoc != NULL) { 16477c478bd9Sstevel@tonic-gate /* 16487c478bd9Sstevel@tonic-gate * Don't allow this. Check IPSEC NOTE above 16497c478bd9Sstevel@tonic-gate * ip_fanout_proto(). 16507c478bd9Sstevel@tonic-gate */ 1651f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_ah); 16527c478bd9Sstevel@tonic-gate *reason = "unexpected AH"; 16537c478bd9Sstevel@tonic-gate ret = B_FALSE; 16547c478bd9Sstevel@tonic-gate break; 16557c478bd9Sstevel@tonic-gate } 16567c478bd9Sstevel@tonic-gate if (ipp->ipp_use_esp) { 16577c478bd9Sstevel@tonic-gate if (esp_assoc == NULL) { 16587c478bd9Sstevel@tonic-gate ret = ipsec_inbound_accept_clear(mp, ipha, 16597c478bd9Sstevel@tonic-gate ip6h); 1660f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_clear); 16617c478bd9Sstevel@tonic-gate *reason = "unprotected not accepted"; 16627c478bd9Sstevel@tonic-gate break; 16637c478bd9Sstevel@tonic-gate } 16647c478bd9Sstevel@tonic-gate ASSERT(esp_assoc != NULL); 16657c478bd9Sstevel@tonic-gate ASSERT(ipp->ipp_encr_alg != 0); 16667c478bd9Sstevel@tonic-gate 16677c478bd9Sstevel@tonic-gate if (esp_assoc->ipsa_encr_alg != 16687c478bd9Sstevel@tonic-gate ipp->ipp_encr_alg) { 1669f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_bad_espealg); 16707c478bd9Sstevel@tonic-gate *reason = "unacceptable esp alg"; 16717c478bd9Sstevel@tonic-gate ret = B_FALSE; 16727c478bd9Sstevel@tonic-gate break; 16737c478bd9Sstevel@tonic-gate } 16747c478bd9Sstevel@tonic-gate /* 16757c478bd9Sstevel@tonic-gate * If the client does not need authentication, 16767c478bd9Sstevel@tonic-gate * we don't verify the alogrithm. 16777c478bd9Sstevel@tonic-gate */ 16787c478bd9Sstevel@tonic-gate if (ipp->ipp_use_espa) { 16797c478bd9Sstevel@tonic-gate if (esp_assoc->ipsa_auth_alg != 16807c478bd9Sstevel@tonic-gate ipp->ipp_esp_auth_alg) { 1681f4b3ec61Sdh155122 *counter = DROPPER(ipss, 1682f4b3ec61Sdh155122 ipds_spd_bad_espaalg); 16837c478bd9Sstevel@tonic-gate *reason = "unacceptable esp auth alg"; 16847c478bd9Sstevel@tonic-gate ret = B_FALSE; 16857c478bd9Sstevel@tonic-gate break; 16867c478bd9Sstevel@tonic-gate } 16877c478bd9Sstevel@tonic-gate } 16887c478bd9Sstevel@tonic-gate } else if (esp_assoc != NULL) { 16897c478bd9Sstevel@tonic-gate /* 16907c478bd9Sstevel@tonic-gate * Don't allow this. Check IPSEC NOTE above 16917c478bd9Sstevel@tonic-gate * ip_fanout_proto(). 16927c478bd9Sstevel@tonic-gate */ 1693f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_esp); 16947c478bd9Sstevel@tonic-gate *reason = "unexpected ESP"; 16957c478bd9Sstevel@tonic-gate ret = B_FALSE; 16967c478bd9Sstevel@tonic-gate break; 16977c478bd9Sstevel@tonic-gate } 16987c478bd9Sstevel@tonic-gate if (ipp->ipp_use_se) { 16997c478bd9Sstevel@tonic-gate if (!decaps) { 17007c478bd9Sstevel@tonic-gate ret = ipsec_inbound_accept_clear(mp, ipha, 17017c478bd9Sstevel@tonic-gate ip6h); 17027c478bd9Sstevel@tonic-gate if (!ret) { 17037c478bd9Sstevel@tonic-gate /* XXX mutant? */ 1704f4b3ec61Sdh155122 *counter = DROPPER(ipss, 1705f4b3ec61Sdh155122 ipds_spd_bad_selfencap); 17067c478bd9Sstevel@tonic-gate *reason = "self encap not found"; 17077c478bd9Sstevel@tonic-gate break; 17087c478bd9Sstevel@tonic-gate } 17097c478bd9Sstevel@tonic-gate } 17107c478bd9Sstevel@tonic-gate } else if (decaps) { 17117c478bd9Sstevel@tonic-gate /* 17127c478bd9Sstevel@tonic-gate * XXX If the packet comes in tunneled and the 17137c478bd9Sstevel@tonic-gate * recipient does not expect it to be tunneled, it 17147c478bd9Sstevel@tonic-gate * is okay. But we drop to be consistent with the 17157c478bd9Sstevel@tonic-gate * other cases. 17167c478bd9Sstevel@tonic-gate */ 1717f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_got_selfencap); 17187c478bd9Sstevel@tonic-gate *reason = "unexpected self encap"; 17197c478bd9Sstevel@tonic-gate ret = B_FALSE; 17207c478bd9Sstevel@tonic-gate break; 17217c478bd9Sstevel@tonic-gate } 1722bd670b35SErik Nordmark if (ira->ira_ipsec_action != NULL) { 17237c478bd9Sstevel@tonic-gate /* 17247c478bd9Sstevel@tonic-gate * This can happen if we do a double policy-check on 17257c478bd9Sstevel@tonic-gate * a packet 17267c478bd9Sstevel@tonic-gate * XXX XXX should fix this case! 17277c478bd9Sstevel@tonic-gate */ 1728bd670b35SErik Nordmark IPACT_REFRELE(ira->ira_ipsec_action); 17297c478bd9Sstevel@tonic-gate } 1730bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 1731bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_action == NULL); 17327c478bd9Sstevel@tonic-gate IPACT_REFHOLD(ap); 1733bd670b35SErik Nordmark ira->ira_ipsec_action = ap; 17347c478bd9Sstevel@tonic-gate break; /* from switch */ 17357c478bd9Sstevel@tonic-gate } 17367c478bd9Sstevel@tonic-gate return (ret); 17377c478bd9Sstevel@tonic-gate } 17387c478bd9Sstevel@tonic-gate 17397c478bd9Sstevel@tonic-gate static boolean_t 17407c478bd9Sstevel@tonic-gate spd_match_inbound_ids(ipsec_latch_t *ipl, ipsa_t *sa) 17417c478bd9Sstevel@tonic-gate { 17427c478bd9Sstevel@tonic-gate ASSERT(ipl->ipl_ids_latched == B_TRUE); 17437c478bd9Sstevel@tonic-gate return ipsid_equal(ipl->ipl_remote_cid, sa->ipsa_src_cid) && 17447c478bd9Sstevel@tonic-gate ipsid_equal(ipl->ipl_local_cid, sa->ipsa_dst_cid); 17457c478bd9Sstevel@tonic-gate } 17467c478bd9Sstevel@tonic-gate 17477c478bd9Sstevel@tonic-gate /* 17488810c16bSdanmcd * Takes a latched conn and an inbound packet and returns a unique_id suitable 17498810c16bSdanmcd * for SA comparisons. Most of the time we will copy from the conn_t, but 17508810c16bSdanmcd * there are cases when the conn_t is latched but it has wildcard selectors, 17518810c16bSdanmcd * and then we need to fallback to scooping them out of the packet. 17528810c16bSdanmcd * 17538810c16bSdanmcd * Assume we'll never have 0 with a conn_t present, so use 0 as a failure. We 17548810c16bSdanmcd * can get away with this because we only have non-zero ports/proto for 17558810c16bSdanmcd * latched conn_ts. 17568810c16bSdanmcd * 17578810c16bSdanmcd * Ideal candidate for an "inline" keyword, as we're JUST convoluted enough 17588810c16bSdanmcd * to not be a nice macro. 17598810c16bSdanmcd */ 17608810c16bSdanmcd static uint64_t 17618810c16bSdanmcd conn_to_unique(conn_t *connp, mblk_t *data_mp, ipha_t *ipha, ip6_t *ip6h) 17628810c16bSdanmcd { 17638810c16bSdanmcd ipsec_selector_t sel; 1764bd670b35SErik Nordmark uint8_t ulp = connp->conn_proto; 17658810c16bSdanmcd 1766bd670b35SErik Nordmark ASSERT(connp->conn_latch_in_policy != NULL); 17678810c16bSdanmcd 17688810c16bSdanmcd if ((ulp == IPPROTO_TCP || ulp == IPPROTO_UDP || ulp == IPPROTO_SCTP) && 17698810c16bSdanmcd (connp->conn_fport == 0 || connp->conn_lport == 0)) { 17708810c16bSdanmcd /* Slow path - we gotta grab from the packet. */ 17718810c16bSdanmcd if (ipsec_init_inbound_sel(&sel, data_mp, ipha, ip6h, 17728810c16bSdanmcd SEL_NONE) != SELRET_SUCCESS) { 17738810c16bSdanmcd /* Failure -> have caller free packet with ENOMEM. */ 17748810c16bSdanmcd return (0); 17758810c16bSdanmcd } 17768810c16bSdanmcd return (SA_UNIQUE_ID(sel.ips_remote_port, sel.ips_local_port, 17778810c16bSdanmcd sel.ips_protocol, 0)); 17788810c16bSdanmcd } 17798810c16bSdanmcd 17808810c16bSdanmcd #ifdef DEBUG_NOT_UNTIL_6478464 17818810c16bSdanmcd if (ipsec_init_inbound_sel(&sel, data_mp, ipha, ip6h, SEL_NONE) == 17828810c16bSdanmcd SELRET_SUCCESS) { 17838810c16bSdanmcd ASSERT(sel.ips_local_port == connp->conn_lport); 17848810c16bSdanmcd ASSERT(sel.ips_remote_port == connp->conn_fport); 1785bd670b35SErik Nordmark ASSERT(sel.ips_protocol == connp->conn_proto); 17868810c16bSdanmcd } 1787bd670b35SErik Nordmark ASSERT(connp->conn_proto != 0); 17888810c16bSdanmcd #endif 17898810c16bSdanmcd 17908810c16bSdanmcd return (SA_UNIQUE_ID(connp->conn_fport, connp->conn_lport, ulp, 0)); 17918810c16bSdanmcd } 17928810c16bSdanmcd 17938810c16bSdanmcd /* 1794bd670b35SErik Nordmark * Called to check policy on a latched connection. 1795bd670b35SErik Nordmark * Note that we don't dereference conn_latch or conn_ihere since the conn might 1796bd670b35SErik Nordmark * be closing. The caller passes a held ipsec_latch_t instead. 17977c478bd9Sstevel@tonic-gate */ 1798bd670b35SErik Nordmark static boolean_t 1799bd670b35SErik Nordmark ipsec_check_ipsecin_latch(ip_recv_attr_t *ira, mblk_t *mp, ipsec_latch_t *ipl, 1800bd670b35SErik Nordmark ipsec_action_t *ap, ipha_t *ipha, ip6_t *ip6h, const char **reason, 1801bd670b35SErik Nordmark kstat_named_t **counter, conn_t *connp, netstack_t *ns) 18027c478bd9Sstevel@tonic-gate { 1803f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 1804f4b3ec61Sdh155122 18057c478bd9Sstevel@tonic-gate ASSERT(ipl->ipl_ids_latched == B_TRUE); 1806bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 18077c478bd9Sstevel@tonic-gate 1808bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_LOOPBACK)) { 18099c7c47efSsommerfe /* 18109c7c47efSsommerfe * Over loopback, there aren't real security associations, 18119c7c47efSsommerfe * so there are neither identities nor "unique" values 18129c7c47efSsommerfe * for us to check the packet against. 18139c7c47efSsommerfe */ 1814bd670b35SErik Nordmark if (ira->ira_ipsec_ah_sa != NULL) { 1815bd670b35SErik Nordmark if (!spd_match_inbound_ids(ipl, 1816bd670b35SErik Nordmark ira->ira_ipsec_ah_sa)) { 1817f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_ah_badid); 18187c478bd9Sstevel@tonic-gate *reason = "AH identity mismatch"; 18197c478bd9Sstevel@tonic-gate return (B_FALSE); 18207c478bd9Sstevel@tonic-gate } 1821bd670b35SErik Nordmark } 18227c478bd9Sstevel@tonic-gate 1823bd670b35SErik Nordmark if (ira->ira_ipsec_esp_sa != NULL) { 1824bd670b35SErik Nordmark if (!spd_match_inbound_ids(ipl, 1825bd670b35SErik Nordmark ira->ira_ipsec_esp_sa)) { 1826f4b3ec61Sdh155122 *counter = DROPPER(ipss, ipds_spd_esp_badid); 18277c478bd9Sstevel@tonic-gate *reason = "ESP identity mismatch"; 18287c478bd9Sstevel@tonic-gate return (B_FALSE); 18297c478bd9Sstevel@tonic-gate } 1830bd670b35SErik Nordmark } 18317c478bd9Sstevel@tonic-gate 18328810c16bSdanmcd /* 18338810c16bSdanmcd * Can fudge pkt_unique from connp because we're latched. 18348810c16bSdanmcd * In DEBUG kernels (see conn_to_unique()'s implementation), 18358810c16bSdanmcd * verify this even if it REALLY slows things down. 18368810c16bSdanmcd */ 1837bd670b35SErik Nordmark if (!ipsec_check_ipsecin_unique(ira, reason, counter, 1838bd670b35SErik Nordmark conn_to_unique(connp, mp, ipha, ip6h), ns)) { 183907b56925Ssommerfe return (B_FALSE); 18409c7c47efSsommerfe } 18419c7c47efSsommerfe } 1842bd670b35SErik Nordmark return (ipsec_check_ipsecin_action(ira, mp, ap, ipha, ip6h, reason, 1843bd670b35SErik Nordmark counter, ns)); 18447c478bd9Sstevel@tonic-gate } 18457c478bd9Sstevel@tonic-gate 18467c478bd9Sstevel@tonic-gate /* 18477c478bd9Sstevel@tonic-gate * Check to see whether this secured datagram meets the policy 18487c478bd9Sstevel@tonic-gate * constraints specified in ipsp. 18497c478bd9Sstevel@tonic-gate * 18507c478bd9Sstevel@tonic-gate * Called from ipsec_check_global_policy, and ipsec_check_inbound_policy. 18517c478bd9Sstevel@tonic-gate * 18527c478bd9Sstevel@tonic-gate * Consumes a reference to ipsp. 1853bd670b35SErik Nordmark * Returns the mblk if ok. 18547c478bd9Sstevel@tonic-gate */ 18557c478bd9Sstevel@tonic-gate static mblk_t * 1856bd670b35SErik Nordmark ipsec_check_ipsecin_policy(mblk_t *data_mp, ipsec_policy_t *ipsp, 1857bd670b35SErik Nordmark ipha_t *ipha, ip6_t *ip6h, uint64_t pkt_unique, ip_recv_attr_t *ira, 1858bd670b35SErik Nordmark netstack_t *ns) 18597c478bd9Sstevel@tonic-gate { 18607c478bd9Sstevel@tonic-gate ipsec_action_t *ap; 18617c478bd9Sstevel@tonic-gate const char *reason = "no policy actions found"; 1862f4b3ec61Sdh155122 ip_stack_t *ipst = ns->netstack_ip; 1863bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec; 1864f4b3ec61Sdh155122 kstat_named_t *counter; 1865f4b3ec61Sdh155122 1866f4b3ec61Sdh155122 counter = DROPPER(ipss, ipds_spd_got_secure); 18677c478bd9Sstevel@tonic-gate 18687c478bd9Sstevel@tonic-gate ASSERT(ipsp != NULL); 18697c478bd9Sstevel@tonic-gate 18707c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 18717c478bd9Sstevel@tonic-gate (ip6h == NULL && ipha != NULL)); 18727c478bd9Sstevel@tonic-gate 1873bd670b35SErik Nordmark if (ira->ira_flags & IRAF_LOOPBACK) 1874bd670b35SErik Nordmark return (ipsec_check_loopback_policy(data_mp, ira, ipsp)); 18757c478bd9Sstevel@tonic-gate 1876bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 18779c7c47efSsommerfe 1878bd670b35SErik Nordmark if (ira->ira_ipsec_action != NULL) { 18797c478bd9Sstevel@tonic-gate /* 18807c478bd9Sstevel@tonic-gate * this can happen if we do a double policy-check on a packet 18817c478bd9Sstevel@tonic-gate * Would be nice to be able to delete this test.. 18827c478bd9Sstevel@tonic-gate */ 1883bd670b35SErik Nordmark IPACT_REFRELE(ira->ira_ipsec_action); 18847c478bd9Sstevel@tonic-gate } 1885bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_action == NULL); 18867c478bd9Sstevel@tonic-gate 1887bd670b35SErik Nordmark if (!SA_IDS_MATCH(ira->ira_ipsec_ah_sa, ira->ira_ipsec_esp_sa)) { 18887c478bd9Sstevel@tonic-gate reason = "inbound AH and ESP identities differ"; 1889f4b3ec61Sdh155122 counter = DROPPER(ipss, ipds_spd_ahesp_diffid); 18907c478bd9Sstevel@tonic-gate goto drop; 18917c478bd9Sstevel@tonic-gate } 18927c478bd9Sstevel@tonic-gate 1893bd670b35SErik Nordmark if (!ipsec_check_ipsecin_unique(ira, &reason, &counter, pkt_unique, 1894bd670b35SErik Nordmark ns)) 189507b56925Ssommerfe goto drop; 189607b56925Ssommerfe 18977c478bd9Sstevel@tonic-gate /* 18987c478bd9Sstevel@tonic-gate * Ok, now loop through the possible actions and see if any 18997c478bd9Sstevel@tonic-gate * of them work for us. 19007c478bd9Sstevel@tonic-gate */ 19017c478bd9Sstevel@tonic-gate 19027c478bd9Sstevel@tonic-gate for (ap = ipsp->ipsp_act; ap != NULL; ap = ap->ipa_next) { 1903bd670b35SErik Nordmark if (ipsec_check_ipsecin_action(ira, data_mp, ap, 1904bd670b35SErik Nordmark ipha, ip6h, &reason, &counter, ns)) { 1905f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); 1906bd670b35SErik Nordmark IPPOL_REFRELE(ipsp); 1907bd670b35SErik Nordmark return (data_mp); 19087c478bd9Sstevel@tonic-gate } 19097c478bd9Sstevel@tonic-gate } 19107c478bd9Sstevel@tonic-gate drop: 1911f4b3ec61Sdh155122 ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, SL_ERROR|SL_WARN|SL_CONSOLE, 19127c478bd9Sstevel@tonic-gate "ipsec inbound policy mismatch: %s, packet dropped\n", 19137c478bd9Sstevel@tonic-gate reason); 1914bd670b35SErik Nordmark IPPOL_REFRELE(ipsp); 1915bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_action == NULL); 1916f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); 1917bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, counter, 1918f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 19197c478bd9Sstevel@tonic-gate return (NULL); 19207c478bd9Sstevel@tonic-gate } 19217c478bd9Sstevel@tonic-gate 19227c478bd9Sstevel@tonic-gate /* 19237c478bd9Sstevel@tonic-gate * sleazy prefix-length-based compare. 19247c478bd9Sstevel@tonic-gate * another inlining candidate.. 19257c478bd9Sstevel@tonic-gate */ 19268810c16bSdanmcd boolean_t 19277c478bd9Sstevel@tonic-gate ip_addr_match(uint8_t *addr1, int pfxlen, in6_addr_t *addr2p) 19287c478bd9Sstevel@tonic-gate { 19297c478bd9Sstevel@tonic-gate int offset = pfxlen>>3; 19307c478bd9Sstevel@tonic-gate int bitsleft = pfxlen & 7; 19317c478bd9Sstevel@tonic-gate uint8_t *addr2 = (uint8_t *)addr2p; 19327c478bd9Sstevel@tonic-gate 19337c478bd9Sstevel@tonic-gate /* 19347c478bd9Sstevel@tonic-gate * and there was much evil.. 19357c478bd9Sstevel@tonic-gate * XXX should inline-expand the bcmp here and do this 32 bits 19367c478bd9Sstevel@tonic-gate * or 64 bits at a time.. 19377c478bd9Sstevel@tonic-gate */ 19387c478bd9Sstevel@tonic-gate return ((bcmp(addr1, addr2, offset) == 0) && 19397c478bd9Sstevel@tonic-gate ((bitsleft == 0) || 1940daa41a61Sdanmcd (((addr1[offset] ^ addr2[offset]) & (0xff<<(8-bitsleft))) == 0))); 19417c478bd9Sstevel@tonic-gate } 19427c478bd9Sstevel@tonic-gate 19437c478bd9Sstevel@tonic-gate static ipsec_policy_t * 19446a182920Ssommerfe ipsec_find_policy_chain(ipsec_policy_t *best, ipsec_policy_t *chain, 19456a182920Ssommerfe ipsec_selector_t *sel, boolean_t is_icmp_inv_acq) 19467c478bd9Sstevel@tonic-gate { 19477c478bd9Sstevel@tonic-gate ipsec_selkey_t *isel; 19486a182920Ssommerfe ipsec_policy_t *p; 19497c478bd9Sstevel@tonic-gate int bpri = best ? best->ipsp_prio : 0; 19507c478bd9Sstevel@tonic-gate 19516a182920Ssommerfe for (p = chain; p != NULL; p = p->ipsp_hash.hash_next) { 19526a182920Ssommerfe uint32_t valid; 19537c478bd9Sstevel@tonic-gate 19547c478bd9Sstevel@tonic-gate if (p->ipsp_prio <= bpri) 19557c478bd9Sstevel@tonic-gate continue; 19567c478bd9Sstevel@tonic-gate isel = &p->ipsp_sel->ipsl_key; 19577c478bd9Sstevel@tonic-gate valid = isel->ipsl_valid; 19587c478bd9Sstevel@tonic-gate 19597c478bd9Sstevel@tonic-gate if ((valid & IPSL_PROTOCOL) && 19607c478bd9Sstevel@tonic-gate (isel->ipsl_proto != sel->ips_protocol)) 19617c478bd9Sstevel@tonic-gate continue; 19627c478bd9Sstevel@tonic-gate 19637c478bd9Sstevel@tonic-gate if ((valid & IPSL_REMOTE_ADDR) && 19647c478bd9Sstevel@tonic-gate !ip_addr_match((uint8_t *)&isel->ipsl_remote, 1965daa41a61Sdanmcd isel->ipsl_remote_pfxlen, &sel->ips_remote_addr_v6)) 19667c478bd9Sstevel@tonic-gate continue; 19677c478bd9Sstevel@tonic-gate 19687c478bd9Sstevel@tonic-gate if ((valid & IPSL_LOCAL_ADDR) && 19697c478bd9Sstevel@tonic-gate !ip_addr_match((uint8_t *)&isel->ipsl_local, 1970daa41a61Sdanmcd isel->ipsl_local_pfxlen, &sel->ips_local_addr_v6)) 19717c478bd9Sstevel@tonic-gate continue; 19727c478bd9Sstevel@tonic-gate 19737c478bd9Sstevel@tonic-gate if ((valid & IPSL_REMOTE_PORT) && 19747c478bd9Sstevel@tonic-gate isel->ipsl_rport != sel->ips_remote_port) 19757c478bd9Sstevel@tonic-gate continue; 19767c478bd9Sstevel@tonic-gate 19777c478bd9Sstevel@tonic-gate if ((valid & IPSL_LOCAL_PORT) && 19787c478bd9Sstevel@tonic-gate isel->ipsl_lport != sel->ips_local_port) 19797c478bd9Sstevel@tonic-gate continue; 19807c478bd9Sstevel@tonic-gate 19817c478bd9Sstevel@tonic-gate if (!is_icmp_inv_acq) { 19827c478bd9Sstevel@tonic-gate if ((valid & IPSL_ICMP_TYPE) && 19837c478bd9Sstevel@tonic-gate (isel->ipsl_icmp_type > sel->ips_icmp_type || 19847c478bd9Sstevel@tonic-gate isel->ipsl_icmp_type_end < sel->ips_icmp_type)) { 19857c478bd9Sstevel@tonic-gate continue; 19867c478bd9Sstevel@tonic-gate } 19877c478bd9Sstevel@tonic-gate 19887c478bd9Sstevel@tonic-gate if ((valid & IPSL_ICMP_CODE) && 19897c478bd9Sstevel@tonic-gate (isel->ipsl_icmp_code > sel->ips_icmp_code || 19907c478bd9Sstevel@tonic-gate isel->ipsl_icmp_code_end < 19917c478bd9Sstevel@tonic-gate sel->ips_icmp_code)) { 19927c478bd9Sstevel@tonic-gate continue; 19937c478bd9Sstevel@tonic-gate } 19947c478bd9Sstevel@tonic-gate } else { 19957c478bd9Sstevel@tonic-gate /* 19967c478bd9Sstevel@tonic-gate * special case for icmp inverse acquire 19977c478bd9Sstevel@tonic-gate * we only want policies that aren't drop/pass 19987c478bd9Sstevel@tonic-gate */ 19997c478bd9Sstevel@tonic-gate if (p->ipsp_act->ipa_act.ipa_type != IPSEC_ACT_APPLY) 20007c478bd9Sstevel@tonic-gate continue; 20017c478bd9Sstevel@tonic-gate } 20027c478bd9Sstevel@tonic-gate 20037c478bd9Sstevel@tonic-gate /* we matched all the packet-port-field selectors! */ 20046a182920Ssommerfe best = p; 20057c478bd9Sstevel@tonic-gate bpri = p->ipsp_prio; 20067c478bd9Sstevel@tonic-gate } 20077c478bd9Sstevel@tonic-gate 20086a182920Ssommerfe return (best); 20096a182920Ssommerfe } 20106a182920Ssommerfe 20117c478bd9Sstevel@tonic-gate /* 20126a182920Ssommerfe * Try to find and return the best policy entry under a given policy 20136a182920Ssommerfe * root for a given set of selectors; the first parameter "best" is 20146a182920Ssommerfe * the current best policy so far. If "best" is non-null, we have a 20156a182920Ssommerfe * reference to it. We return a reference to a policy; if that policy 20166a182920Ssommerfe * is not the original "best", we need to release that reference 20176a182920Ssommerfe * before returning. 20186a182920Ssommerfe */ 20198810c16bSdanmcd ipsec_policy_t * 20208810c16bSdanmcd ipsec_find_policy_head(ipsec_policy_t *best, ipsec_policy_head_t *head, 2021bd670b35SErik Nordmark int direction, ipsec_selector_t *sel) 20226a182920Ssommerfe { 20236a182920Ssommerfe ipsec_policy_t *curbest; 20246a182920Ssommerfe ipsec_policy_root_t *root; 20256a182920Ssommerfe uint8_t is_icmp_inv_acq = sel->ips_is_icmp_inv_acq; 20266a182920Ssommerfe int af = sel->ips_isv4 ? IPSEC_AF_V4 : IPSEC_AF_V6; 20276a182920Ssommerfe 20286a182920Ssommerfe curbest = best; 20296a182920Ssommerfe root = &head->iph_root[direction]; 20306a182920Ssommerfe 20316a182920Ssommerfe #ifdef DEBUG 20326a182920Ssommerfe if (is_icmp_inv_acq) { 20336a182920Ssommerfe if (sel->ips_isv4) { 20346a182920Ssommerfe if (sel->ips_protocol != IPPROTO_ICMP) { 20356a182920Ssommerfe cmn_err(CE_WARN, "ipsec_find_policy_head:" 2036daa41a61Sdanmcd " expecting icmp, got %d", 2037daa41a61Sdanmcd sel->ips_protocol); 20386a182920Ssommerfe } 20396a182920Ssommerfe } else { 20406a182920Ssommerfe if (sel->ips_protocol != IPPROTO_ICMPV6) { 20416a182920Ssommerfe cmn_err(CE_WARN, "ipsec_find_policy_head:" 2042daa41a61Sdanmcd " expecting icmpv6, got %d", 2043daa41a61Sdanmcd sel->ips_protocol); 20446a182920Ssommerfe } 20456a182920Ssommerfe } 20466a182920Ssommerfe } 20476a182920Ssommerfe #endif 20486a182920Ssommerfe 20496a182920Ssommerfe rw_enter(&head->iph_lock, RW_READER); 20506a182920Ssommerfe 20516a182920Ssommerfe if (root->ipr_nchains > 0) { 20526a182920Ssommerfe curbest = ipsec_find_policy_chain(curbest, 20538810c16bSdanmcd root->ipr_hash[selector_hash(sel, root)].hash_head, sel, 20548810c16bSdanmcd is_icmp_inv_acq); 20556a182920Ssommerfe } 20566a182920Ssommerfe curbest = ipsec_find_policy_chain(curbest, root->ipr_nonhash[af], sel, 20576a182920Ssommerfe is_icmp_inv_acq); 20586a182920Ssommerfe 20596a182920Ssommerfe /* 20606a182920Ssommerfe * Adjust reference counts if we found anything new. 20617c478bd9Sstevel@tonic-gate */ 20627c478bd9Sstevel@tonic-gate if (curbest != best) { 20636a182920Ssommerfe ASSERT(curbest != NULL); 20647c478bd9Sstevel@tonic-gate IPPOL_REFHOLD(curbest); 20656a182920Ssommerfe 20667c478bd9Sstevel@tonic-gate if (best != NULL) { 2067bd670b35SErik Nordmark IPPOL_REFRELE(best); 20687c478bd9Sstevel@tonic-gate } 20697c478bd9Sstevel@tonic-gate } 20707c478bd9Sstevel@tonic-gate 20717c478bd9Sstevel@tonic-gate rw_exit(&head->iph_lock); 20727c478bd9Sstevel@tonic-gate 20737c478bd9Sstevel@tonic-gate return (curbest); 20747c478bd9Sstevel@tonic-gate } 20757c478bd9Sstevel@tonic-gate 20767c478bd9Sstevel@tonic-gate /* 20777c478bd9Sstevel@tonic-gate * Find the best system policy (either global or per-interface) which 20787c478bd9Sstevel@tonic-gate * applies to the given selector; look in all the relevant policy roots 20797c478bd9Sstevel@tonic-gate * to figure out which policy wins. 20807c478bd9Sstevel@tonic-gate * 20817c478bd9Sstevel@tonic-gate * Returns a reference to a policy; caller must release this 20827c478bd9Sstevel@tonic-gate * reference when done. 20837c478bd9Sstevel@tonic-gate */ 20847c478bd9Sstevel@tonic-gate ipsec_policy_t * 2085bd670b35SErik Nordmark ipsec_find_policy(int direction, const conn_t *connp, ipsec_selector_t *sel, 2086bd670b35SErik Nordmark netstack_t *ns) 20877c478bd9Sstevel@tonic-gate { 20887c478bd9Sstevel@tonic-gate ipsec_policy_t *p; 2089f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 20907c478bd9Sstevel@tonic-gate 2091f4b3ec61Sdh155122 p = ipsec_find_policy_head(NULL, &ipss->ipsec_system_policy, 2092bd670b35SErik Nordmark direction, sel); 20937c478bd9Sstevel@tonic-gate if ((connp != NULL) && (connp->conn_policy != NULL)) { 20947c478bd9Sstevel@tonic-gate p = ipsec_find_policy_head(p, connp->conn_policy, 2095bd670b35SErik Nordmark direction, sel); 20967c478bd9Sstevel@tonic-gate } 20977c478bd9Sstevel@tonic-gate 20987c478bd9Sstevel@tonic-gate return (p); 20997c478bd9Sstevel@tonic-gate } 21007c478bd9Sstevel@tonic-gate 21017c478bd9Sstevel@tonic-gate /* 21027c478bd9Sstevel@tonic-gate * Check with global policy and see whether this inbound 21037c478bd9Sstevel@tonic-gate * packet meets the policy constraints. 21047c478bd9Sstevel@tonic-gate * 21057c478bd9Sstevel@tonic-gate * Locate appropriate policy from global policy, supplemented by the 21067c478bd9Sstevel@tonic-gate * conn's configured and/or cached policy if the conn is supplied. 21077c478bd9Sstevel@tonic-gate * 21087c478bd9Sstevel@tonic-gate * Dispatch to ipsec_check_ipsecin_policy if we have policy and an 21097c478bd9Sstevel@tonic-gate * encrypted packet to see if they match. 21107c478bd9Sstevel@tonic-gate * 21117c478bd9Sstevel@tonic-gate * Otherwise, see if the policy allows cleartext; if not, drop it on the 21127c478bd9Sstevel@tonic-gate * floor. 21137c478bd9Sstevel@tonic-gate */ 21147c478bd9Sstevel@tonic-gate mblk_t * 2115bd670b35SErik Nordmark ipsec_check_global_policy(mblk_t *data_mp, conn_t *connp, 2116bd670b35SErik Nordmark ipha_t *ipha, ip6_t *ip6h, ip_recv_attr_t *ira, netstack_t *ns) 21177c478bd9Sstevel@tonic-gate { 21187c478bd9Sstevel@tonic-gate ipsec_policy_t *p; 21197c478bd9Sstevel@tonic-gate ipsec_selector_t sel; 21207c478bd9Sstevel@tonic-gate boolean_t policy_present; 21217c478bd9Sstevel@tonic-gate kstat_named_t *counter; 21228810c16bSdanmcd uint64_t pkt_unique; 2123f4b3ec61Sdh155122 ip_stack_t *ipst = ns->netstack_ip; 2124bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec; 21257c478bd9Sstevel@tonic-gate 21267c478bd9Sstevel@tonic-gate sel.ips_is_icmp_inv_acq = 0; 21277c478bd9Sstevel@tonic-gate 21287c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 21297c478bd9Sstevel@tonic-gate (ip6h == NULL && ipha != NULL)); 21307c478bd9Sstevel@tonic-gate 21317c478bd9Sstevel@tonic-gate if (ipha != NULL) 2132f4b3ec61Sdh155122 policy_present = ipss->ipsec_inbound_v4_policy_present; 21337c478bd9Sstevel@tonic-gate else 2134f4b3ec61Sdh155122 policy_present = ipss->ipsec_inbound_v6_policy_present; 21357c478bd9Sstevel@tonic-gate 21367c478bd9Sstevel@tonic-gate if (!policy_present && connp == NULL) { 21377c478bd9Sstevel@tonic-gate /* 21387c478bd9Sstevel@tonic-gate * No global policy and no per-socket policy; 21397c478bd9Sstevel@tonic-gate * just pass it back (but we shouldn't get here in that case) 21407c478bd9Sstevel@tonic-gate */ 2141bd670b35SErik Nordmark return (data_mp); 21427c478bd9Sstevel@tonic-gate } 21437c478bd9Sstevel@tonic-gate 21447c478bd9Sstevel@tonic-gate /* 21457c478bd9Sstevel@tonic-gate * If we have cached policy, use it. 21467c478bd9Sstevel@tonic-gate * Otherwise consult system policy. 21477c478bd9Sstevel@tonic-gate */ 21487c478bd9Sstevel@tonic-gate if ((connp != NULL) && (connp->conn_latch != NULL)) { 2149bd670b35SErik Nordmark p = connp->conn_latch_in_policy; 21507c478bd9Sstevel@tonic-gate if (p != NULL) { 21517c478bd9Sstevel@tonic-gate IPPOL_REFHOLD(p); 21527c478bd9Sstevel@tonic-gate } 21538810c16bSdanmcd /* 21548810c16bSdanmcd * Fudge sel for UNIQUE_ID setting below. 21558810c16bSdanmcd */ 21568810c16bSdanmcd pkt_unique = conn_to_unique(connp, data_mp, ipha, ip6h); 21577c478bd9Sstevel@tonic-gate } else { 21587c478bd9Sstevel@tonic-gate /* Initialize the ports in the selector */ 21598810c16bSdanmcd if (ipsec_init_inbound_sel(&sel, data_mp, ipha, ip6h, 21608810c16bSdanmcd SEL_NONE) == SELRET_NOMEM) { 21617c478bd9Sstevel@tonic-gate /* 21627c478bd9Sstevel@tonic-gate * Technically not a policy mismatch, but it is 21637c478bd9Sstevel@tonic-gate * an internal failure. 21647c478bd9Sstevel@tonic-gate */ 216532350c00Sdanmcd ipsec_log_policy_failure(IPSEC_POLICY_MISMATCH, 21665d3b8cb7SBill Sommerfeld "ipsec_init_inbound_sel", ipha, ip6h, B_TRUE, ns); 2167f4b3ec61Sdh155122 counter = DROPPER(ipss, ipds_spd_nomem); 21687c478bd9Sstevel@tonic-gate goto fail; 21697c478bd9Sstevel@tonic-gate } 21707c478bd9Sstevel@tonic-gate 21717c478bd9Sstevel@tonic-gate /* 21727c478bd9Sstevel@tonic-gate * Find the policy which best applies. 21737c478bd9Sstevel@tonic-gate * 21747c478bd9Sstevel@tonic-gate * If we find global policy, we should look at both 21757c478bd9Sstevel@tonic-gate * local policy and global policy and see which is 21767c478bd9Sstevel@tonic-gate * stronger and match accordingly. 21777c478bd9Sstevel@tonic-gate * 21787c478bd9Sstevel@tonic-gate * If we don't find a global policy, check with 21797c478bd9Sstevel@tonic-gate * local policy alone. 21807c478bd9Sstevel@tonic-gate */ 21817c478bd9Sstevel@tonic-gate 2182bd670b35SErik Nordmark p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, &sel, ns); 21838810c16bSdanmcd pkt_unique = SA_UNIQUE_ID(sel.ips_remote_port, 21848810c16bSdanmcd sel.ips_local_port, sel.ips_protocol, 0); 21857c478bd9Sstevel@tonic-gate } 21867c478bd9Sstevel@tonic-gate 21877c478bd9Sstevel@tonic-gate if (p == NULL) { 2188bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 21897c478bd9Sstevel@tonic-gate /* 21907c478bd9Sstevel@tonic-gate * We have no policy; default to succeeding. 21917c478bd9Sstevel@tonic-gate * XXX paranoid system design doesn't do this. 21927c478bd9Sstevel@tonic-gate */ 2193f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); 2194bd670b35SErik Nordmark return (data_mp); 21957c478bd9Sstevel@tonic-gate } else { 2196f4b3ec61Sdh155122 counter = DROPPER(ipss, ipds_spd_got_secure); 219732350c00Sdanmcd ipsec_log_policy_failure(IPSEC_POLICY_NOT_NEEDED, 2198f4b3ec61Sdh155122 "ipsec_check_global_policy", ipha, ip6h, B_TRUE, 2199f4b3ec61Sdh155122 ns); 22007c478bd9Sstevel@tonic-gate goto fail; 22017c478bd9Sstevel@tonic-gate } 22027c478bd9Sstevel@tonic-gate } 2203bd670b35SErik Nordmark if (ira->ira_flags & IRAF_IPSEC_SECURE) { 2204bd670b35SErik Nordmark return (ipsec_check_ipsecin_policy(data_mp, p, ipha, ip6h, 2205bd670b35SErik Nordmark pkt_unique, ira, ns)); 220632350c00Sdanmcd } 22077c478bd9Sstevel@tonic-gate if (p->ipsp_act->ipa_allow_clear) { 2208f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); 2209bd670b35SErik Nordmark IPPOL_REFRELE(p); 2210bd670b35SErik Nordmark return (data_mp); 22117c478bd9Sstevel@tonic-gate } 2212bd670b35SErik Nordmark IPPOL_REFRELE(p); 22137c478bd9Sstevel@tonic-gate /* 22147c478bd9Sstevel@tonic-gate * If we reach here, we will drop the packet because it failed the 22157c478bd9Sstevel@tonic-gate * global policy check because the packet was cleartext, and it 22167c478bd9Sstevel@tonic-gate * should not have been. 22177c478bd9Sstevel@tonic-gate */ 221832350c00Sdanmcd ipsec_log_policy_failure(IPSEC_POLICY_MISMATCH, 2219f4b3ec61Sdh155122 "ipsec_check_global_policy", ipha, ip6h, B_FALSE, ns); 2220f4b3ec61Sdh155122 counter = DROPPER(ipss, ipds_spd_got_clear); 22217c478bd9Sstevel@tonic-gate 22227c478bd9Sstevel@tonic-gate fail: 2223bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, counter, 2224f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 2225f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); 22267c478bd9Sstevel@tonic-gate return (NULL); 22277c478bd9Sstevel@tonic-gate } 22287c478bd9Sstevel@tonic-gate 22297c478bd9Sstevel@tonic-gate /* 22307c478bd9Sstevel@tonic-gate * We check whether an inbound datagram is a valid one 22317c478bd9Sstevel@tonic-gate * to accept in clear. If it is secure, it is the job 22327c478bd9Sstevel@tonic-gate * of IPSEC to log information appropriately if it 22337c478bd9Sstevel@tonic-gate * suspects that it may not be the real one. 22347c478bd9Sstevel@tonic-gate * 22357c478bd9Sstevel@tonic-gate * It is called only while fanning out to the ULP 22367c478bd9Sstevel@tonic-gate * where ULP accepts only secure data and the incoming 22377c478bd9Sstevel@tonic-gate * is clear. Usually we never accept clear datagrams in 22387c478bd9Sstevel@tonic-gate * such cases. ICMP is the only exception. 22397c478bd9Sstevel@tonic-gate * 22407c478bd9Sstevel@tonic-gate * NOTE : We don't call this function if the client (ULP) 22417c478bd9Sstevel@tonic-gate * is willing to accept things in clear. 22427c478bd9Sstevel@tonic-gate */ 22437c478bd9Sstevel@tonic-gate boolean_t 22447c478bd9Sstevel@tonic-gate ipsec_inbound_accept_clear(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) 22457c478bd9Sstevel@tonic-gate { 22467c478bd9Sstevel@tonic-gate ushort_t iph_hdr_length; 22477c478bd9Sstevel@tonic-gate icmph_t *icmph; 22487c478bd9Sstevel@tonic-gate icmp6_t *icmp6; 22497c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 22507c478bd9Sstevel@tonic-gate 22517c478bd9Sstevel@tonic-gate ASSERT((ipha != NULL && ip6h == NULL) || 22527c478bd9Sstevel@tonic-gate (ipha == NULL && ip6h != NULL)); 22537c478bd9Sstevel@tonic-gate 22547c478bd9Sstevel@tonic-gate if (ip6h != NULL) { 22557c478bd9Sstevel@tonic-gate iph_hdr_length = ip_hdr_length_v6(mp, ip6h); 22567c478bd9Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &iph_hdr_length, 22577c478bd9Sstevel@tonic-gate &nexthdrp)) { 22587c478bd9Sstevel@tonic-gate return (B_FALSE); 22597c478bd9Sstevel@tonic-gate } 22607c478bd9Sstevel@tonic-gate if (*nexthdrp != IPPROTO_ICMPV6) 22617c478bd9Sstevel@tonic-gate return (B_FALSE); 22627c478bd9Sstevel@tonic-gate icmp6 = (icmp6_t *)(&mp->b_rptr[iph_hdr_length]); 22637c478bd9Sstevel@tonic-gate /* Match IPv6 ICMP policy as closely as IPv4 as possible. */ 22647c478bd9Sstevel@tonic-gate switch (icmp6->icmp6_type) { 22657c478bd9Sstevel@tonic-gate case ICMP6_PARAM_PROB: 22667c478bd9Sstevel@tonic-gate /* Corresponds to port/proto unreach in IPv4. */ 22677c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REQUEST: 22687c478bd9Sstevel@tonic-gate /* Just like IPv4. */ 22697c478bd9Sstevel@tonic-gate return (B_FALSE); 22707c478bd9Sstevel@tonic-gate 22717c478bd9Sstevel@tonic-gate case MLD_LISTENER_QUERY: 22727c478bd9Sstevel@tonic-gate case MLD_LISTENER_REPORT: 22737c478bd9Sstevel@tonic-gate case MLD_LISTENER_REDUCTION: 22747c478bd9Sstevel@tonic-gate /* 22757c478bd9Sstevel@tonic-gate * XXX Seperate NDD in IPv4 what about here? 22767c478bd9Sstevel@tonic-gate * Plus, mcast is important to ND. 22777c478bd9Sstevel@tonic-gate */ 22787c478bd9Sstevel@tonic-gate case ICMP6_DST_UNREACH: 22797c478bd9Sstevel@tonic-gate /* Corresponds to HOST/NET unreachable in IPv4. */ 22807c478bd9Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG: 22817c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REPLY: 22827c478bd9Sstevel@tonic-gate /* These are trusted in IPv4. */ 22837c478bd9Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 22847c478bd9Sstevel@tonic-gate case ND_ROUTER_ADVERT: 22857c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT: 22867c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT: 22877c478bd9Sstevel@tonic-gate case ND_REDIRECT: 22887c478bd9Sstevel@tonic-gate /* Trust ND messages for now. */ 22897c478bd9Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED: 22907c478bd9Sstevel@tonic-gate default: 22917c478bd9Sstevel@tonic-gate return (B_TRUE); 22927c478bd9Sstevel@tonic-gate } 22937c478bd9Sstevel@tonic-gate } else { 22947c478bd9Sstevel@tonic-gate /* 22957c478bd9Sstevel@tonic-gate * If it is not ICMP, fail this request. 22967c478bd9Sstevel@tonic-gate */ 22978810c16bSdanmcd if (ipha->ipha_protocol != IPPROTO_ICMP) { 22988810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 22998810c16bSdanmcd cmn_err(CE_WARN, "Dropping - ipha_proto = %d\n", 23008810c16bSdanmcd ipha->ipha_protocol); 23018810c16bSdanmcd #endif 23027c478bd9Sstevel@tonic-gate return (B_FALSE); 23038810c16bSdanmcd } 23047c478bd9Sstevel@tonic-gate iph_hdr_length = IPH_HDR_LENGTH(ipha); 23057c478bd9Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 23067c478bd9Sstevel@tonic-gate /* 23077c478bd9Sstevel@tonic-gate * It is an insecure icmp message. Check to see whether we are 23087c478bd9Sstevel@tonic-gate * willing to accept this one. 23097c478bd9Sstevel@tonic-gate */ 23107c478bd9Sstevel@tonic-gate 23117c478bd9Sstevel@tonic-gate switch (icmph->icmph_type) { 23127c478bd9Sstevel@tonic-gate case ICMP_ECHO_REPLY: 23137c478bd9Sstevel@tonic-gate case ICMP_TIME_STAMP_REPLY: 23147c478bd9Sstevel@tonic-gate case ICMP_INFO_REPLY: 23157c478bd9Sstevel@tonic-gate case ICMP_ROUTER_ADVERTISEMENT: 23167c478bd9Sstevel@tonic-gate /* 23177c478bd9Sstevel@tonic-gate * We should not encourage clear replies if this 23187c478bd9Sstevel@tonic-gate * client expects secure. If somebody is replying 23197c478bd9Sstevel@tonic-gate * in clear some mailicious user watching both the 23207c478bd9Sstevel@tonic-gate * request and reply, can do chosen-plain-text attacks. 23217c478bd9Sstevel@tonic-gate * With global policy we might be just expecting secure 23227c478bd9Sstevel@tonic-gate * but sending out clear. We don't know what the right 23237c478bd9Sstevel@tonic-gate * thing is. We can't do much here as we can't control 23247c478bd9Sstevel@tonic-gate * the sender here. Till we are sure of what to do, 23257c478bd9Sstevel@tonic-gate * accept them. 23267c478bd9Sstevel@tonic-gate */ 23277c478bd9Sstevel@tonic-gate return (B_TRUE); 23287c478bd9Sstevel@tonic-gate case ICMP_ECHO_REQUEST: 23297c478bd9Sstevel@tonic-gate case ICMP_TIME_STAMP_REQUEST: 23307c478bd9Sstevel@tonic-gate case ICMP_INFO_REQUEST: 23317c478bd9Sstevel@tonic-gate case ICMP_ADDRESS_MASK_REQUEST: 23327c478bd9Sstevel@tonic-gate case ICMP_ROUTER_SOLICITATION: 23337c478bd9Sstevel@tonic-gate case ICMP_ADDRESS_MASK_REPLY: 23347c478bd9Sstevel@tonic-gate /* 23357c478bd9Sstevel@tonic-gate * Don't accept this as somebody could be sending 23367c478bd9Sstevel@tonic-gate * us plain text to get encrypted data. If we reply, 23377c478bd9Sstevel@tonic-gate * it will lead to chosen plain text attack. 23387c478bd9Sstevel@tonic-gate */ 23397c478bd9Sstevel@tonic-gate return (B_FALSE); 23407c478bd9Sstevel@tonic-gate case ICMP_DEST_UNREACHABLE: 23417c478bd9Sstevel@tonic-gate switch (icmph->icmph_code) { 23427c478bd9Sstevel@tonic-gate case ICMP_FRAGMENTATION_NEEDED: 23437c478bd9Sstevel@tonic-gate /* 23447c478bd9Sstevel@tonic-gate * Be in sync with icmp_inbound, where we have 2345bd670b35SErik Nordmark * already set dce_pmtu 23467c478bd9Sstevel@tonic-gate */ 23478810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 23488810c16bSdanmcd cmn_err(CE_WARN, "ICMP frag needed\n"); 23498810c16bSdanmcd #endif 23507c478bd9Sstevel@tonic-gate return (B_TRUE); 23517c478bd9Sstevel@tonic-gate case ICMP_HOST_UNREACHABLE: 23527c478bd9Sstevel@tonic-gate case ICMP_NET_UNREACHABLE: 23537c478bd9Sstevel@tonic-gate /* 23547c478bd9Sstevel@tonic-gate * By accepting, we could reset a connection. 23557c478bd9Sstevel@tonic-gate * How do we solve the problem of some 23567c478bd9Sstevel@tonic-gate * intermediate router sending in-secure ICMP 23577c478bd9Sstevel@tonic-gate * messages ? 23587c478bd9Sstevel@tonic-gate */ 23597c478bd9Sstevel@tonic-gate return (B_TRUE); 23607c478bd9Sstevel@tonic-gate case ICMP_PORT_UNREACHABLE: 23617c478bd9Sstevel@tonic-gate case ICMP_PROTOCOL_UNREACHABLE: 23627c478bd9Sstevel@tonic-gate default : 23637c478bd9Sstevel@tonic-gate return (B_FALSE); 23647c478bd9Sstevel@tonic-gate } 23657c478bd9Sstevel@tonic-gate case ICMP_SOURCE_QUENCH: 23667c478bd9Sstevel@tonic-gate /* 23677c478bd9Sstevel@tonic-gate * If this is an attack, TCP will slow start 23687c478bd9Sstevel@tonic-gate * because of this. Is it very harmful ? 23697c478bd9Sstevel@tonic-gate */ 23707c478bd9Sstevel@tonic-gate return (B_TRUE); 23717c478bd9Sstevel@tonic-gate case ICMP_PARAM_PROBLEM: 23727c478bd9Sstevel@tonic-gate return (B_FALSE); 23737c478bd9Sstevel@tonic-gate case ICMP_TIME_EXCEEDED: 23747c478bd9Sstevel@tonic-gate return (B_TRUE); 23757c478bd9Sstevel@tonic-gate case ICMP_REDIRECT: 23767c478bd9Sstevel@tonic-gate return (B_FALSE); 23777c478bd9Sstevel@tonic-gate default : 23787c478bd9Sstevel@tonic-gate return (B_FALSE); 23797c478bd9Sstevel@tonic-gate } 23807c478bd9Sstevel@tonic-gate } 23817c478bd9Sstevel@tonic-gate } 23827c478bd9Sstevel@tonic-gate 23837c478bd9Sstevel@tonic-gate void 23847c478bd9Sstevel@tonic-gate ipsec_latch_ids(ipsec_latch_t *ipl, ipsid_t *local, ipsid_t *remote) 23857c478bd9Sstevel@tonic-gate { 23867c478bd9Sstevel@tonic-gate mutex_enter(&ipl->ipl_lock); 23877c478bd9Sstevel@tonic-gate 23887c478bd9Sstevel@tonic-gate if (ipl->ipl_ids_latched) { 23897c478bd9Sstevel@tonic-gate /* I lost, someone else got here before me */ 23907c478bd9Sstevel@tonic-gate mutex_exit(&ipl->ipl_lock); 23917c478bd9Sstevel@tonic-gate return; 23927c478bd9Sstevel@tonic-gate } 23937c478bd9Sstevel@tonic-gate 23947c478bd9Sstevel@tonic-gate if (local != NULL) 23957c478bd9Sstevel@tonic-gate IPSID_REFHOLD(local); 23967c478bd9Sstevel@tonic-gate if (remote != NULL) 23977c478bd9Sstevel@tonic-gate IPSID_REFHOLD(remote); 23987c478bd9Sstevel@tonic-gate 23997c478bd9Sstevel@tonic-gate ipl->ipl_local_cid = local; 24007c478bd9Sstevel@tonic-gate ipl->ipl_remote_cid = remote; 24017c478bd9Sstevel@tonic-gate ipl->ipl_ids_latched = B_TRUE; 24027c478bd9Sstevel@tonic-gate mutex_exit(&ipl->ipl_lock); 24037c478bd9Sstevel@tonic-gate } 24047c478bd9Sstevel@tonic-gate 24057c478bd9Sstevel@tonic-gate void 2406bd670b35SErik Nordmark ipsec_latch_inbound(conn_t *connp, ip_recv_attr_t *ira) 24077c478bd9Sstevel@tonic-gate { 24087c478bd9Sstevel@tonic-gate ipsa_t *sa; 2409bd670b35SErik Nordmark ipsec_latch_t *ipl = connp->conn_latch; 24107c478bd9Sstevel@tonic-gate 24117c478bd9Sstevel@tonic-gate if (!ipl->ipl_ids_latched) { 24127c478bd9Sstevel@tonic-gate ipsid_t *local = NULL; 24137c478bd9Sstevel@tonic-gate ipsid_t *remote = NULL; 24147c478bd9Sstevel@tonic-gate 2415bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_LOOPBACK)) { 2416bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 2417bd670b35SErik Nordmark if (ira->ira_ipsec_esp_sa != NULL) 2418bd670b35SErik Nordmark sa = ira->ira_ipsec_esp_sa; 24197c478bd9Sstevel@tonic-gate else 2420bd670b35SErik Nordmark sa = ira->ira_ipsec_ah_sa; 24217c478bd9Sstevel@tonic-gate ASSERT(sa != NULL); 24227c478bd9Sstevel@tonic-gate local = sa->ipsa_dst_cid; 24237c478bd9Sstevel@tonic-gate remote = sa->ipsa_src_cid; 24247c478bd9Sstevel@tonic-gate } 24257c478bd9Sstevel@tonic-gate ipsec_latch_ids(ipl, local, remote); 24267c478bd9Sstevel@tonic-gate } 2427bd670b35SErik Nordmark if (ira->ira_flags & IRAF_IPSEC_SECURE) { 2428bd670b35SErik Nordmark if (connp->conn_latch_in_action != NULL) { 2429bd670b35SErik Nordmark /* 2430bd670b35SErik Nordmark * Previously cached action. This is probably 2431bd670b35SErik Nordmark * harmless, but in DEBUG kernels, check for 2432bd670b35SErik Nordmark * action equality. 2433bd670b35SErik Nordmark * 2434bd670b35SErik Nordmark * Preserve the existing action to preserve latch 2435bd670b35SErik Nordmark * invariance. 2436bd670b35SErik Nordmark */ 2437bd670b35SErik Nordmark ASSERT(connp->conn_latch_in_action == 2438bd670b35SErik Nordmark ira->ira_ipsec_action); 2439bd670b35SErik Nordmark return; 2440bd670b35SErik Nordmark } 2441bd670b35SErik Nordmark connp->conn_latch_in_action = ira->ira_ipsec_action; 2442bd670b35SErik Nordmark IPACT_REFHOLD(connp->conn_latch_in_action); 2443bd670b35SErik Nordmark } 24447c478bd9Sstevel@tonic-gate } 24457c478bd9Sstevel@tonic-gate 24467c478bd9Sstevel@tonic-gate /* 24477c478bd9Sstevel@tonic-gate * Check whether the policy constraints are met either for an 24487c478bd9Sstevel@tonic-gate * inbound datagram; called from IP in numerous places. 24497c478bd9Sstevel@tonic-gate * 24507c478bd9Sstevel@tonic-gate * Note that this is not a chokepoint for inbound policy checks; 245107b56925Ssommerfe * see also ipsec_check_ipsecin_latch() and ipsec_check_global_policy() 24527c478bd9Sstevel@tonic-gate */ 24537c478bd9Sstevel@tonic-gate mblk_t * 2454bd670b35SErik Nordmark ipsec_check_inbound_policy(mblk_t *mp, conn_t *connp, 2455bd670b35SErik Nordmark ipha_t *ipha, ip6_t *ip6h, ip_recv_attr_t *ira) 24567c478bd9Sstevel@tonic-gate { 24577c478bd9Sstevel@tonic-gate boolean_t ret; 24587c478bd9Sstevel@tonic-gate ipsec_latch_t *ipl; 2459bd670b35SErik Nordmark ipsec_action_t *ap; 24608810c16bSdanmcd uint64_t unique_id; 2461f4b3ec61Sdh155122 ipsec_stack_t *ipss; 2462f4b3ec61Sdh155122 ip_stack_t *ipst; 2463f4b3ec61Sdh155122 netstack_t *ns; 246450b2f153SVladimir Kotal ipsec_policy_head_t *policy_head; 2465bd670b35SErik Nordmark ipsec_policy_t *p = NULL; 24667c478bd9Sstevel@tonic-gate 24677c478bd9Sstevel@tonic-gate ASSERT(connp != NULL); 2468f4b3ec61Sdh155122 ns = connp->conn_netstack; 2469f4b3ec61Sdh155122 ipss = ns->netstack_ipsec; 2470f4b3ec61Sdh155122 ipst = ns->netstack_ip; 24717c478bd9Sstevel@tonic-gate 2472bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 24737c478bd9Sstevel@tonic-gate /* 24747c478bd9Sstevel@tonic-gate * This is the case where the incoming datagram is 24757c478bd9Sstevel@tonic-gate * cleartext and we need to see whether this client 24767c478bd9Sstevel@tonic-gate * would like to receive such untrustworthy things from 24777c478bd9Sstevel@tonic-gate * the wire. 24787c478bd9Sstevel@tonic-gate */ 24797c478bd9Sstevel@tonic-gate ASSERT(mp != NULL); 24807c478bd9Sstevel@tonic-gate 2481a065e3c6SGeorge Shepherd mutex_enter(&connp->conn_lock); 2482a065e3c6SGeorge Shepherd if (connp->conn_state_flags & CONN_CONDEMNED) { 2483a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 2484bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, NULL, 2485bd670b35SErik Nordmark DROPPER(ipss, ipds_spd_got_clear), 2486a065e3c6SGeorge Shepherd &ipss->ipsec_spd_dropper); 2487a065e3c6SGeorge Shepherd BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); 2488a065e3c6SGeorge Shepherd return (NULL); 2489a065e3c6SGeorge Shepherd } 2490bd670b35SErik Nordmark if (connp->conn_latch != NULL) { 2491a065e3c6SGeorge Shepherd /* Hold a reference in case the conn is closing */ 2492bd670b35SErik Nordmark p = connp->conn_latch_in_policy; 2493bd670b35SErik Nordmark if (p != NULL) 2494bd670b35SErik Nordmark IPPOL_REFHOLD(p); 2495a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 24967c478bd9Sstevel@tonic-gate /* 24977c478bd9Sstevel@tonic-gate * Policy is cached in the conn. 24987c478bd9Sstevel@tonic-gate */ 2499bd670b35SErik Nordmark if (p != NULL && !p->ipsp_act->ipa_allow_clear) { 25007c478bd9Sstevel@tonic-gate ret = ipsec_inbound_accept_clear(mp, 25017c478bd9Sstevel@tonic-gate ipha, ip6h); 25027c478bd9Sstevel@tonic-gate if (ret) { 2503f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, 2504f4b3ec61Sdh155122 ipsecInSucceeded); 2505bd670b35SErik Nordmark IPPOL_REFRELE(p); 2506bd670b35SErik Nordmark return (mp); 25077c478bd9Sstevel@tonic-gate } else { 25087c478bd9Sstevel@tonic-gate ipsec_log_policy_failure( 25097c478bd9Sstevel@tonic-gate IPSEC_POLICY_MISMATCH, 25107c478bd9Sstevel@tonic-gate "ipsec_check_inbound_policy", ipha, 2511f4b3ec61Sdh155122 ip6h, B_FALSE, ns); 2512bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, NULL, 2513f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_got_clear), 2514f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 2515f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, 2516f4b3ec61Sdh155122 ipsecInFailed); 2517bd670b35SErik Nordmark IPPOL_REFRELE(p); 25187c478bd9Sstevel@tonic-gate return (NULL); 25197c478bd9Sstevel@tonic-gate } 25207c478bd9Sstevel@tonic-gate } else { 2521f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); 2522bd670b35SErik Nordmark if (p != NULL) 2523bd670b35SErik Nordmark IPPOL_REFRELE(p); 2524bd670b35SErik Nordmark return (mp); 25257c478bd9Sstevel@tonic-gate } 25267c478bd9Sstevel@tonic-gate } else { 252750b2f153SVladimir Kotal policy_head = connp->conn_policy; 2528a065e3c6SGeorge Shepherd 252950b2f153SVladimir Kotal /* Hold a reference in case the conn is closing */ 253050b2f153SVladimir Kotal if (policy_head != NULL) 253150b2f153SVladimir Kotal IPPH_REFHOLD(policy_head); 2532a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 25337c478bd9Sstevel@tonic-gate /* 25347c478bd9Sstevel@tonic-gate * As this is a non-hardbound connection we need 25357c478bd9Sstevel@tonic-gate * to look at both per-socket policy and global 2536bd670b35SErik Nordmark * policy. 25377c478bd9Sstevel@tonic-gate */ 2538bd670b35SErik Nordmark mp = ipsec_check_global_policy(mp, connp, 2539bd670b35SErik Nordmark ipha, ip6h, ira, ns); 254050b2f153SVladimir Kotal if (policy_head != NULL) 254150b2f153SVladimir Kotal IPPH_REFRELE(policy_head, ns); 2542bd670b35SErik Nordmark return (mp); 25437c478bd9Sstevel@tonic-gate } 25447c478bd9Sstevel@tonic-gate } 25457c478bd9Sstevel@tonic-gate 2546a065e3c6SGeorge Shepherd mutex_enter(&connp->conn_lock); 2547a065e3c6SGeorge Shepherd /* Connection is closing */ 2548a065e3c6SGeorge Shepherd if (connp->conn_state_flags & CONN_CONDEMNED) { 2549a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 2550bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, NULL, 2551bd670b35SErik Nordmark DROPPER(ipss, ipds_spd_got_clear), 2552a065e3c6SGeorge Shepherd &ipss->ipsec_spd_dropper); 2553a065e3c6SGeorge Shepherd BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); 2554a065e3c6SGeorge Shepherd return (NULL); 2555a065e3c6SGeorge Shepherd } 2556a065e3c6SGeorge Shepherd 2557a065e3c6SGeorge Shepherd /* 2558a065e3c6SGeorge Shepherd * Once a connection is latched it remains so for life, the conn_latch 2559a065e3c6SGeorge Shepherd * pointer on the conn has not changed, simply initializing ipl here 2560a065e3c6SGeorge Shepherd * as the earlier initialization was done only in the cleartext case. 2561a065e3c6SGeorge Shepherd */ 256236cdea1aSDan McDonald if ((ipl = connp->conn_latch) == NULL) { 256350b2f153SVladimir Kotal mblk_t *retmp; 256450b2f153SVladimir Kotal policy_head = connp->conn_policy; 256550b2f153SVladimir Kotal 256650b2f153SVladimir Kotal /* Hold a reference in case the conn is closing */ 256750b2f153SVladimir Kotal if (policy_head != NULL) 256850b2f153SVladimir Kotal IPPH_REFHOLD(policy_head); 2569a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 25707c478bd9Sstevel@tonic-gate /* 257107b56925Ssommerfe * We don't have policies cached in the conn 25727c478bd9Sstevel@tonic-gate * for this stream. So, look at the global 25737c478bd9Sstevel@tonic-gate * policy. It will check against conn or global 25747c478bd9Sstevel@tonic-gate * depending on whichever is stronger. 25757c478bd9Sstevel@tonic-gate */ 2576bd670b35SErik Nordmark retmp = ipsec_check_global_policy(mp, connp, 2577bd670b35SErik Nordmark ipha, ip6h, ira, ns); 257850b2f153SVladimir Kotal if (policy_head != NULL) 257950b2f153SVladimir Kotal IPPH_REFRELE(policy_head, ns); 258050b2f153SVladimir Kotal return (retmp); 258107b56925Ssommerfe } 258207b56925Ssommerfe 2583a065e3c6SGeorge Shepherd IPLATCH_REFHOLD(ipl); 2584bd670b35SErik Nordmark /* Hold reference on conn_latch_in_action in case conn is closing */ 2585bd670b35SErik Nordmark ap = connp->conn_latch_in_action; 2586bd670b35SErik Nordmark if (ap != NULL) 2587bd670b35SErik Nordmark IPACT_REFHOLD(ap); 2588a065e3c6SGeorge Shepherd mutex_exit(&connp->conn_lock); 2589a065e3c6SGeorge Shepherd 2590bd670b35SErik Nordmark if (ap != NULL) { 25917c478bd9Sstevel@tonic-gate /* Policy is cached & latched; fast(er) path */ 25927c478bd9Sstevel@tonic-gate const char *reason; 25937c478bd9Sstevel@tonic-gate kstat_named_t *counter; 2594f4b3ec61Sdh155122 2595bd670b35SErik Nordmark if (ipsec_check_ipsecin_latch(ira, mp, ipl, ap, 2596bd670b35SErik Nordmark ipha, ip6h, &reason, &counter, connp, ns)) { 2597f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); 2598bd670b35SErik Nordmark IPLATCH_REFRELE(ipl); 2599bd670b35SErik Nordmark IPACT_REFRELE(ap); 2600bd670b35SErik Nordmark return (mp); 26017c478bd9Sstevel@tonic-gate } 2602f4b3ec61Sdh155122 ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, 2603f4b3ec61Sdh155122 SL_ERROR|SL_WARN|SL_CONSOLE, 26047c478bd9Sstevel@tonic-gate "ipsec inbound policy mismatch: %s, packet dropped\n", 26057c478bd9Sstevel@tonic-gate reason); 2606bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, NULL, counter, 2607f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 2608f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); 2609bd670b35SErik Nordmark IPLATCH_REFRELE(ipl); 2610bd670b35SErik Nordmark IPACT_REFRELE(ap); 26117c478bd9Sstevel@tonic-gate return (NULL); 2612bd670b35SErik Nordmark } 2613bd670b35SErik Nordmark if ((p = connp->conn_latch_in_policy) == NULL) { 261407b56925Ssommerfe ipsec_weird_null_inbound_policy++; 2615bd670b35SErik Nordmark IPLATCH_REFRELE(ipl); 2616bd670b35SErik Nordmark return (mp); 261707b56925Ssommerfe } 26187c478bd9Sstevel@tonic-gate 26198810c16bSdanmcd unique_id = conn_to_unique(connp, mp, ipha, ip6h); 2620bd670b35SErik Nordmark IPPOL_REFHOLD(p); 2621bd670b35SErik Nordmark mp = ipsec_check_ipsecin_policy(mp, p, ipha, ip6h, unique_id, ira, ns); 26227c478bd9Sstevel@tonic-gate /* 26237c478bd9Sstevel@tonic-gate * NOTE: ipsecIn{Failed,Succeeeded} bumped by 26247c478bd9Sstevel@tonic-gate * ipsec_check_ipsecin_policy(). 26257c478bd9Sstevel@tonic-gate */ 2626bd670b35SErik Nordmark if (mp != NULL) 2627bd670b35SErik Nordmark ipsec_latch_inbound(connp, ira); 2628bd670b35SErik Nordmark IPLATCH_REFRELE(ipl); 2629bd670b35SErik Nordmark return (mp); 26307c478bd9Sstevel@tonic-gate } 26317c478bd9Sstevel@tonic-gate 26328810c16bSdanmcd /* 2633bd670b35SErik Nordmark * Handle all sorts of cases like tunnel-mode and ICMP. 26345d3b8cb7SBill Sommerfeld */ 26355d3b8cb7SBill Sommerfeld static int 26365d3b8cb7SBill Sommerfeld prepended_length(mblk_t *mp, uintptr_t hptr) 26375d3b8cb7SBill Sommerfeld { 26385d3b8cb7SBill Sommerfeld int rc = 0; 26395d3b8cb7SBill Sommerfeld 26405d3b8cb7SBill Sommerfeld while (mp != NULL) { 26415d3b8cb7SBill Sommerfeld if (hptr >= (uintptr_t)mp->b_rptr && hptr < 26425d3b8cb7SBill Sommerfeld (uintptr_t)mp->b_wptr) { 26435d3b8cb7SBill Sommerfeld rc += (int)(hptr - (uintptr_t)mp->b_rptr); 26445d3b8cb7SBill Sommerfeld break; /* out of while loop */ 26455d3b8cb7SBill Sommerfeld } 26465d3b8cb7SBill Sommerfeld rc += (int)MBLKL(mp); 26475d3b8cb7SBill Sommerfeld mp = mp->b_cont; 26485d3b8cb7SBill Sommerfeld } 26495d3b8cb7SBill Sommerfeld 26505d3b8cb7SBill Sommerfeld if (mp == NULL) { 26515d3b8cb7SBill Sommerfeld /* 26525d3b8cb7SBill Sommerfeld * IF (big IF) we make it here by naturally exiting the loop, 26535d3b8cb7SBill Sommerfeld * then ip6h isn't in the mblk chain "mp" at all. 26545d3b8cb7SBill Sommerfeld * 26555d3b8cb7SBill Sommerfeld * The only case where this happens is with a reversed IP 26565d3b8cb7SBill Sommerfeld * header that gets passed up by inbound ICMP processing. 26575d3b8cb7SBill Sommerfeld * This unfortunately triggers longstanding bug 6478464. For 26585d3b8cb7SBill Sommerfeld * now, just pass up 0 for the answer. 26595d3b8cb7SBill Sommerfeld */ 26605d3b8cb7SBill Sommerfeld #ifdef DEBUG_NOT_UNTIL_6478464 26615d3b8cb7SBill Sommerfeld ASSERT(mp != NULL); 26625d3b8cb7SBill Sommerfeld #endif 26635d3b8cb7SBill Sommerfeld rc = 0; 26645d3b8cb7SBill Sommerfeld } 26655d3b8cb7SBill Sommerfeld 26665d3b8cb7SBill Sommerfeld return (rc); 26675d3b8cb7SBill Sommerfeld } 26685d3b8cb7SBill Sommerfeld 26695d3b8cb7SBill Sommerfeld /* 26708810c16bSdanmcd * Returns: 26718810c16bSdanmcd * 26728810c16bSdanmcd * SELRET_NOMEM --> msgpullup() needed to gather things failed. 26738810c16bSdanmcd * SELRET_BADPKT --> If we're being called after tunnel-mode fragment 26748810c16bSdanmcd * gathering, the initial fragment is too short for 26758810c16bSdanmcd * useful data. Only returned if SEL_TUNNEL_FIRSTFRAG is 26768810c16bSdanmcd * set. 26778810c16bSdanmcd * SELRET_SUCCESS --> "sel" now has initialized IPsec selector data. 26788810c16bSdanmcd * SELRET_TUNFRAG --> This is a fragment in a tunnel-mode packet. Caller 26798810c16bSdanmcd * should put this packet in a fragment-gathering queue. 26808810c16bSdanmcd * Only returned if SEL_TUNNEL_MODE and SEL_PORT_POLICY 26818810c16bSdanmcd * is set. 2682bd670b35SErik Nordmark * 2683bd670b35SErik Nordmark * Note that ipha/ip6h can be in a different mblk (mp->b_cont) in the case 2684bd670b35SErik Nordmark * of tunneled packets. 2685bd670b35SErik Nordmark * Also, mp->b_rptr can be an ICMP error where ipha/ip6h is the packet in 2686bd670b35SErik Nordmark * error past the ICMP error. 26878810c16bSdanmcd */ 26888810c16bSdanmcd static selret_t 26898810c16bSdanmcd ipsec_init_inbound_sel(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, 26908810c16bSdanmcd ip6_t *ip6h, uint8_t sel_flags) 26917c478bd9Sstevel@tonic-gate { 26927c478bd9Sstevel@tonic-gate uint16_t *ports; 2693bd670b35SErik Nordmark int outer_hdr_len = 0; /* For ICMP or tunnel-mode cases... */ 26947c478bd9Sstevel@tonic-gate ushort_t hdr_len; 26957c478bd9Sstevel@tonic-gate mblk_t *spare_mp = NULL; 26965d3b8cb7SBill Sommerfeld uint8_t *nexthdrp, *transportp; 26977c478bd9Sstevel@tonic-gate uint8_t nexthdr; 26985d3b8cb7SBill Sommerfeld uint8_t icmp_proto; 2699bd670b35SErik Nordmark ip_pkt_t ipp; 27008810c16bSdanmcd boolean_t port_policy_present = (sel_flags & SEL_PORT_POLICY); 27018810c16bSdanmcd boolean_t is_icmp = (sel_flags & SEL_IS_ICMP); 27028810c16bSdanmcd boolean_t tunnel_mode = (sel_flags & SEL_TUNNEL_MODE); 27032b24ab6bSSebastien Roy boolean_t post_frag = (sel_flags & SEL_POST_FRAG); 27047c478bd9Sstevel@tonic-gate 27057c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 27067c478bd9Sstevel@tonic-gate (ipha != NULL && ip6h == NULL)); 27077c478bd9Sstevel@tonic-gate 27087c478bd9Sstevel@tonic-gate if (ip6h != NULL) { 27095d3b8cb7SBill Sommerfeld outer_hdr_len = prepended_length(mp, (uintptr_t)ip6h); 27105d3b8cb7SBill Sommerfeld nexthdr = ip6h->ip6_nxt; 27115d3b8cb7SBill Sommerfeld icmp_proto = IPPROTO_ICMPV6; 27127c478bd9Sstevel@tonic-gate sel->ips_isv4 = B_FALSE; 27137c478bd9Sstevel@tonic-gate sel->ips_local_addr_v6 = ip6h->ip6_dst; 27147c478bd9Sstevel@tonic-gate sel->ips_remote_addr_v6 = ip6h->ip6_src; 27157c478bd9Sstevel@tonic-gate 27168810c16bSdanmcd bzero(&ipp, sizeof (ipp)); 27178810c16bSdanmcd 27187c478bd9Sstevel@tonic-gate switch (nexthdr) { 27197c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 27207c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 27217c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 27228810c16bSdanmcd case IPPROTO_FRAGMENT: 27237c478bd9Sstevel@tonic-gate /* 27247c478bd9Sstevel@tonic-gate * Use ip_hdr_length_nexthdr_v6(). And have a spare 27257c478bd9Sstevel@tonic-gate * mblk that's contiguous to feed it 27267c478bd9Sstevel@tonic-gate */ 27277c478bd9Sstevel@tonic-gate if ((spare_mp = msgpullup(mp, -1)) == NULL) 27288810c16bSdanmcd return (SELRET_NOMEM); 27297c478bd9Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(spare_mp, 27308810c16bSdanmcd (ip6_t *)(spare_mp->b_rptr + outer_hdr_len), 27318810c16bSdanmcd &hdr_len, &nexthdrp)) { 27328810c16bSdanmcd /* Malformed packet - caller frees. */ 27338810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 27348810c16bSdanmcd return (SELRET_BADPKT); 27357c478bd9Sstevel@tonic-gate } 2736d1a98e54SPaul Wernau /* Repopulate now that we have the whole packet */ 2737d1a98e54SPaul Wernau ip6h = (ip6_t *)(spare_mp->b_rptr + outer_hdr_len); 2738d1a98e54SPaul Wernau (void) ip_find_hdr_v6(spare_mp, ip6h, B_FALSE, &ipp, 2739d1a98e54SPaul Wernau NULL); 27407c478bd9Sstevel@tonic-gate nexthdr = *nexthdrp; 27417c478bd9Sstevel@tonic-gate /* We can just extract based on hdr_len now. */ 27427c478bd9Sstevel@tonic-gate break; 27437c478bd9Sstevel@tonic-gate default: 2744d1a98e54SPaul Wernau (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &ipp, NULL); 27457c478bd9Sstevel@tonic-gate hdr_len = IPV6_HDR_LEN; 27467c478bd9Sstevel@tonic-gate break; 27477c478bd9Sstevel@tonic-gate } 27488810c16bSdanmcd if (port_policy_present && IS_V6_FRAGMENT(ipp) && !is_icmp) { 27498810c16bSdanmcd /* IPv6 Fragment */ 27508810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 27518810c16bSdanmcd return (SELRET_TUNFRAG); 27528810c16bSdanmcd } 27535d3b8cb7SBill Sommerfeld transportp = (uint8_t *)ip6h + hdr_len; 27547c478bd9Sstevel@tonic-gate } else { 27555d3b8cb7SBill Sommerfeld outer_hdr_len = prepended_length(mp, (uintptr_t)ipha); 27565d3b8cb7SBill Sommerfeld icmp_proto = IPPROTO_ICMP; 27577c478bd9Sstevel@tonic-gate sel->ips_isv4 = B_TRUE; 27587c478bd9Sstevel@tonic-gate sel->ips_local_addr_v4 = ipha->ipha_dst; 27597c478bd9Sstevel@tonic-gate sel->ips_remote_addr_v4 = ipha->ipha_src; 27607c478bd9Sstevel@tonic-gate nexthdr = ipha->ipha_protocol; 27617c478bd9Sstevel@tonic-gate hdr_len = IPH_HDR_LENGTH(ipha); 27628810c16bSdanmcd 27638810c16bSdanmcd if (port_policy_present && 27648810c16bSdanmcd IS_V4_FRAGMENT(ipha->ipha_fragment_offset_and_flags) && 27658810c16bSdanmcd !is_icmp) { 27668810c16bSdanmcd /* IPv4 Fragment */ 27678810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 27688810c16bSdanmcd return (SELRET_TUNFRAG); 27698810c16bSdanmcd } 27705d3b8cb7SBill Sommerfeld transportp = (uint8_t *)ipha + hdr_len; 27717c478bd9Sstevel@tonic-gate } 27727c478bd9Sstevel@tonic-gate sel->ips_protocol = nexthdr; 27737c478bd9Sstevel@tonic-gate 27748810c16bSdanmcd if ((nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP && 27755d3b8cb7SBill Sommerfeld nexthdr != IPPROTO_SCTP && nexthdr != icmp_proto) || 27762b24ab6bSSebastien Roy (!port_policy_present && !post_frag && tunnel_mode)) { 27777c478bd9Sstevel@tonic-gate sel->ips_remote_port = sel->ips_local_port = 0; 27788810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 27798810c16bSdanmcd return (SELRET_SUCCESS); 27807c478bd9Sstevel@tonic-gate } 27817c478bd9Sstevel@tonic-gate 27825d3b8cb7SBill Sommerfeld if (transportp + 4 > mp->b_wptr) { 27837c478bd9Sstevel@tonic-gate /* If we didn't pullup a copy already, do so now. */ 27847c478bd9Sstevel@tonic-gate /* 27857c478bd9Sstevel@tonic-gate * XXX performance, will upper-layers frequently split TCP/UDP 27867c478bd9Sstevel@tonic-gate * apart from IP or options? If so, perhaps we should revisit 27877c478bd9Sstevel@tonic-gate * the spare_mp strategy. 27887c478bd9Sstevel@tonic-gate */ 278907b56925Ssommerfe ipsec_hdr_pullup_needed++; 27907c478bd9Sstevel@tonic-gate if (spare_mp == NULL && 27917c478bd9Sstevel@tonic-gate (spare_mp = msgpullup(mp, -1)) == NULL) { 27928810c16bSdanmcd return (SELRET_NOMEM); 27937c478bd9Sstevel@tonic-gate } 27945d3b8cb7SBill Sommerfeld transportp = &spare_mp->b_rptr[hdr_len + outer_hdr_len]; 27957c478bd9Sstevel@tonic-gate } 27967c478bd9Sstevel@tonic-gate 27975d3b8cb7SBill Sommerfeld if (nexthdr == icmp_proto) { 27985d3b8cb7SBill Sommerfeld sel->ips_icmp_type = *transportp++; 27995d3b8cb7SBill Sommerfeld sel->ips_icmp_code = *transportp; 28007c478bd9Sstevel@tonic-gate sel->ips_remote_port = sel->ips_local_port = 0; 28018810c16bSdanmcd } else { 28025d3b8cb7SBill Sommerfeld ports = (uint16_t *)transportp; 28037c478bd9Sstevel@tonic-gate sel->ips_remote_port = *ports++; 28047c478bd9Sstevel@tonic-gate sel->ips_local_port = *ports; 28058810c16bSdanmcd } 28068810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 28078810c16bSdanmcd return (SELRET_SUCCESS); 28087c478bd9Sstevel@tonic-gate } 28097c478bd9Sstevel@tonic-gate 2810bd670b35SErik Nordmark /* 2811bd670b35SErik Nordmark * This is called with a b_next chain of messages from the fragcache code, 2812bd670b35SErik Nordmark * hence it needs to discard a chain on error. 2813bd670b35SErik Nordmark */ 28147c478bd9Sstevel@tonic-gate static boolean_t 28157c478bd9Sstevel@tonic-gate ipsec_init_outbound_ports(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, 2816f4b3ec61Sdh155122 ip6_t *ip6h, int outer_hdr_len, ipsec_stack_t *ipss) 28177c478bd9Sstevel@tonic-gate { 28187c478bd9Sstevel@tonic-gate /* 28197c478bd9Sstevel@tonic-gate * XXX cut&paste shared with ipsec_init_inbound_sel 28207c478bd9Sstevel@tonic-gate */ 28217c478bd9Sstevel@tonic-gate uint16_t *ports; 28227c478bd9Sstevel@tonic-gate ushort_t hdr_len; 28237c478bd9Sstevel@tonic-gate mblk_t *spare_mp = NULL; 28247c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 28257c478bd9Sstevel@tonic-gate uint8_t nexthdr; 28267c478bd9Sstevel@tonic-gate uint8_t *typecode; 28277c478bd9Sstevel@tonic-gate uint8_t check_proto; 28287c478bd9Sstevel@tonic-gate 28297c478bd9Sstevel@tonic-gate ASSERT((ipha == NULL && ip6h != NULL) || 28307c478bd9Sstevel@tonic-gate (ipha != NULL && ip6h == NULL)); 28317c478bd9Sstevel@tonic-gate 28327c478bd9Sstevel@tonic-gate if (ip6h != NULL) { 28337c478bd9Sstevel@tonic-gate check_proto = IPPROTO_ICMPV6; 28347c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 28357c478bd9Sstevel@tonic-gate switch (nexthdr) { 28367c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 28377c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 28387c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 28398810c16bSdanmcd case IPPROTO_FRAGMENT: 28407c478bd9Sstevel@tonic-gate /* 28417c478bd9Sstevel@tonic-gate * Use ip_hdr_length_nexthdr_v6(). And have a spare 28427c478bd9Sstevel@tonic-gate * mblk that's contiguous to feed it 28437c478bd9Sstevel@tonic-gate */ 28447c478bd9Sstevel@tonic-gate spare_mp = msgpullup(mp, -1); 28457c478bd9Sstevel@tonic-gate if (spare_mp == NULL || 28467c478bd9Sstevel@tonic-gate !ip_hdr_length_nexthdr_v6(spare_mp, 28478810c16bSdanmcd (ip6_t *)(spare_mp->b_rptr + outer_hdr_len), 28488810c16bSdanmcd &hdr_len, &nexthdrp)) { 28497c478bd9Sstevel@tonic-gate /* Always works, even if NULL. */ 28508810c16bSdanmcd ipsec_freemsg_chain(spare_mp); 2851bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_FALSE, NULL, 2852f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 2853f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 28547c478bd9Sstevel@tonic-gate return (B_FALSE); 28557c478bd9Sstevel@tonic-gate } else { 28567c478bd9Sstevel@tonic-gate nexthdr = *nexthdrp; 28577c478bd9Sstevel@tonic-gate /* We can just extract based on hdr_len now. */ 28587c478bd9Sstevel@tonic-gate } 28597c478bd9Sstevel@tonic-gate break; 28607c478bd9Sstevel@tonic-gate default: 28617c478bd9Sstevel@tonic-gate hdr_len = IPV6_HDR_LEN; 28627c478bd9Sstevel@tonic-gate break; 28637c478bd9Sstevel@tonic-gate } 28647c478bd9Sstevel@tonic-gate } else { 28657c478bd9Sstevel@tonic-gate check_proto = IPPROTO_ICMP; 28667c478bd9Sstevel@tonic-gate hdr_len = IPH_HDR_LENGTH(ipha); 28677c478bd9Sstevel@tonic-gate nexthdr = ipha->ipha_protocol; 28687c478bd9Sstevel@tonic-gate } 28697c478bd9Sstevel@tonic-gate 28707c478bd9Sstevel@tonic-gate sel->ips_protocol = nexthdr; 28717c478bd9Sstevel@tonic-gate if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP && 28727c478bd9Sstevel@tonic-gate nexthdr != IPPROTO_SCTP && nexthdr != check_proto) { 28737c478bd9Sstevel@tonic-gate sel->ips_local_port = sel->ips_remote_port = 0; 28748810c16bSdanmcd ipsec_freemsg_chain(spare_mp); /* Always works, even if NULL */ 28757c478bd9Sstevel@tonic-gate return (B_TRUE); 28767c478bd9Sstevel@tonic-gate } 28777c478bd9Sstevel@tonic-gate 28788810c16bSdanmcd if (&mp->b_rptr[hdr_len] + 4 + outer_hdr_len > mp->b_wptr) { 28797c478bd9Sstevel@tonic-gate /* If we didn't pullup a copy already, do so now. */ 28807c478bd9Sstevel@tonic-gate /* 28817c478bd9Sstevel@tonic-gate * XXX performance, will upper-layers frequently split TCP/UDP 28827c478bd9Sstevel@tonic-gate * apart from IP or options? If so, perhaps we should revisit 28837c478bd9Sstevel@tonic-gate * the spare_mp strategy. 28847c478bd9Sstevel@tonic-gate * 28857c478bd9Sstevel@tonic-gate * XXX should this be msgpullup(mp, hdr_len+4) ??? 28867c478bd9Sstevel@tonic-gate */ 28877c478bd9Sstevel@tonic-gate if (spare_mp == NULL && 28887c478bd9Sstevel@tonic-gate (spare_mp = msgpullup(mp, -1)) == NULL) { 2889bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_FALSE, NULL, 2890f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 2891f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 28927c478bd9Sstevel@tonic-gate return (B_FALSE); 28937c478bd9Sstevel@tonic-gate } 28948810c16bSdanmcd ports = (uint16_t *)&spare_mp->b_rptr[hdr_len + outer_hdr_len]; 28957c478bd9Sstevel@tonic-gate } else { 28968810c16bSdanmcd ports = (uint16_t *)&mp->b_rptr[hdr_len + outer_hdr_len]; 28977c478bd9Sstevel@tonic-gate } 28987c478bd9Sstevel@tonic-gate 28997c478bd9Sstevel@tonic-gate if (nexthdr == check_proto) { 29007c478bd9Sstevel@tonic-gate typecode = (uint8_t *)ports; 29017c478bd9Sstevel@tonic-gate sel->ips_icmp_type = *typecode++; 29027c478bd9Sstevel@tonic-gate sel->ips_icmp_code = *typecode; 29037c478bd9Sstevel@tonic-gate sel->ips_remote_port = sel->ips_local_port = 0; 29048810c16bSdanmcd } else { 29057c478bd9Sstevel@tonic-gate sel->ips_local_port = *ports++; 29067c478bd9Sstevel@tonic-gate sel->ips_remote_port = *ports; 29078810c16bSdanmcd } 29088810c16bSdanmcd ipsec_freemsg_chain(spare_mp); /* Always works, even if NULL */ 29097c478bd9Sstevel@tonic-gate return (B_TRUE); 29107c478bd9Sstevel@tonic-gate } 29117c478bd9Sstevel@tonic-gate 29127c478bd9Sstevel@tonic-gate /* 2913bd670b35SErik Nordmark * Prepend an mblk with a ipsec_crypto_t to the message chain. 2914bd670b35SErik Nordmark * Frees the argument and returns NULL should the allocation fail. 2915bd670b35SErik Nordmark * Returns the pointer to the crypto data part. 2916bd670b35SErik Nordmark */ 2917bd670b35SErik Nordmark mblk_t * 2918bd670b35SErik Nordmark ipsec_add_crypto_data(mblk_t *data_mp, ipsec_crypto_t **icp) 2919bd670b35SErik Nordmark { 2920bd670b35SErik Nordmark mblk_t *mp; 2921bd670b35SErik Nordmark 2922bd670b35SErik Nordmark mp = allocb(sizeof (ipsec_crypto_t), BPRI_MED); 2923bd670b35SErik Nordmark if (mp == NULL) { 2924bd670b35SErik Nordmark freemsg(data_mp); 2925bd670b35SErik Nordmark return (NULL); 2926bd670b35SErik Nordmark } 2927bd670b35SErik Nordmark bzero(mp->b_rptr, sizeof (ipsec_crypto_t)); 2928bd670b35SErik Nordmark mp->b_wptr += sizeof (ipsec_crypto_t); 2929bd670b35SErik Nordmark mp->b_cont = data_mp; 2930bd670b35SErik Nordmark mp->b_datap->db_type = M_EVENT; /* For ASSERT */ 2931bd670b35SErik Nordmark *icp = (ipsec_crypto_t *)mp->b_rptr; 2932bd670b35SErik Nordmark return (mp); 2933bd670b35SErik Nordmark } 2934bd670b35SErik Nordmark 2935bd670b35SErik Nordmark /* 2936bd670b35SErik Nordmark * Remove what was prepended above. Return b_cont and a pointer to the 2937bd670b35SErik Nordmark * crypto data. 2938bd670b35SErik Nordmark * The caller must call ipsec_free_crypto_data for mblk once it is done 2939bd670b35SErik Nordmark * with the crypto data. 2940bd670b35SErik Nordmark */ 2941bd670b35SErik Nordmark mblk_t * 2942bd670b35SErik Nordmark ipsec_remove_crypto_data(mblk_t *crypto_mp, ipsec_crypto_t **icp) 2943bd670b35SErik Nordmark { 2944bd670b35SErik Nordmark ASSERT(crypto_mp->b_datap->db_type == M_EVENT); 2945bd670b35SErik Nordmark ASSERT(MBLKL(crypto_mp) == sizeof (ipsec_crypto_t)); 2946bd670b35SErik Nordmark 2947bd670b35SErik Nordmark *icp = (ipsec_crypto_t *)crypto_mp->b_rptr; 2948bd670b35SErik Nordmark return (crypto_mp->b_cont); 2949bd670b35SErik Nordmark } 2950bd670b35SErik Nordmark 2951bd670b35SErik Nordmark /* 2952bd670b35SErik Nordmark * Free what was prepended above. Return b_cont. 2953bd670b35SErik Nordmark */ 2954bd670b35SErik Nordmark mblk_t * 2955bd670b35SErik Nordmark ipsec_free_crypto_data(mblk_t *crypto_mp) 2956bd670b35SErik Nordmark { 2957bd670b35SErik Nordmark mblk_t *mp; 2958bd670b35SErik Nordmark 2959bd670b35SErik Nordmark ASSERT(crypto_mp->b_datap->db_type == M_EVENT); 2960bd670b35SErik Nordmark ASSERT(MBLKL(crypto_mp) == sizeof (ipsec_crypto_t)); 2961bd670b35SErik Nordmark 2962bd670b35SErik Nordmark mp = crypto_mp->b_cont; 2963bd670b35SErik Nordmark freeb(crypto_mp); 2964bd670b35SErik Nordmark return (mp); 2965bd670b35SErik Nordmark } 2966bd670b35SErik Nordmark 2967bd670b35SErik Nordmark /* 29687c478bd9Sstevel@tonic-gate * Create an ipsec_action_t based on the way an inbound packet was protected. 29697c478bd9Sstevel@tonic-gate * Used to reflect traffic back to a sender. 29707c478bd9Sstevel@tonic-gate * 29717c478bd9Sstevel@tonic-gate * We don't bother interning the action into the hash table. 29727c478bd9Sstevel@tonic-gate */ 29737c478bd9Sstevel@tonic-gate ipsec_action_t * 2974bd670b35SErik Nordmark ipsec_in_to_out_action(ip_recv_attr_t *ira) 29757c478bd9Sstevel@tonic-gate { 29767c478bd9Sstevel@tonic-gate ipsa_t *ah_assoc, *esp_assoc; 29777c478bd9Sstevel@tonic-gate uint_t auth_alg = 0, encr_alg = 0, espa_alg = 0; 29787c478bd9Sstevel@tonic-gate ipsec_action_t *ap; 29797c478bd9Sstevel@tonic-gate boolean_t unique; 29807c478bd9Sstevel@tonic-gate 29817c478bd9Sstevel@tonic-gate ap = kmem_cache_alloc(ipsec_action_cache, KM_NOSLEEP); 29827c478bd9Sstevel@tonic-gate 29837c478bd9Sstevel@tonic-gate if (ap == NULL) 29847c478bd9Sstevel@tonic-gate return (NULL); 29857c478bd9Sstevel@tonic-gate 29867c478bd9Sstevel@tonic-gate bzero(ap, sizeof (*ap)); 29876a182920Ssommerfe HASH_NULL(ap, ipa_hash); 29887c478bd9Sstevel@tonic-gate ap->ipa_next = NULL; 29897c478bd9Sstevel@tonic-gate ap->ipa_refs = 1; 29907c478bd9Sstevel@tonic-gate 29917c478bd9Sstevel@tonic-gate /* 29927c478bd9Sstevel@tonic-gate * Get the algorithms that were used for this packet. 29937c478bd9Sstevel@tonic-gate */ 29947c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_type = IPSEC_ACT_APPLY; 29957c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_log = 0; 2996bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 2997bd670b35SErik Nordmark 2998bd670b35SErik Nordmark ah_assoc = ira->ira_ipsec_ah_sa; 29997c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_use_ah = (ah_assoc != NULL); 30007c478bd9Sstevel@tonic-gate 3001bd670b35SErik Nordmark esp_assoc = ira->ira_ipsec_esp_sa; 30027c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_use_esp = (esp_assoc != NULL); 30037c478bd9Sstevel@tonic-gate 30047c478bd9Sstevel@tonic-gate if (esp_assoc != NULL) { 30057c478bd9Sstevel@tonic-gate encr_alg = esp_assoc->ipsa_encr_alg; 30067c478bd9Sstevel@tonic-gate espa_alg = esp_assoc->ipsa_auth_alg; 30077c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_use_espa = (espa_alg != 0); 30087c478bd9Sstevel@tonic-gate } 30097c478bd9Sstevel@tonic-gate if (ah_assoc != NULL) 30107c478bd9Sstevel@tonic-gate auth_alg = ah_assoc->ipsa_auth_alg; 30117c478bd9Sstevel@tonic-gate 30127c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_encr_alg = (uint8_t)encr_alg; 30137c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_auth_alg = (uint8_t)auth_alg; 30147c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_esp_auth_alg = (uint8_t)espa_alg; 3015bd670b35SErik Nordmark ap->ipa_act.ipa_apply.ipp_use_se = 3016bd670b35SErik Nordmark !!(ira->ira_flags & IRAF_IPSEC_DECAPS); 30177c478bd9Sstevel@tonic-gate unique = B_FALSE; 30187c478bd9Sstevel@tonic-gate 30197c478bd9Sstevel@tonic-gate if (esp_assoc != NULL) { 30207c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_espa_minbits = 30217c478bd9Sstevel@tonic-gate esp_assoc->ipsa_authkeybits; 30227c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_espa_maxbits = 30237c478bd9Sstevel@tonic-gate esp_assoc->ipsa_authkeybits; 30247c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_espe_minbits = 30257c478bd9Sstevel@tonic-gate esp_assoc->ipsa_encrkeybits; 30267c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_espe_maxbits = 30277c478bd9Sstevel@tonic-gate esp_assoc->ipsa_encrkeybits; 30287c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_km_proto = esp_assoc->ipsa_kmp; 30297c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_km_cookie = esp_assoc->ipsa_kmc; 30307c478bd9Sstevel@tonic-gate if (esp_assoc->ipsa_flags & IPSA_F_UNIQUE) 30317c478bd9Sstevel@tonic-gate unique = B_TRUE; 30327c478bd9Sstevel@tonic-gate } 30337c478bd9Sstevel@tonic-gate if (ah_assoc != NULL) { 30347c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_ah_minbits = 30357c478bd9Sstevel@tonic-gate ah_assoc->ipsa_authkeybits; 30367c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_ah_maxbits = 30377c478bd9Sstevel@tonic-gate ah_assoc->ipsa_authkeybits; 30387c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_km_proto = ah_assoc->ipsa_kmp; 30397c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_km_cookie = ah_assoc->ipsa_kmc; 30407c478bd9Sstevel@tonic-gate if (ah_assoc->ipsa_flags & IPSA_F_UNIQUE) 30417c478bd9Sstevel@tonic-gate unique = B_TRUE; 30427c478bd9Sstevel@tonic-gate } 30437c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_use_unique = unique; 30447c478bd9Sstevel@tonic-gate ap->ipa_want_unique = unique; 30457c478bd9Sstevel@tonic-gate ap->ipa_allow_clear = B_FALSE; 3046bd670b35SErik Nordmark ap->ipa_want_se = !!(ira->ira_flags & IRAF_IPSEC_DECAPS); 30477c478bd9Sstevel@tonic-gate ap->ipa_want_ah = (ah_assoc != NULL); 30487c478bd9Sstevel@tonic-gate ap->ipa_want_esp = (esp_assoc != NULL); 30497c478bd9Sstevel@tonic-gate 30507c478bd9Sstevel@tonic-gate ap->ipa_ovhd = ipsec_act_ovhd(&ap->ipa_act); 30517c478bd9Sstevel@tonic-gate 30527c478bd9Sstevel@tonic-gate ap->ipa_act.ipa_apply.ipp_replay_depth = 0; /* don't care */ 30537c478bd9Sstevel@tonic-gate 30547c478bd9Sstevel@tonic-gate return (ap); 30557c478bd9Sstevel@tonic-gate } 30567c478bd9Sstevel@tonic-gate 30577c478bd9Sstevel@tonic-gate 30587c478bd9Sstevel@tonic-gate /* 30597c478bd9Sstevel@tonic-gate * Compute the worst-case amount of extra space required by an action. 30607c478bd9Sstevel@tonic-gate * Note that, because of the ESP considerations listed below, this is 30617c478bd9Sstevel@tonic-gate * actually not the same as the best-case reduction in the MTU; in the 30627c478bd9Sstevel@tonic-gate * future, we should pass additional information to this function to 30637c478bd9Sstevel@tonic-gate * allow the actual MTU impact to be computed. 30647c478bd9Sstevel@tonic-gate * 30657c478bd9Sstevel@tonic-gate * AH: Revisit this if we implement algorithms with 30667c478bd9Sstevel@tonic-gate * a verifier size of more than 12 bytes. 30677c478bd9Sstevel@tonic-gate * 30687c478bd9Sstevel@tonic-gate * ESP: A more exact but more messy computation would take into 30697c478bd9Sstevel@tonic-gate * account the interaction between the cipher block size and the 30707c478bd9Sstevel@tonic-gate * effective MTU, yielding the inner payload size which reflects a 30717c478bd9Sstevel@tonic-gate * packet with *minimum* ESP padding.. 30727c478bd9Sstevel@tonic-gate */ 30738810c16bSdanmcd int32_t 30747c478bd9Sstevel@tonic-gate ipsec_act_ovhd(const ipsec_act_t *act) 30757c478bd9Sstevel@tonic-gate { 30767c478bd9Sstevel@tonic-gate int32_t overhead = 0; 30777c478bd9Sstevel@tonic-gate 30787c478bd9Sstevel@tonic-gate if (act->ipa_type == IPSEC_ACT_APPLY) { 30797c478bd9Sstevel@tonic-gate const ipsec_prot_t *ipp = &act->ipa_apply; 30807c478bd9Sstevel@tonic-gate 30817c478bd9Sstevel@tonic-gate if (ipp->ipp_use_ah) 30827c478bd9Sstevel@tonic-gate overhead += IPSEC_MAX_AH_HDR_SIZE; 30837c478bd9Sstevel@tonic-gate if (ipp->ipp_use_esp) { 30847c478bd9Sstevel@tonic-gate overhead += IPSEC_MAX_ESP_HDR_SIZE; 30857c478bd9Sstevel@tonic-gate overhead += sizeof (struct udphdr); 30867c478bd9Sstevel@tonic-gate } 30877c478bd9Sstevel@tonic-gate if (ipp->ipp_use_se) 30887c478bd9Sstevel@tonic-gate overhead += IP_SIMPLE_HDR_LENGTH; 30897c478bd9Sstevel@tonic-gate } 30907c478bd9Sstevel@tonic-gate return (overhead); 30917c478bd9Sstevel@tonic-gate } 30927c478bd9Sstevel@tonic-gate 30937c478bd9Sstevel@tonic-gate /* 30947c478bd9Sstevel@tonic-gate * This hash function is used only when creating policies and thus is not 30957c478bd9Sstevel@tonic-gate * performance-critical for packet flows. 30967c478bd9Sstevel@tonic-gate * 30977c478bd9Sstevel@tonic-gate * Future work: canonicalize the structures hashed with this (i.e., 30987c478bd9Sstevel@tonic-gate * zeroize padding) so the hash works correctly. 30997c478bd9Sstevel@tonic-gate */ 31007c478bd9Sstevel@tonic-gate /* ARGSUSED */ 31017c478bd9Sstevel@tonic-gate static uint32_t 31027c478bd9Sstevel@tonic-gate policy_hash(int size, const void *start, const void *end) 31037c478bd9Sstevel@tonic-gate { 31047c478bd9Sstevel@tonic-gate return (0); 31057c478bd9Sstevel@tonic-gate } 31067c478bd9Sstevel@tonic-gate 31076a182920Ssommerfe 31086a182920Ssommerfe /* 31096a182920Ssommerfe * Hash function macros for each address type. 31106a182920Ssommerfe * 31116a182920Ssommerfe * The IPV6 hash function assumes that the low order 32-bits of the 31126a182920Ssommerfe * address (typically containing the low order 24 bits of the mac 31136a182920Ssommerfe * address) are reasonably well-distributed. Revisit this if we run 31146a182920Ssommerfe * into trouble from lots of collisions on ::1 addresses and the like 31156a182920Ssommerfe * (seems unlikely). 31166a182920Ssommerfe */ 31178810c16bSdanmcd #define IPSEC_IPV4_HASH(a, n) ((a) % (n)) 31188810c16bSdanmcd #define IPSEC_IPV6_HASH(a, n) (((a).s6_addr32[3]) % (n)) 31196a182920Ssommerfe 31206a182920Ssommerfe /* 31216a182920Ssommerfe * These two hash functions should produce coordinated values 31226a182920Ssommerfe * but have slightly different roles. 31236a182920Ssommerfe */ 31246a182920Ssommerfe static uint32_t 3125f4b3ec61Sdh155122 selkey_hash(const ipsec_selkey_t *selkey, netstack_t *ns) 31266a182920Ssommerfe { 31276a182920Ssommerfe uint32_t valid = selkey->ipsl_valid; 3128f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 31296a182920Ssommerfe 31306a182920Ssommerfe if (!(valid & IPSL_REMOTE_ADDR)) 31316a182920Ssommerfe return (IPSEC_SEL_NOHASH); 31326a182920Ssommerfe 31336a182920Ssommerfe if (valid & IPSL_IPV4) { 3134f4b3ec61Sdh155122 if (selkey->ipsl_remote_pfxlen == 32) { 31358810c16bSdanmcd return (IPSEC_IPV4_HASH(selkey->ipsl_remote.ipsad_v4, 3136f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize)); 3137f4b3ec61Sdh155122 } 31386a182920Ssommerfe } 31396a182920Ssommerfe if (valid & IPSL_IPV6) { 3140f4b3ec61Sdh155122 if (selkey->ipsl_remote_pfxlen == 128) { 31418810c16bSdanmcd return (IPSEC_IPV6_HASH(selkey->ipsl_remote.ipsad_v6, 3142f4b3ec61Sdh155122 ipss->ipsec_spd_hashsize)); 3143f4b3ec61Sdh155122 } 31446a182920Ssommerfe } 31456a182920Ssommerfe return (IPSEC_SEL_NOHASH); 31466a182920Ssommerfe } 31476a182920Ssommerfe 31486a182920Ssommerfe static uint32_t 31498810c16bSdanmcd selector_hash(ipsec_selector_t *sel, ipsec_policy_root_t *root) 31506a182920Ssommerfe { 31516a182920Ssommerfe if (sel->ips_isv4) { 31528810c16bSdanmcd return (IPSEC_IPV4_HASH(sel->ips_remote_addr_v4, 31538810c16bSdanmcd root->ipr_nchains)); 31546a182920Ssommerfe } 31558810c16bSdanmcd return (IPSEC_IPV6_HASH(sel->ips_remote_addr_v6, root->ipr_nchains)); 31566a182920Ssommerfe } 31576a182920Ssommerfe 31587c478bd9Sstevel@tonic-gate /* 31597c478bd9Sstevel@tonic-gate * Intern actions into the action hash table. 31607c478bd9Sstevel@tonic-gate */ 31617c478bd9Sstevel@tonic-gate ipsec_action_t * 3162f4b3ec61Sdh155122 ipsec_act_find(const ipsec_act_t *a, int n, netstack_t *ns) 31637c478bd9Sstevel@tonic-gate { 31647c478bd9Sstevel@tonic-gate int i; 31657c478bd9Sstevel@tonic-gate uint32_t hval; 31667c478bd9Sstevel@tonic-gate ipsec_action_t *ap; 31677c478bd9Sstevel@tonic-gate ipsec_action_t *prev = NULL; 31687c478bd9Sstevel@tonic-gate int32_t overhead, maxovhd = 0; 31697c478bd9Sstevel@tonic-gate boolean_t allow_clear = B_FALSE; 31707c478bd9Sstevel@tonic-gate boolean_t want_ah = B_FALSE; 31717c478bd9Sstevel@tonic-gate boolean_t want_esp = B_FALSE; 31727c478bd9Sstevel@tonic-gate boolean_t want_se = B_FALSE; 31737c478bd9Sstevel@tonic-gate boolean_t want_unique = B_FALSE; 3174f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 31757c478bd9Sstevel@tonic-gate 31767c478bd9Sstevel@tonic-gate /* 31777c478bd9Sstevel@tonic-gate * TODO: should canonicalize a[] (i.e., zeroize any padding) 31787c478bd9Sstevel@tonic-gate * so we can use a non-trivial policy_hash function. 31797c478bd9Sstevel@tonic-gate */ 31807c478bd9Sstevel@tonic-gate for (i = n-1; i >= 0; i--) { 31817c478bd9Sstevel@tonic-gate hval = policy_hash(IPSEC_ACTION_HASH_SIZE, &a[i], &a[n]); 31827c478bd9Sstevel@tonic-gate 3183f4b3ec61Sdh155122 HASH_LOCK(ipss->ipsec_action_hash, hval); 31847c478bd9Sstevel@tonic-gate 3185f4b3ec61Sdh155122 for (HASH_ITERATE(ap, ipa_hash, 3186f4b3ec61Sdh155122 ipss->ipsec_action_hash, hval)) { 31877c478bd9Sstevel@tonic-gate if (bcmp(&ap->ipa_act, &a[i], sizeof (*a)) != 0) 31887c478bd9Sstevel@tonic-gate continue; 31897c478bd9Sstevel@tonic-gate if (ap->ipa_next != prev) 31907c478bd9Sstevel@tonic-gate continue; 31917c478bd9Sstevel@tonic-gate break; 31927c478bd9Sstevel@tonic-gate } 31937c478bd9Sstevel@tonic-gate if (ap != NULL) { 3194f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_action_hash, hval); 31957c478bd9Sstevel@tonic-gate prev = ap; 31967c478bd9Sstevel@tonic-gate continue; 31977c478bd9Sstevel@tonic-gate } 31987c478bd9Sstevel@tonic-gate /* 31997c478bd9Sstevel@tonic-gate * need to allocate a new one.. 32007c478bd9Sstevel@tonic-gate */ 32017c478bd9Sstevel@tonic-gate ap = kmem_cache_alloc(ipsec_action_cache, KM_NOSLEEP); 32027c478bd9Sstevel@tonic-gate if (ap == NULL) { 3203f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_action_hash, hval); 32047c478bd9Sstevel@tonic-gate if (prev != NULL) 32057c478bd9Sstevel@tonic-gate ipsec_action_free(prev); 32067c478bd9Sstevel@tonic-gate return (NULL); 32077c478bd9Sstevel@tonic-gate } 3208f4b3ec61Sdh155122 HASH_INSERT(ap, ipa_hash, ipss->ipsec_action_hash, hval); 32097c478bd9Sstevel@tonic-gate 32107c478bd9Sstevel@tonic-gate ap->ipa_next = prev; 32117c478bd9Sstevel@tonic-gate ap->ipa_act = a[i]; 32127c478bd9Sstevel@tonic-gate 32137c478bd9Sstevel@tonic-gate overhead = ipsec_act_ovhd(&a[i]); 32147c478bd9Sstevel@tonic-gate if (maxovhd < overhead) 32157c478bd9Sstevel@tonic-gate maxovhd = overhead; 32167c478bd9Sstevel@tonic-gate 32177c478bd9Sstevel@tonic-gate if ((a[i].ipa_type == IPSEC_ACT_BYPASS) || 32187c478bd9Sstevel@tonic-gate (a[i].ipa_type == IPSEC_ACT_CLEAR)) 32197c478bd9Sstevel@tonic-gate allow_clear = B_TRUE; 32207c478bd9Sstevel@tonic-gate if (a[i].ipa_type == IPSEC_ACT_APPLY) { 32217c478bd9Sstevel@tonic-gate const ipsec_prot_t *ipp = &a[i].ipa_apply; 32227c478bd9Sstevel@tonic-gate 32237c478bd9Sstevel@tonic-gate ASSERT(ipp->ipp_use_ah || ipp->ipp_use_esp); 32247c478bd9Sstevel@tonic-gate want_ah |= ipp->ipp_use_ah; 32257c478bd9Sstevel@tonic-gate want_esp |= ipp->ipp_use_esp; 32267c478bd9Sstevel@tonic-gate want_se |= ipp->ipp_use_se; 32277c478bd9Sstevel@tonic-gate want_unique |= ipp->ipp_use_unique; 32287c478bd9Sstevel@tonic-gate } 32297c478bd9Sstevel@tonic-gate ap->ipa_allow_clear = allow_clear; 32307c478bd9Sstevel@tonic-gate ap->ipa_want_ah = want_ah; 32317c478bd9Sstevel@tonic-gate ap->ipa_want_esp = want_esp; 32327c478bd9Sstevel@tonic-gate ap->ipa_want_se = want_se; 32337c478bd9Sstevel@tonic-gate ap->ipa_want_unique = want_unique; 32347c478bd9Sstevel@tonic-gate ap->ipa_refs = 1; /* from the hash table */ 32357c478bd9Sstevel@tonic-gate ap->ipa_ovhd = maxovhd; 32367c478bd9Sstevel@tonic-gate if (prev) 32377c478bd9Sstevel@tonic-gate prev->ipa_refs++; 32387c478bd9Sstevel@tonic-gate prev = ap; 3239f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_action_hash, hval); 32407c478bd9Sstevel@tonic-gate } 32417c478bd9Sstevel@tonic-gate 32427c478bd9Sstevel@tonic-gate ap->ipa_refs++; /* caller's reference */ 32437c478bd9Sstevel@tonic-gate 32447c478bd9Sstevel@tonic-gate return (ap); 32457c478bd9Sstevel@tonic-gate } 32467c478bd9Sstevel@tonic-gate 32477c478bd9Sstevel@tonic-gate /* 32487c478bd9Sstevel@tonic-gate * Called when refcount goes to 0, indicating that all references to this 32497c478bd9Sstevel@tonic-gate * node are gone. 32507c478bd9Sstevel@tonic-gate * 32517c478bd9Sstevel@tonic-gate * This does not unchain the action from the hash table. 32527c478bd9Sstevel@tonic-gate */ 32537c478bd9Sstevel@tonic-gate void 32547c478bd9Sstevel@tonic-gate ipsec_action_free(ipsec_action_t *ap) 32557c478bd9Sstevel@tonic-gate { 32567c478bd9Sstevel@tonic-gate for (;;) { 32577c478bd9Sstevel@tonic-gate ipsec_action_t *np = ap->ipa_next; 32587c478bd9Sstevel@tonic-gate ASSERT(ap->ipa_refs == 0); 32597c478bd9Sstevel@tonic-gate ASSERT(ap->ipa_hash.hash_pp == NULL); 32607c478bd9Sstevel@tonic-gate kmem_cache_free(ipsec_action_cache, ap); 32617c478bd9Sstevel@tonic-gate ap = np; 32627c478bd9Sstevel@tonic-gate /* Inlined IPACT_REFRELE -- avoid recursion */ 32637c478bd9Sstevel@tonic-gate if (ap == NULL) 32647c478bd9Sstevel@tonic-gate break; 32657c478bd9Sstevel@tonic-gate membar_exit(); 32661a5e258fSJosef 'Jeff' Sipek if (atomic_dec_32_nv(&(ap)->ipa_refs) != 0) 32677c478bd9Sstevel@tonic-gate break; 32687c478bd9Sstevel@tonic-gate /* End inlined IPACT_REFRELE */ 32697c478bd9Sstevel@tonic-gate } 32707c478bd9Sstevel@tonic-gate } 32717c478bd9Sstevel@tonic-gate 32727c478bd9Sstevel@tonic-gate /* 3273f4b3ec61Sdh155122 * Called when the action hash table goes away. 3274f4b3ec61Sdh155122 * 3275f4b3ec61Sdh155122 * The actions can be queued on an mblk with ipsec_in or 3276f4b3ec61Sdh155122 * ipsec_out, hence the actions might still be around. 3277f4b3ec61Sdh155122 * But we decrement ipa_refs here since we no longer have 3278f4b3ec61Sdh155122 * a reference to the action from the hash table. 3279f4b3ec61Sdh155122 */ 3280f4b3ec61Sdh155122 static void 3281f4b3ec61Sdh155122 ipsec_action_free_table(ipsec_action_t *ap) 3282f4b3ec61Sdh155122 { 3283f4b3ec61Sdh155122 while (ap != NULL) { 3284f4b3ec61Sdh155122 ipsec_action_t *np = ap->ipa_next; 3285f4b3ec61Sdh155122 3286f4b3ec61Sdh155122 /* FIXME: remove? */ 3287f4b3ec61Sdh155122 (void) printf("ipsec_action_free_table(%p) ref %d\n", 3288f4b3ec61Sdh155122 (void *)ap, ap->ipa_refs); 3289f4b3ec61Sdh155122 ASSERT(ap->ipa_refs > 0); 3290f4b3ec61Sdh155122 IPACT_REFRELE(ap); 3291f4b3ec61Sdh155122 ap = np; 3292f4b3ec61Sdh155122 } 3293f4b3ec61Sdh155122 } 3294f4b3ec61Sdh155122 3295f4b3ec61Sdh155122 /* 3296f4b3ec61Sdh155122 * Need to walk all stack instances since the reclaim function 3297f4b3ec61Sdh155122 * is global for all instances 3298f4b3ec61Sdh155122 */ 3299f4b3ec61Sdh155122 /* ARGSUSED */ 3300f4b3ec61Sdh155122 static void 3301f4b3ec61Sdh155122 ipsec_action_reclaim(void *arg) 3302f4b3ec61Sdh155122 { 3303f4b3ec61Sdh155122 netstack_handle_t nh; 3304f4b3ec61Sdh155122 netstack_t *ns; 33054ba231ceSKacheong Poon ipsec_stack_t *ipss; 3306f4b3ec61Sdh155122 3307f4b3ec61Sdh155122 netstack_next_init(&nh); 3308f4b3ec61Sdh155122 while ((ns = netstack_next(&nh)) != NULL) { 33094ba231ceSKacheong Poon /* 33104ba231ceSKacheong Poon * netstack_next() can return a netstack_t with a NULL 33114ba231ceSKacheong Poon * netstack_ipsec at boot time. 33124ba231ceSKacheong Poon */ 33134ba231ceSKacheong Poon if ((ipss = ns->netstack_ipsec) == NULL) { 33144ba231ceSKacheong Poon netstack_rele(ns); 33154ba231ceSKacheong Poon continue; 33164ba231ceSKacheong Poon } 33174ba231ceSKacheong Poon ipsec_action_reclaim_stack(ipss); 3318f4b3ec61Sdh155122 netstack_rele(ns); 3319f4b3ec61Sdh155122 } 3320f4b3ec61Sdh155122 netstack_next_fini(&nh); 3321f4b3ec61Sdh155122 } 3322f4b3ec61Sdh155122 3323f4b3ec61Sdh155122 /* 33247c478bd9Sstevel@tonic-gate * Periodically sweep action hash table for actions with refcount==1, and 33257c478bd9Sstevel@tonic-gate * nuke them. We cannot do this "on demand" (i.e., from IPACT_REFRELE) 33267c478bd9Sstevel@tonic-gate * because we can't close the race between another thread finding the action 33277c478bd9Sstevel@tonic-gate * in the hash table without holding the bucket lock during IPACT_REFRELE. 33287c478bd9Sstevel@tonic-gate * Instead, we run this function sporadically to clean up after ourselves; 33297c478bd9Sstevel@tonic-gate * we also set it as the "reclaim" function for the action kmem_cache. 33307c478bd9Sstevel@tonic-gate * 33317c478bd9Sstevel@tonic-gate * Note that it may take several passes of ipsec_action_gc() to free all 33327c478bd9Sstevel@tonic-gate * "stale" actions. 33337c478bd9Sstevel@tonic-gate */ 33347c478bd9Sstevel@tonic-gate static void 33354ba231ceSKacheong Poon ipsec_action_reclaim_stack(ipsec_stack_t *ipss) 33367c478bd9Sstevel@tonic-gate { 33377c478bd9Sstevel@tonic-gate int i; 33387c478bd9Sstevel@tonic-gate 33397c478bd9Sstevel@tonic-gate for (i = 0; i < IPSEC_ACTION_HASH_SIZE; i++) { 33407c478bd9Sstevel@tonic-gate ipsec_action_t *ap, *np; 33417c478bd9Sstevel@tonic-gate 33427c478bd9Sstevel@tonic-gate /* skip the lock if nobody home */ 3343f4b3ec61Sdh155122 if (ipss->ipsec_action_hash[i].hash_head == NULL) 33447c478bd9Sstevel@tonic-gate continue; 33457c478bd9Sstevel@tonic-gate 3346f4b3ec61Sdh155122 HASH_LOCK(ipss->ipsec_action_hash, i); 3347f4b3ec61Sdh155122 for (ap = ipss->ipsec_action_hash[i].hash_head; 33487c478bd9Sstevel@tonic-gate ap != NULL; ap = np) { 33497c478bd9Sstevel@tonic-gate ASSERT(ap->ipa_refs > 0); 33507c478bd9Sstevel@tonic-gate np = ap->ipa_hash.hash_next; 33517c478bd9Sstevel@tonic-gate if (ap->ipa_refs > 1) 33527c478bd9Sstevel@tonic-gate continue; 3353f4b3ec61Sdh155122 HASH_UNCHAIN(ap, ipa_hash, 3354f4b3ec61Sdh155122 ipss->ipsec_action_hash, i); 33557c478bd9Sstevel@tonic-gate IPACT_REFRELE(ap); 33567c478bd9Sstevel@tonic-gate } 3357f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_action_hash, i); 33587c478bd9Sstevel@tonic-gate } 33597c478bd9Sstevel@tonic-gate } 33607c478bd9Sstevel@tonic-gate 33617c478bd9Sstevel@tonic-gate /* 33627c478bd9Sstevel@tonic-gate * Intern a selector set into the selector set hash table. 33637c478bd9Sstevel@tonic-gate * This is simpler than the actions case.. 33647c478bd9Sstevel@tonic-gate */ 33656a182920Ssommerfe static ipsec_sel_t * 3366f4b3ec61Sdh155122 ipsec_find_sel(ipsec_selkey_t *selkey, netstack_t *ns) 33677c478bd9Sstevel@tonic-gate { 33687c478bd9Sstevel@tonic-gate ipsec_sel_t *sp; 33696a182920Ssommerfe uint32_t hval, bucket; 3370f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 33717c478bd9Sstevel@tonic-gate 33727c478bd9Sstevel@tonic-gate /* 33737c478bd9Sstevel@tonic-gate * Exactly one AF bit should be set in selkey. 33747c478bd9Sstevel@tonic-gate */ 33757c478bd9Sstevel@tonic-gate ASSERT(!(selkey->ipsl_valid & IPSL_IPV4) ^ 33767c478bd9Sstevel@tonic-gate !(selkey->ipsl_valid & IPSL_IPV6)); 33777c478bd9Sstevel@tonic-gate 3378f4b3ec61Sdh155122 hval = selkey_hash(selkey, ns); 33798810c16bSdanmcd /* Set pol_hval to uninitialized until we put it in a polhead. */ 33808810c16bSdanmcd selkey->ipsl_sel_hval = hval; 33817c478bd9Sstevel@tonic-gate 33826a182920Ssommerfe bucket = (hval == IPSEC_SEL_NOHASH) ? 0 : hval; 33837c478bd9Sstevel@tonic-gate 3384f4b3ec61Sdh155122 ASSERT(!HASH_LOCKED(ipss->ipsec_sel_hash, bucket)); 3385f4b3ec61Sdh155122 HASH_LOCK(ipss->ipsec_sel_hash, bucket); 33866a182920Ssommerfe 3387f4b3ec61Sdh155122 for (HASH_ITERATE(sp, ipsl_hash, ipss->ipsec_sel_hash, bucket)) { 33888810c16bSdanmcd if (bcmp(&sp->ipsl_key, selkey, 33898810c16bSdanmcd offsetof(ipsec_selkey_t, ipsl_pol_hval)) == 0) 33907c478bd9Sstevel@tonic-gate break; 33917c478bd9Sstevel@tonic-gate } 33927c478bd9Sstevel@tonic-gate if (sp != NULL) { 33937c478bd9Sstevel@tonic-gate sp->ipsl_refs++; 33947c478bd9Sstevel@tonic-gate 3395f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_sel_hash, bucket); 33967c478bd9Sstevel@tonic-gate return (sp); 33977c478bd9Sstevel@tonic-gate } 33986a182920Ssommerfe 33997c478bd9Sstevel@tonic-gate sp = kmem_cache_alloc(ipsec_sel_cache, KM_NOSLEEP); 34007c478bd9Sstevel@tonic-gate if (sp == NULL) { 3401f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_sel_hash, bucket); 34027c478bd9Sstevel@tonic-gate return (NULL); 34037c478bd9Sstevel@tonic-gate } 34047c478bd9Sstevel@tonic-gate 3405f4b3ec61Sdh155122 HASH_INSERT(sp, ipsl_hash, ipss->ipsec_sel_hash, bucket); 34067c478bd9Sstevel@tonic-gate sp->ipsl_refs = 2; /* one for hash table, one for caller */ 34077c478bd9Sstevel@tonic-gate sp->ipsl_key = *selkey; 34088810c16bSdanmcd /* Set to uninitalized and have insertion into polhead fix things. */ 34098810c16bSdanmcd if (selkey->ipsl_sel_hval != IPSEC_SEL_NOHASH) 34108810c16bSdanmcd sp->ipsl_key.ipsl_pol_hval = 0; 34118810c16bSdanmcd else 34128810c16bSdanmcd sp->ipsl_key.ipsl_pol_hval = IPSEC_SEL_NOHASH; 34137c478bd9Sstevel@tonic-gate 3414f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_sel_hash, bucket); 34157c478bd9Sstevel@tonic-gate 34167c478bd9Sstevel@tonic-gate return (sp); 34177c478bd9Sstevel@tonic-gate } 34187c478bd9Sstevel@tonic-gate 34197c478bd9Sstevel@tonic-gate static void 3420f4b3ec61Sdh155122 ipsec_sel_rel(ipsec_sel_t **spp, netstack_t *ns) 34217c478bd9Sstevel@tonic-gate { 34227c478bd9Sstevel@tonic-gate ipsec_sel_t *sp = *spp; 34238810c16bSdanmcd int hval = sp->ipsl_key.ipsl_sel_hval; 3424f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 3425f4b3ec61Sdh155122 34267c478bd9Sstevel@tonic-gate *spp = NULL; 34277c478bd9Sstevel@tonic-gate 34286a182920Ssommerfe if (hval == IPSEC_SEL_NOHASH) 34296a182920Ssommerfe hval = 0; 34306a182920Ssommerfe 3431f4b3ec61Sdh155122 ASSERT(!HASH_LOCKED(ipss->ipsec_sel_hash, hval)); 3432f4b3ec61Sdh155122 HASH_LOCK(ipss->ipsec_sel_hash, hval); 34337c478bd9Sstevel@tonic-gate if (--sp->ipsl_refs == 1) { 3434f4b3ec61Sdh155122 HASH_UNCHAIN(sp, ipsl_hash, ipss->ipsec_sel_hash, hval); 34357c478bd9Sstevel@tonic-gate sp->ipsl_refs--; 3436f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_sel_hash, hval); 34377c478bd9Sstevel@tonic-gate ASSERT(sp->ipsl_refs == 0); 34387c478bd9Sstevel@tonic-gate kmem_cache_free(ipsec_sel_cache, sp); 34397c478bd9Sstevel@tonic-gate /* Caller unlocks */ 34407c478bd9Sstevel@tonic-gate return; 34417c478bd9Sstevel@tonic-gate } 34427c478bd9Sstevel@tonic-gate 3443f4b3ec61Sdh155122 HASH_UNLOCK(ipss->ipsec_sel_hash, hval); 34447c478bd9Sstevel@tonic-gate } 34457c478bd9Sstevel@tonic-gate 34467c478bd9Sstevel@tonic-gate /* 34477c478bd9Sstevel@tonic-gate * Free a policy rule which we know is no longer being referenced. 34487c478bd9Sstevel@tonic-gate */ 34497c478bd9Sstevel@tonic-gate void 3450bd670b35SErik Nordmark ipsec_policy_free(ipsec_policy_t *ipp) 34517c478bd9Sstevel@tonic-gate { 34527c478bd9Sstevel@tonic-gate ASSERT(ipp->ipsp_refs == 0); 34537c478bd9Sstevel@tonic-gate ASSERT(ipp->ipsp_sel != NULL); 34547c478bd9Sstevel@tonic-gate ASSERT(ipp->ipsp_act != NULL); 3455bd670b35SErik Nordmark ASSERT(ipp->ipsp_netstack != NULL); 3456f4b3ec61Sdh155122 3457bd670b35SErik Nordmark ipsec_sel_rel(&ipp->ipsp_sel, ipp->ipsp_netstack); 34587c478bd9Sstevel@tonic-gate IPACT_REFRELE(ipp->ipsp_act); 34597c478bd9Sstevel@tonic-gate kmem_cache_free(ipsec_pol_cache, ipp); 34607c478bd9Sstevel@tonic-gate } 34617c478bd9Sstevel@tonic-gate 34627c478bd9Sstevel@tonic-gate /* 34637c478bd9Sstevel@tonic-gate * Construction of new policy rules; construct a policy, and add it to 34647c478bd9Sstevel@tonic-gate * the appropriate tables. 34657c478bd9Sstevel@tonic-gate */ 34667c478bd9Sstevel@tonic-gate ipsec_policy_t * 34676a182920Ssommerfe ipsec_policy_create(ipsec_selkey_t *keys, const ipsec_act_t *a, 3468f4b3ec61Sdh155122 int nacts, int prio, uint64_t *index_ptr, netstack_t *ns) 34697c478bd9Sstevel@tonic-gate { 34707c478bd9Sstevel@tonic-gate ipsec_action_t *ap; 34717c478bd9Sstevel@tonic-gate ipsec_sel_t *sp; 34727c478bd9Sstevel@tonic-gate ipsec_policy_t *ipp; 3473f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 34747c478bd9Sstevel@tonic-gate 34758810c16bSdanmcd if (index_ptr == NULL) 3476f4b3ec61Sdh155122 index_ptr = &ipss->ipsec_next_policy_index; 34778810c16bSdanmcd 34787c478bd9Sstevel@tonic-gate ipp = kmem_cache_alloc(ipsec_pol_cache, KM_NOSLEEP); 3479f4b3ec61Sdh155122 ap = ipsec_act_find(a, nacts, ns); 3480f4b3ec61Sdh155122 sp = ipsec_find_sel(keys, ns); 34817c478bd9Sstevel@tonic-gate 34827c478bd9Sstevel@tonic-gate if ((ap == NULL) || (sp == NULL) || (ipp == NULL)) { 34837c478bd9Sstevel@tonic-gate if (ap != NULL) { 34847c478bd9Sstevel@tonic-gate IPACT_REFRELE(ap); 34857c478bd9Sstevel@tonic-gate } 34867c478bd9Sstevel@tonic-gate if (sp != NULL) 3487f4b3ec61Sdh155122 ipsec_sel_rel(&sp, ns); 34887c478bd9Sstevel@tonic-gate if (ipp != NULL) 34897c478bd9Sstevel@tonic-gate kmem_cache_free(ipsec_pol_cache, ipp); 34907c478bd9Sstevel@tonic-gate return (NULL); 34917c478bd9Sstevel@tonic-gate } 34927c478bd9Sstevel@tonic-gate 34936a182920Ssommerfe HASH_NULL(ipp, ipsp_hash); 34947c478bd9Sstevel@tonic-gate 3495bd670b35SErik Nordmark ipp->ipsp_netstack = ns; /* Needed for ipsec_policy_free */ 34967c478bd9Sstevel@tonic-gate ipp->ipsp_refs = 1; /* caller's reference */ 34977c478bd9Sstevel@tonic-gate ipp->ipsp_sel = sp; 34987c478bd9Sstevel@tonic-gate ipp->ipsp_act = ap; 34997c478bd9Sstevel@tonic-gate ipp->ipsp_prio = prio; /* rule priority */ 35008810c16bSdanmcd ipp->ipsp_index = *index_ptr; 35018810c16bSdanmcd (*index_ptr)++; 35027c478bd9Sstevel@tonic-gate 35037c478bd9Sstevel@tonic-gate return (ipp); 35047c478bd9Sstevel@tonic-gate } 35057c478bd9Sstevel@tonic-gate 35067c478bd9Sstevel@tonic-gate static void 3507f4b3ec61Sdh155122 ipsec_update_present_flags(ipsec_stack_t *ipss) 35087c478bd9Sstevel@tonic-gate { 3509f4b3ec61Sdh155122 boolean_t hashpol; 3510f4b3ec61Sdh155122 3511f4b3ec61Sdh155122 hashpol = (avl_numnodes(&ipss->ipsec_system_policy.iph_rulebyid) > 0); 35126a182920Ssommerfe 35136a182920Ssommerfe if (hashpol) { 3514f4b3ec61Sdh155122 ipss->ipsec_outbound_v4_policy_present = B_TRUE; 3515f4b3ec61Sdh155122 ipss->ipsec_outbound_v6_policy_present = B_TRUE; 3516f4b3ec61Sdh155122 ipss->ipsec_inbound_v4_policy_present = B_TRUE; 3517f4b3ec61Sdh155122 ipss->ipsec_inbound_v6_policy_present = B_TRUE; 35186a182920Ssommerfe return; 35196a182920Ssommerfe } 35206a182920Ssommerfe 3521f4b3ec61Sdh155122 ipss->ipsec_outbound_v4_policy_present = (NULL != 3522f4b3ec61Sdh155122 ipss->ipsec_system_policy.iph_root[IPSEC_TYPE_OUTBOUND]. 35236a182920Ssommerfe ipr_nonhash[IPSEC_AF_V4]); 3524f4b3ec61Sdh155122 ipss->ipsec_outbound_v6_policy_present = (NULL != 3525f4b3ec61Sdh155122 ipss->ipsec_system_policy.iph_root[IPSEC_TYPE_OUTBOUND]. 35266a182920Ssommerfe ipr_nonhash[IPSEC_AF_V6]); 3527f4b3ec61Sdh155122 ipss->ipsec_inbound_v4_policy_present = (NULL != 3528f4b3ec61Sdh155122 ipss->ipsec_system_policy.iph_root[IPSEC_TYPE_INBOUND]. 35296a182920Ssommerfe ipr_nonhash[IPSEC_AF_V4]); 3530f4b3ec61Sdh155122 ipss->ipsec_inbound_v6_policy_present = (NULL != 3531f4b3ec61Sdh155122 ipss->ipsec_system_policy.iph_root[IPSEC_TYPE_INBOUND]. 35326a182920Ssommerfe ipr_nonhash[IPSEC_AF_V6]); 35337c478bd9Sstevel@tonic-gate } 35347c478bd9Sstevel@tonic-gate 35357c478bd9Sstevel@tonic-gate boolean_t 3536f4b3ec61Sdh155122 ipsec_policy_delete(ipsec_policy_head_t *php, ipsec_selkey_t *keys, int dir, 3537f4b3ec61Sdh155122 netstack_t *ns) 35387c478bd9Sstevel@tonic-gate { 35397c478bd9Sstevel@tonic-gate ipsec_sel_t *sp; 35406a182920Ssommerfe ipsec_policy_t *ip, *nip, *head; 35417c478bd9Sstevel@tonic-gate int af; 35426a182920Ssommerfe ipsec_policy_root_t *pr = &php->iph_root[dir]; 35437c478bd9Sstevel@tonic-gate 3544f4b3ec61Sdh155122 sp = ipsec_find_sel(keys, ns); 35457c478bd9Sstevel@tonic-gate 35467c478bd9Sstevel@tonic-gate if (sp == NULL) 35477c478bd9Sstevel@tonic-gate return (B_FALSE); 35487c478bd9Sstevel@tonic-gate 35497c478bd9Sstevel@tonic-gate af = (sp->ipsl_key.ipsl_valid & IPSL_IPV4) ? IPSEC_AF_V4 : IPSEC_AF_V6; 35507c478bd9Sstevel@tonic-gate 35517c478bd9Sstevel@tonic-gate rw_enter(&php->iph_lock, RW_WRITER); 35527c478bd9Sstevel@tonic-gate 35538810c16bSdanmcd if (sp->ipsl_key.ipsl_pol_hval == IPSEC_SEL_NOHASH) { 35546a182920Ssommerfe head = pr->ipr_nonhash[af]; 35556a182920Ssommerfe } else { 35568810c16bSdanmcd head = pr->ipr_hash[sp->ipsl_key.ipsl_pol_hval].hash_head; 35576a182920Ssommerfe } 35586a182920Ssommerfe 35596a182920Ssommerfe for (ip = head; ip != NULL; ip = nip) { 35606a182920Ssommerfe nip = ip->ipsp_hash.hash_next; 35617c478bd9Sstevel@tonic-gate if (ip->ipsp_sel != sp) { 35627c478bd9Sstevel@tonic-gate continue; 35637c478bd9Sstevel@tonic-gate } 35646a182920Ssommerfe 3565bd670b35SErik Nordmark IPPOL_UNCHAIN(php, ip); 35666a182920Ssommerfe 35677c478bd9Sstevel@tonic-gate php->iph_gen++; 3568f4b3ec61Sdh155122 ipsec_update_present_flags(ns->netstack_ipsec); 35696a182920Ssommerfe 35706a182920Ssommerfe rw_exit(&php->iph_lock); 35716a182920Ssommerfe 3572f4b3ec61Sdh155122 ipsec_sel_rel(&sp, ns); 35736a182920Ssommerfe 35747c478bd9Sstevel@tonic-gate return (B_TRUE); 35757c478bd9Sstevel@tonic-gate } 35766a182920Ssommerfe 35777c478bd9Sstevel@tonic-gate rw_exit(&php->iph_lock); 3578f4b3ec61Sdh155122 ipsec_sel_rel(&sp, ns); 35797c478bd9Sstevel@tonic-gate return (B_FALSE); 35807c478bd9Sstevel@tonic-gate } 35817c478bd9Sstevel@tonic-gate 35827c478bd9Sstevel@tonic-gate int 3583f4b3ec61Sdh155122 ipsec_policy_delete_index(ipsec_policy_head_t *php, uint64_t policy_index, 3584f4b3ec61Sdh155122 netstack_t *ns) 35857c478bd9Sstevel@tonic-gate { 35866a182920Ssommerfe boolean_t found = B_FALSE; 35876a182920Ssommerfe ipsec_policy_t ipkey; 35886a182920Ssommerfe ipsec_policy_t *ip; 35896a182920Ssommerfe avl_index_t where; 35907c478bd9Sstevel@tonic-gate 3591188e1664SErik Nordmark bzero(&ipkey, sizeof (ipkey)); 35926a182920Ssommerfe ipkey.ipsp_index = policy_index; 35937c478bd9Sstevel@tonic-gate 35947c478bd9Sstevel@tonic-gate rw_enter(&php->iph_lock, RW_WRITER); 35957c478bd9Sstevel@tonic-gate 35966a182920Ssommerfe /* 35976a182920Ssommerfe * We could be cleverer here about the walk. 35986a182920Ssommerfe * but well, (k+1)*log(N) will do for now (k==number of matches, 35996a182920Ssommerfe * N==number of table entries 36006a182920Ssommerfe */ 36016a182920Ssommerfe for (;;) { 36026a182920Ssommerfe ip = (ipsec_policy_t *)avl_find(&php->iph_rulebyid, 36036a182920Ssommerfe (void *)&ipkey, &where); 36046a182920Ssommerfe ASSERT(ip == NULL); 36056a182920Ssommerfe 36066a182920Ssommerfe ip = avl_nearest(&php->iph_rulebyid, where, AVL_AFTER); 36076a182920Ssommerfe 36086a182920Ssommerfe if (ip == NULL) 36096a182920Ssommerfe break; 36106a182920Ssommerfe 36117c478bd9Sstevel@tonic-gate if (ip->ipsp_index != policy_index) { 36126a182920Ssommerfe ASSERT(ip->ipsp_index > policy_index); 36136a182920Ssommerfe break; 36147c478bd9Sstevel@tonic-gate } 36156a182920Ssommerfe 3616bd670b35SErik Nordmark IPPOL_UNCHAIN(php, ip); 36177c478bd9Sstevel@tonic-gate found = B_TRUE; 36187c478bd9Sstevel@tonic-gate } 36196a182920Ssommerfe 36206a182920Ssommerfe if (found) { 36216a182920Ssommerfe php->iph_gen++; 3622f4b3ec61Sdh155122 ipsec_update_present_flags(ns->netstack_ipsec); 36236a182920Ssommerfe } 36246a182920Ssommerfe 36257c478bd9Sstevel@tonic-gate rw_exit(&php->iph_lock); 36266a182920Ssommerfe 36277c478bd9Sstevel@tonic-gate return (found ? 0 : ENOENT); 36287c478bd9Sstevel@tonic-gate } 36297c478bd9Sstevel@tonic-gate 36307c478bd9Sstevel@tonic-gate /* 36317c478bd9Sstevel@tonic-gate * Given a constructed ipsec_policy_t policy rule, see if it can be entered 36328810c16bSdanmcd * into the correct policy ruleset. As a side-effect, it sets the hash 36338810c16bSdanmcd * entries on "ipp"'s ipsp_pol_hval. 36347c478bd9Sstevel@tonic-gate * 36357c478bd9Sstevel@tonic-gate * Returns B_TRUE if it can be entered, B_FALSE if it can't be (because a 36367c478bd9Sstevel@tonic-gate * duplicate policy exists with exactly the same selectors), or an icmp 36377c478bd9Sstevel@tonic-gate * rule exists with a different encryption/authentication action. 36387c478bd9Sstevel@tonic-gate */ 36397c478bd9Sstevel@tonic-gate boolean_t 36407c478bd9Sstevel@tonic-gate ipsec_check_policy(ipsec_policy_head_t *php, ipsec_policy_t *ipp, int direction) 36417c478bd9Sstevel@tonic-gate { 36427c478bd9Sstevel@tonic-gate ipsec_policy_root_t *pr = &php->iph_root[direction]; 36437c478bd9Sstevel@tonic-gate int af = -1; 36446a182920Ssommerfe ipsec_policy_t *p2, *head; 36457c478bd9Sstevel@tonic-gate uint8_t check_proto; 36466a182920Ssommerfe ipsec_selkey_t *selkey = &ipp->ipsp_sel->ipsl_key; 36476a182920Ssommerfe uint32_t valid = selkey->ipsl_valid; 36487c478bd9Sstevel@tonic-gate 36496a182920Ssommerfe if (valid & IPSL_IPV6) { 36506a182920Ssommerfe ASSERT(!(valid & IPSL_IPV4)); 36517c478bd9Sstevel@tonic-gate af = IPSEC_AF_V6; 36527c478bd9Sstevel@tonic-gate check_proto = IPPROTO_ICMPV6; 36537c478bd9Sstevel@tonic-gate } else { 36546a182920Ssommerfe ASSERT(valid & IPSL_IPV4); 36557c478bd9Sstevel@tonic-gate af = IPSEC_AF_V4; 36567c478bd9Sstevel@tonic-gate check_proto = IPPROTO_ICMP; 36577c478bd9Sstevel@tonic-gate } 36587c478bd9Sstevel@tonic-gate 36596a182920Ssommerfe ASSERT(RW_WRITE_HELD(&php->iph_lock)); 36606a182920Ssommerfe 36617c478bd9Sstevel@tonic-gate /* 36627c478bd9Sstevel@tonic-gate * Double-check that we don't have any duplicate selectors here. 36637c478bd9Sstevel@tonic-gate * Because selectors are interned below, we need only compare pointers 36647c478bd9Sstevel@tonic-gate * for equality. 36657c478bd9Sstevel@tonic-gate */ 36668810c16bSdanmcd if (selkey->ipsl_sel_hval == IPSEC_SEL_NOHASH) { 36676a182920Ssommerfe head = pr->ipr_nonhash[af]; 36686a182920Ssommerfe } else { 36698810c16bSdanmcd selkey->ipsl_pol_hval = 36708810c16bSdanmcd (selkey->ipsl_valid & IPSL_IPV4) ? 36718810c16bSdanmcd IPSEC_IPV4_HASH(selkey->ipsl_remote.ipsad_v4, 36728810c16bSdanmcd pr->ipr_nchains) : 36738810c16bSdanmcd IPSEC_IPV6_HASH(selkey->ipsl_remote.ipsad_v6, 36748810c16bSdanmcd pr->ipr_nchains); 36758810c16bSdanmcd 36768810c16bSdanmcd head = pr->ipr_hash[selkey->ipsl_pol_hval].hash_head; 36776a182920Ssommerfe } 36787c478bd9Sstevel@tonic-gate 36796a182920Ssommerfe for (p2 = head; p2 != NULL; p2 = p2->ipsp_hash.hash_next) { 36807c478bd9Sstevel@tonic-gate if (p2->ipsp_sel == ipp->ipsp_sel) 36817c478bd9Sstevel@tonic-gate return (B_FALSE); 36827c478bd9Sstevel@tonic-gate } 36837c478bd9Sstevel@tonic-gate 36847c478bd9Sstevel@tonic-gate /* 36857c478bd9Sstevel@tonic-gate * If it's ICMP and not a drop or pass rule, run through the ICMP 36867c478bd9Sstevel@tonic-gate * rules and make sure the action is either new or the same as any 36877c478bd9Sstevel@tonic-gate * other actions. We don't have to check the full chain because 36887c478bd9Sstevel@tonic-gate * discard and bypass will override all other actions 36897c478bd9Sstevel@tonic-gate */ 36907c478bd9Sstevel@tonic-gate 36916a182920Ssommerfe if (valid & IPSL_PROTOCOL && 36926a182920Ssommerfe selkey->ipsl_proto == check_proto && 36937c478bd9Sstevel@tonic-gate (ipp->ipsp_act->ipa_act.ipa_type == IPSEC_ACT_APPLY)) { 36946a182920Ssommerfe 36956a182920Ssommerfe for (p2 = head; p2 != NULL; p2 = p2->ipsp_hash.hash_next) { 36966a182920Ssommerfe 36977c478bd9Sstevel@tonic-gate if (p2->ipsp_sel->ipsl_key.ipsl_valid & IPSL_PROTOCOL && 36987c478bd9Sstevel@tonic-gate p2->ipsp_sel->ipsl_key.ipsl_proto == check_proto && 36997c478bd9Sstevel@tonic-gate (p2->ipsp_act->ipa_act.ipa_type == 37007c478bd9Sstevel@tonic-gate IPSEC_ACT_APPLY)) { 37017c478bd9Sstevel@tonic-gate return (ipsec_compare_action(p2, ipp)); 37027c478bd9Sstevel@tonic-gate } 37037c478bd9Sstevel@tonic-gate } 37047c478bd9Sstevel@tonic-gate } 37057c478bd9Sstevel@tonic-gate 37067c478bd9Sstevel@tonic-gate return (B_TRUE); 37077c478bd9Sstevel@tonic-gate } 37087c478bd9Sstevel@tonic-gate 37097c478bd9Sstevel@tonic-gate /* 37107c478bd9Sstevel@tonic-gate * compare the action chains of two policies for equality 37117c478bd9Sstevel@tonic-gate * B_TRUE -> effective equality 37127c478bd9Sstevel@tonic-gate */ 37137c478bd9Sstevel@tonic-gate 37147c478bd9Sstevel@tonic-gate static boolean_t 37157c478bd9Sstevel@tonic-gate ipsec_compare_action(ipsec_policy_t *p1, ipsec_policy_t *p2) 37167c478bd9Sstevel@tonic-gate { 37177c478bd9Sstevel@tonic-gate 37187c478bd9Sstevel@tonic-gate ipsec_action_t *act1, *act2; 37197c478bd9Sstevel@tonic-gate 37207c478bd9Sstevel@tonic-gate /* We have a valid rule. Let's compare the actions */ 37217c478bd9Sstevel@tonic-gate if (p1->ipsp_act == p2->ipsp_act) { 37227c478bd9Sstevel@tonic-gate /* same action. We are good */ 37237c478bd9Sstevel@tonic-gate return (B_TRUE); 37247c478bd9Sstevel@tonic-gate } 37257c478bd9Sstevel@tonic-gate 37267c478bd9Sstevel@tonic-gate /* we have to walk the chain */ 37277c478bd9Sstevel@tonic-gate 37287c478bd9Sstevel@tonic-gate act1 = p1->ipsp_act; 37297c478bd9Sstevel@tonic-gate act2 = p2->ipsp_act; 37307c478bd9Sstevel@tonic-gate 37317c478bd9Sstevel@tonic-gate while (act1 != NULL && act2 != NULL) { 37327c478bd9Sstevel@tonic-gate 37337c478bd9Sstevel@tonic-gate /* otherwise, Are we close enough? */ 37347c478bd9Sstevel@tonic-gate if (act1->ipa_allow_clear != act2->ipa_allow_clear || 37357c478bd9Sstevel@tonic-gate act1->ipa_want_ah != act2->ipa_want_ah || 37367c478bd9Sstevel@tonic-gate act1->ipa_want_esp != act2->ipa_want_esp || 37377c478bd9Sstevel@tonic-gate act1->ipa_want_se != act2->ipa_want_se) { 37387c478bd9Sstevel@tonic-gate /* Nope, we aren't */ 37397c478bd9Sstevel@tonic-gate return (B_FALSE); 37407c478bd9Sstevel@tonic-gate } 37417c478bd9Sstevel@tonic-gate 37427c478bd9Sstevel@tonic-gate if (act1->ipa_want_ah) { 37437c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_auth_alg != 37447c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_auth_alg) { 37457c478bd9Sstevel@tonic-gate return (B_FALSE); 37467c478bd9Sstevel@tonic-gate } 37477c478bd9Sstevel@tonic-gate 37487c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_ah_minbits != 37497c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_ah_minbits || 37507c478bd9Sstevel@tonic-gate act1->ipa_act.ipa_apply.ipp_ah_maxbits != 37517c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_ah_maxbits) { 37527c478bd9Sstevel@tonic-gate return (B_FALSE); 37537c478bd9Sstevel@tonic-gate } 37547c478bd9Sstevel@tonic-gate } 37557c478bd9Sstevel@tonic-gate 37567c478bd9Sstevel@tonic-gate if (act1->ipa_want_esp) { 37577c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_use_esp != 37587c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_use_esp || 37597c478bd9Sstevel@tonic-gate act1->ipa_act.ipa_apply.ipp_use_espa != 37607c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_use_espa) { 37617c478bd9Sstevel@tonic-gate return (B_FALSE); 37627c478bd9Sstevel@tonic-gate } 37637c478bd9Sstevel@tonic-gate 37647c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_use_esp) { 37657c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_encr_alg != 37667c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_encr_alg) { 37677c478bd9Sstevel@tonic-gate return (B_FALSE); 37687c478bd9Sstevel@tonic-gate } 37697c478bd9Sstevel@tonic-gate 37707c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_espe_minbits != 37717c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_espe_minbits || 37727c478bd9Sstevel@tonic-gate act1->ipa_act.ipa_apply.ipp_espe_maxbits != 37737c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_espe_maxbits) { 37747c478bd9Sstevel@tonic-gate return (B_FALSE); 37757c478bd9Sstevel@tonic-gate } 37767c478bd9Sstevel@tonic-gate } 37777c478bd9Sstevel@tonic-gate 37787c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_use_espa) { 37797c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_esp_auth_alg != 37807c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_esp_auth_alg) { 37817c478bd9Sstevel@tonic-gate return (B_FALSE); 37827c478bd9Sstevel@tonic-gate } 37837c478bd9Sstevel@tonic-gate 37847c478bd9Sstevel@tonic-gate if (act1->ipa_act.ipa_apply.ipp_espa_minbits != 37857c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_espa_minbits || 37867c478bd9Sstevel@tonic-gate act1->ipa_act.ipa_apply.ipp_espa_maxbits != 37877c478bd9Sstevel@tonic-gate act2->ipa_act.ipa_apply.ipp_espa_maxbits) { 37887c478bd9Sstevel@tonic-gate return (B_FALSE); 37897c478bd9Sstevel@tonic-gate } 37907c478bd9Sstevel@tonic-gate } 37917c478bd9Sstevel@tonic-gate 37927c478bd9Sstevel@tonic-gate } 37937c478bd9Sstevel@tonic-gate 37947c478bd9Sstevel@tonic-gate act1 = act1->ipa_next; 37957c478bd9Sstevel@tonic-gate act2 = act2->ipa_next; 37967c478bd9Sstevel@tonic-gate } 37977c478bd9Sstevel@tonic-gate 37987c478bd9Sstevel@tonic-gate if (act1 != NULL || act2 != NULL) { 37997c478bd9Sstevel@tonic-gate return (B_FALSE); 38007c478bd9Sstevel@tonic-gate } 38017c478bd9Sstevel@tonic-gate 38027c478bd9Sstevel@tonic-gate return (B_TRUE); 38037c478bd9Sstevel@tonic-gate } 38047c478bd9Sstevel@tonic-gate 38057c478bd9Sstevel@tonic-gate 38067c478bd9Sstevel@tonic-gate /* 38077c478bd9Sstevel@tonic-gate * Given a constructed ipsec_policy_t policy rule, enter it into 38087c478bd9Sstevel@tonic-gate * the correct policy ruleset. 38097c478bd9Sstevel@tonic-gate * 38107c478bd9Sstevel@tonic-gate * ipsec_check_policy() is assumed to have succeeded first (to check for 38117c478bd9Sstevel@tonic-gate * duplicates). 38127c478bd9Sstevel@tonic-gate */ 38137c478bd9Sstevel@tonic-gate void 3814f4b3ec61Sdh155122 ipsec_enter_policy(ipsec_policy_head_t *php, ipsec_policy_t *ipp, int direction, 3815f4b3ec61Sdh155122 netstack_t *ns) 38167c478bd9Sstevel@tonic-gate { 38177c478bd9Sstevel@tonic-gate ipsec_policy_root_t *pr = &php->iph_root[direction]; 38186a182920Ssommerfe ipsec_selkey_t *selkey = &ipp->ipsp_sel->ipsl_key; 38196a182920Ssommerfe uint32_t valid = selkey->ipsl_valid; 38208810c16bSdanmcd uint32_t hval = selkey->ipsl_pol_hval; 38217c478bd9Sstevel@tonic-gate int af = -1; 38227c478bd9Sstevel@tonic-gate 38237c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&php->iph_lock)); 38247c478bd9Sstevel@tonic-gate 38256a182920Ssommerfe if (valid & IPSL_IPV6) { 38266a182920Ssommerfe ASSERT(!(valid & IPSL_IPV4)); 38277c478bd9Sstevel@tonic-gate af = IPSEC_AF_V6; 38287c478bd9Sstevel@tonic-gate } else { 38296a182920Ssommerfe ASSERT(valid & IPSL_IPV4); 38307c478bd9Sstevel@tonic-gate af = IPSEC_AF_V4; 38317c478bd9Sstevel@tonic-gate } 38327c478bd9Sstevel@tonic-gate 38337c478bd9Sstevel@tonic-gate php->iph_gen++; 38347c478bd9Sstevel@tonic-gate 38356a182920Ssommerfe if (hval == IPSEC_SEL_NOHASH) { 38366a182920Ssommerfe HASHLIST_INSERT(ipp, ipsp_hash, pr->ipr_nonhash[af]); 38376a182920Ssommerfe } else { 38386a182920Ssommerfe HASH_LOCK(pr->ipr_hash, hval); 38396a182920Ssommerfe HASH_INSERT(ipp, ipsp_hash, pr->ipr_hash, hval); 38406a182920Ssommerfe HASH_UNLOCK(pr->ipr_hash, hval); 38416a182920Ssommerfe } 38426a182920Ssommerfe 38436a182920Ssommerfe ipsec_insert_always(&php->iph_rulebyid, ipp); 38446a182920Ssommerfe 3845f4b3ec61Sdh155122 ipsec_update_present_flags(ns->netstack_ipsec); 38467c478bd9Sstevel@tonic-gate } 38477c478bd9Sstevel@tonic-gate 38486a182920Ssommerfe static void 3849bd670b35SErik Nordmark ipsec_ipr_flush(ipsec_policy_head_t *php, ipsec_policy_root_t *ipr) 38506a182920Ssommerfe { 38516a182920Ssommerfe ipsec_policy_t *ip, *nip; 38526a182920Ssommerfe int af, chain, nchain; 38536a182920Ssommerfe 38546a182920Ssommerfe for (af = 0; af < IPSEC_NAF; af++) { 38556a182920Ssommerfe for (ip = ipr->ipr_nonhash[af]; ip != NULL; ip = nip) { 38566a182920Ssommerfe nip = ip->ipsp_hash.hash_next; 3857bd670b35SErik Nordmark IPPOL_UNCHAIN(php, ip); 38586a182920Ssommerfe } 38596a182920Ssommerfe ipr->ipr_nonhash[af] = NULL; 38606a182920Ssommerfe } 38616a182920Ssommerfe nchain = ipr->ipr_nchains; 38626a182920Ssommerfe 38636a182920Ssommerfe for (chain = 0; chain < nchain; chain++) { 38646a182920Ssommerfe for (ip = ipr->ipr_hash[chain].hash_head; ip != NULL; 38656a182920Ssommerfe ip = nip) { 38666a182920Ssommerfe nip = ip->ipsp_hash.hash_next; 3867bd670b35SErik Nordmark IPPOL_UNCHAIN(php, ip); 38686a182920Ssommerfe } 38696a182920Ssommerfe ipr->ipr_hash[chain].hash_head = NULL; 38706a182920Ssommerfe } 38716a182920Ssommerfe } 38726a182920Ssommerfe 38732b24ab6bSSebastien Roy /* 38742b24ab6bSSebastien Roy * Create and insert inbound or outbound policy associated with actp for the 38752b24ab6bSSebastien Roy * address family fam into the policy head ph. Returns B_TRUE if policy was 38762b24ab6bSSebastien Roy * inserted, and B_FALSE otherwise. 38772b24ab6bSSebastien Roy */ 38782b24ab6bSSebastien Roy boolean_t 38792b24ab6bSSebastien Roy ipsec_polhead_insert(ipsec_policy_head_t *ph, ipsec_act_t *actp, uint_t nact, 38802b24ab6bSSebastien Roy int fam, int ptype, netstack_t *ns) 38812b24ab6bSSebastien Roy { 38822b24ab6bSSebastien Roy ipsec_selkey_t sel; 38832b24ab6bSSebastien Roy ipsec_policy_t *pol; 38842b24ab6bSSebastien Roy ipsec_policy_root_t *pr; 38852b24ab6bSSebastien Roy 38862b24ab6bSSebastien Roy bzero(&sel, sizeof (sel)); 38872b24ab6bSSebastien Roy sel.ipsl_valid = (fam == IPSEC_AF_V4 ? IPSL_IPV4 : IPSL_IPV6); 38882b24ab6bSSebastien Roy if ((pol = ipsec_policy_create(&sel, actp, nact, IPSEC_PRIO_SOCKET, 38892b24ab6bSSebastien Roy NULL, ns)) != NULL) { 38902b24ab6bSSebastien Roy pr = &ph->iph_root[ptype]; 38912b24ab6bSSebastien Roy HASHLIST_INSERT(pol, ipsp_hash, pr->ipr_nonhash[fam]); 38922b24ab6bSSebastien Roy ipsec_insert_always(&ph->iph_rulebyid, pol); 38932b24ab6bSSebastien Roy } 38942b24ab6bSSebastien Roy return (pol != NULL); 38952b24ab6bSSebastien Roy } 38962b24ab6bSSebastien Roy 38977c478bd9Sstevel@tonic-gate void 3898f4b3ec61Sdh155122 ipsec_polhead_flush(ipsec_policy_head_t *php, netstack_t *ns) 38997c478bd9Sstevel@tonic-gate { 39006a182920Ssommerfe int dir; 39017c478bd9Sstevel@tonic-gate 39027c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&php->iph_lock)); 39037c478bd9Sstevel@tonic-gate 39046a182920Ssommerfe for (dir = 0; dir < IPSEC_NTYPES; dir++) 3905bd670b35SErik Nordmark ipsec_ipr_flush(php, &php->iph_root[dir]); 39066a182920Ssommerfe 3907bd670b35SErik Nordmark php->iph_gen++; 3908f4b3ec61Sdh155122 ipsec_update_present_flags(ns->netstack_ipsec); 39097c478bd9Sstevel@tonic-gate } 39107c478bd9Sstevel@tonic-gate 39117c478bd9Sstevel@tonic-gate void 3912f4b3ec61Sdh155122 ipsec_polhead_free(ipsec_policy_head_t *php, netstack_t *ns) 39137c478bd9Sstevel@tonic-gate { 39148810c16bSdanmcd int dir; 39158810c16bSdanmcd 39167c478bd9Sstevel@tonic-gate ASSERT(php->iph_refs == 0); 3917f4b3ec61Sdh155122 39187c478bd9Sstevel@tonic-gate rw_enter(&php->iph_lock, RW_WRITER); 3919f4b3ec61Sdh155122 ipsec_polhead_flush(php, ns); 39207c478bd9Sstevel@tonic-gate rw_exit(&php->iph_lock); 39217c478bd9Sstevel@tonic-gate rw_destroy(&php->iph_lock); 39228810c16bSdanmcd for (dir = 0; dir < IPSEC_NTYPES; dir++) { 39238810c16bSdanmcd ipsec_policy_root_t *ipr = &php->iph_root[dir]; 39248810c16bSdanmcd int chain; 39258810c16bSdanmcd 39268810c16bSdanmcd for (chain = 0; chain < ipr->ipr_nchains; chain++) 39278810c16bSdanmcd mutex_destroy(&(ipr->ipr_hash[chain].hash_lock)); 39288810c16bSdanmcd 39298810c16bSdanmcd } 39308810c16bSdanmcd ipsec_polhead_free_table(php); 39317c478bd9Sstevel@tonic-gate kmem_free(php, sizeof (*php)); 39327c478bd9Sstevel@tonic-gate } 39337c478bd9Sstevel@tonic-gate 39346a182920Ssommerfe static void 39356a182920Ssommerfe ipsec_ipr_init(ipsec_policy_root_t *ipr) 39366a182920Ssommerfe { 39376a182920Ssommerfe int af; 39386a182920Ssommerfe 39396a182920Ssommerfe ipr->ipr_nchains = 0; 39406a182920Ssommerfe ipr->ipr_hash = NULL; 39416a182920Ssommerfe 39426a182920Ssommerfe for (af = 0; af < IPSEC_NAF; af++) { 39436a182920Ssommerfe ipr->ipr_nonhash[af] = NULL; 39446a182920Ssommerfe } 39456a182920Ssommerfe } 39466a182920Ssommerfe 39478810c16bSdanmcd ipsec_policy_head_t * 39487c478bd9Sstevel@tonic-gate ipsec_polhead_create(void) 39497c478bd9Sstevel@tonic-gate { 39507c478bd9Sstevel@tonic-gate ipsec_policy_head_t *php; 39517c478bd9Sstevel@tonic-gate 39527c478bd9Sstevel@tonic-gate php = kmem_alloc(sizeof (*php), KM_NOSLEEP); 39536a182920Ssommerfe if (php == NULL) 39546a182920Ssommerfe return (php); 39556a182920Ssommerfe 39567c478bd9Sstevel@tonic-gate rw_init(&php->iph_lock, NULL, RW_DEFAULT, NULL); 39577c478bd9Sstevel@tonic-gate php->iph_refs = 1; 39586a182920Ssommerfe php->iph_gen = 0; 39596a182920Ssommerfe 39606a182920Ssommerfe ipsec_ipr_init(&php->iph_root[IPSEC_TYPE_INBOUND]); 39616a182920Ssommerfe ipsec_ipr_init(&php->iph_root[IPSEC_TYPE_OUTBOUND]); 39626a182920Ssommerfe 39636a182920Ssommerfe avl_create(&php->iph_rulebyid, ipsec_policy_cmpbyid, 39646a182920Ssommerfe sizeof (ipsec_policy_t), offsetof(ipsec_policy_t, ipsp_byid)); 39656a182920Ssommerfe 39667c478bd9Sstevel@tonic-gate return (php); 39677c478bd9Sstevel@tonic-gate } 39687c478bd9Sstevel@tonic-gate 39697c478bd9Sstevel@tonic-gate /* 39707c478bd9Sstevel@tonic-gate * Clone the policy head into a new polhead; release one reference to the 39717c478bd9Sstevel@tonic-gate * old one and return the only reference to the new one. 39727c478bd9Sstevel@tonic-gate * If the old one had a refcount of 1, just return it. 39737c478bd9Sstevel@tonic-gate */ 39748810c16bSdanmcd ipsec_policy_head_t * 3975f4b3ec61Sdh155122 ipsec_polhead_split(ipsec_policy_head_t *php, netstack_t *ns) 39767c478bd9Sstevel@tonic-gate { 39777c478bd9Sstevel@tonic-gate ipsec_policy_head_t *nphp; 39787c478bd9Sstevel@tonic-gate 39797c478bd9Sstevel@tonic-gate if (php == NULL) 39807c478bd9Sstevel@tonic-gate return (ipsec_polhead_create()); 39817c478bd9Sstevel@tonic-gate else if (php->iph_refs == 1) 39827c478bd9Sstevel@tonic-gate return (php); 39837c478bd9Sstevel@tonic-gate 39847c478bd9Sstevel@tonic-gate nphp = ipsec_polhead_create(); 39857c478bd9Sstevel@tonic-gate if (nphp == NULL) 39867c478bd9Sstevel@tonic-gate return (NULL); 39877c478bd9Sstevel@tonic-gate 3988f4b3ec61Sdh155122 if (ipsec_copy_polhead(php, nphp, ns) != 0) { 3989f4b3ec61Sdh155122 ipsec_polhead_free(nphp, ns); 39907c478bd9Sstevel@tonic-gate return (NULL); 39917c478bd9Sstevel@tonic-gate } 3992f4b3ec61Sdh155122 IPPH_REFRELE(php, ns); 39937c478bd9Sstevel@tonic-gate return (nphp); 39947c478bd9Sstevel@tonic-gate } 39957c478bd9Sstevel@tonic-gate 39967c478bd9Sstevel@tonic-gate /* 39977c478bd9Sstevel@tonic-gate * When sending a response to a ICMP request or generating a RST 39987c478bd9Sstevel@tonic-gate * in the TCP case, the outbound packets need to go at the same level 39997c478bd9Sstevel@tonic-gate * of protection as the incoming ones i.e we associate our outbound 40007c478bd9Sstevel@tonic-gate * policy with how the packet came in. We call this after we have 40017c478bd9Sstevel@tonic-gate * accepted the incoming packet which may or may not have been in 40027c478bd9Sstevel@tonic-gate * clear and hence we are sending the reply back with the policy 40037c478bd9Sstevel@tonic-gate * matching the incoming datagram's policy. 40047c478bd9Sstevel@tonic-gate * 40057c478bd9Sstevel@tonic-gate * NOTE : This technology serves two purposes : 40067c478bd9Sstevel@tonic-gate * 40077c478bd9Sstevel@tonic-gate * 1) If we have multiple outbound policies, we send out a reply 40087c478bd9Sstevel@tonic-gate * matching with how it came in rather than matching the outbound 40097c478bd9Sstevel@tonic-gate * policy. 40107c478bd9Sstevel@tonic-gate * 40117c478bd9Sstevel@tonic-gate * 2) For assymetric policies, we want to make sure that incoming 40127c478bd9Sstevel@tonic-gate * and outgoing has the same level of protection. Assymetric 40137c478bd9Sstevel@tonic-gate * policies exist only with global policy where we may not have 40147c478bd9Sstevel@tonic-gate * both outbound and inbound at the same time. 40157c478bd9Sstevel@tonic-gate * 40167c478bd9Sstevel@tonic-gate * NOTE2: This function is called by cleartext cases, so it needs to be 40177c478bd9Sstevel@tonic-gate * in IP proper. 4018bd670b35SErik Nordmark * 4019bd670b35SErik Nordmark * Note: the caller has moved other parts of ira into ixa already. 40207c478bd9Sstevel@tonic-gate */ 40217c478bd9Sstevel@tonic-gate boolean_t 4022bd670b35SErik Nordmark ipsec_in_to_out(ip_recv_attr_t *ira, ip_xmit_attr_t *ixa, mblk_t *data_mp, 4023bd670b35SErik Nordmark ipha_t *ipha, ip6_t *ip6h) 40247c478bd9Sstevel@tonic-gate { 40257c478bd9Sstevel@tonic-gate ipsec_selector_t sel; 40267c478bd9Sstevel@tonic-gate ipsec_action_t *reflect_action = NULL; 4027bd670b35SErik Nordmark netstack_t *ns = ixa->ixa_ipst->ips_netstack; 40287c478bd9Sstevel@tonic-gate 40297c478bd9Sstevel@tonic-gate bzero((void*)&sel, sizeof (sel)); 40307c478bd9Sstevel@tonic-gate 4031bd670b35SErik Nordmark if (ira->ira_ipsec_action != NULL) { 40327c478bd9Sstevel@tonic-gate /* transfer reference.. */ 4033bd670b35SErik Nordmark reflect_action = ira->ira_ipsec_action; 4034bd670b35SErik Nordmark ira->ira_ipsec_action = NULL; 4035bd670b35SErik Nordmark } else if (!(ira->ira_flags & IRAF_LOOPBACK)) 4036bd670b35SErik Nordmark reflect_action = ipsec_in_to_out_action(ira); 40375d3b8cb7SBill Sommerfeld 40385d3b8cb7SBill Sommerfeld /* 40397c478bd9Sstevel@tonic-gate * The caller is going to send the datagram out which might 4040bd670b35SErik Nordmark * go on the wire or delivered locally through ire_send_local. 40417c478bd9Sstevel@tonic-gate * 40427c478bd9Sstevel@tonic-gate * 1) If it goes out on the wire, new associations will be 40437c478bd9Sstevel@tonic-gate * obtained. 4044bd670b35SErik Nordmark * 2) If it is delivered locally, ire_send_local will convert 4045bd670b35SErik Nordmark * this ip_xmit_attr_t back to a ip_recv_attr_t looking at the 4046bd670b35SErik Nordmark * requests. 40477c478bd9Sstevel@tonic-gate */ 4048bd670b35SErik Nordmark ixa->ixa_ipsec_action = reflect_action; 40497c478bd9Sstevel@tonic-gate 4050bd670b35SErik Nordmark if (!ipsec_init_outbound_ports(&sel, data_mp, ipha, ip6h, 0, 4051bd670b35SErik Nordmark ns->netstack_ipsec)) { 4052bd670b35SErik Nordmark /* Note: data_mp already consumed and ip_drop_packet done */ 40537c478bd9Sstevel@tonic-gate return (B_FALSE); 4054bd670b35SErik Nordmark } 4055bd670b35SErik Nordmark ixa->ixa_ipsec_src_port = sel.ips_local_port; 4056bd670b35SErik Nordmark ixa->ixa_ipsec_dst_port = sel.ips_remote_port; 4057bd670b35SErik Nordmark ixa->ixa_ipsec_proto = sel.ips_protocol; 4058bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; 4059bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; 40607c478bd9Sstevel@tonic-gate 40617c478bd9Sstevel@tonic-gate /* 40627c478bd9Sstevel@tonic-gate * Don't use global policy for this, as we want 40637c478bd9Sstevel@tonic-gate * to use the same protection that was applied to the inbound packet. 4064bd670b35SErik Nordmark * Thus we set IXAF_NO_IPSEC is it arrived in the clear to make 4065bd670b35SErik Nordmark * it be sent in the clear. 40667c478bd9Sstevel@tonic-gate */ 4067bd670b35SErik Nordmark if (ira->ira_flags & IRAF_IPSEC_SECURE) 4068bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_SECURE; 4069bd670b35SErik Nordmark else 4070bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_NO_IPSEC; 4071f4b3ec61Sdh155122 40727c478bd9Sstevel@tonic-gate return (B_TRUE); 40737c478bd9Sstevel@tonic-gate } 40747c478bd9Sstevel@tonic-gate 4075bd670b35SErik Nordmark void 4076bd670b35SErik Nordmark ipsec_out_release_refs(ip_xmit_attr_t *ixa) 40777c478bd9Sstevel@tonic-gate { 4078bd670b35SErik Nordmark if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) 4079bd670b35SErik Nordmark return; 40807c478bd9Sstevel@tonic-gate 4081bd670b35SErik Nordmark if (ixa->ixa_ipsec_ah_sa != NULL) { 4082bd670b35SErik Nordmark IPSA_REFRELE(ixa->ixa_ipsec_ah_sa); 4083bd670b35SErik Nordmark ixa->ixa_ipsec_ah_sa = NULL; 4084bd670b35SErik Nordmark } 4085bd670b35SErik Nordmark if (ixa->ixa_ipsec_esp_sa != NULL) { 4086bd670b35SErik Nordmark IPSA_REFRELE(ixa->ixa_ipsec_esp_sa); 4087bd670b35SErik Nordmark ixa->ixa_ipsec_esp_sa = NULL; 4088bd670b35SErik Nordmark } 4089bd670b35SErik Nordmark if (ixa->ixa_ipsec_policy != NULL) { 4090bd670b35SErik Nordmark IPPOL_REFRELE(ixa->ixa_ipsec_policy); 4091bd670b35SErik Nordmark ixa->ixa_ipsec_policy = NULL; 4092bd670b35SErik Nordmark } 4093bd670b35SErik Nordmark if (ixa->ixa_ipsec_action != NULL) { 4094bd670b35SErik Nordmark IPACT_REFRELE(ixa->ixa_ipsec_action); 4095bd670b35SErik Nordmark ixa->ixa_ipsec_action = NULL; 4096bd670b35SErik Nordmark } 4097bd670b35SErik Nordmark if (ixa->ixa_ipsec_latch) { 4098bd670b35SErik Nordmark IPLATCH_REFRELE(ixa->ixa_ipsec_latch); 4099bd670b35SErik Nordmark ixa->ixa_ipsec_latch = NULL; 4100bd670b35SErik Nordmark } 4101bd670b35SErik Nordmark /* Clear the soft references to the SAs */ 4102bd670b35SErik Nordmark ixa->ixa_ipsec_ref[0].ipsr_sa = NULL; 4103bd670b35SErik Nordmark ixa->ixa_ipsec_ref[0].ipsr_bucket = NULL; 4104bd670b35SErik Nordmark ixa->ixa_ipsec_ref[0].ipsr_gen = 0; 4105bd670b35SErik Nordmark ixa->ixa_ipsec_ref[1].ipsr_sa = NULL; 4106bd670b35SErik Nordmark ixa->ixa_ipsec_ref[1].ipsr_bucket = NULL; 4107bd670b35SErik Nordmark ixa->ixa_ipsec_ref[1].ipsr_gen = 0; 4108bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 4109f4b3ec61Sdh155122 } 41107c478bd9Sstevel@tonic-gate 4111bd670b35SErik Nordmark void 4112bd670b35SErik Nordmark ipsec_in_release_refs(ip_recv_attr_t *ira) 41137c478bd9Sstevel@tonic-gate { 4114bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) 4115bd670b35SErik Nordmark return; 41167c478bd9Sstevel@tonic-gate 4117bd670b35SErik Nordmark if (ira->ira_ipsec_ah_sa != NULL) { 4118bd670b35SErik Nordmark IPSA_REFRELE(ira->ira_ipsec_ah_sa); 4119bd670b35SErik Nordmark ira->ira_ipsec_ah_sa = NULL; 41207c478bd9Sstevel@tonic-gate } 4121bd670b35SErik Nordmark if (ira->ira_ipsec_esp_sa != NULL) { 4122bd670b35SErik Nordmark IPSA_REFRELE(ira->ira_ipsec_esp_sa); 4123bd670b35SErik Nordmark ira->ira_ipsec_esp_sa = NULL; 41247c478bd9Sstevel@tonic-gate } 4125bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_IPSEC_SECURE; 41267c478bd9Sstevel@tonic-gate } 41277c478bd9Sstevel@tonic-gate 41287c478bd9Sstevel@tonic-gate /* 4129bd670b35SErik Nordmark * This is called from ire_send_local when a packet 4130bd670b35SErik Nordmark * is looped back. We setup the ip_recv_attr_t "borrowing" the references 4131bd670b35SErik Nordmark * held by the callers. 4132bd670b35SErik Nordmark * Note that we don't do any IPsec but we carry the actions and IPSEC flags 4133bd670b35SErik Nordmark * across so that the fanout policy checks see that IPsec was applied. 41347c478bd9Sstevel@tonic-gate * 4135bd670b35SErik Nordmark * The caller should do ipsec_in_release_refs() on the ira by calling 4136bd670b35SErik Nordmark * ira_cleanup(). 41377c478bd9Sstevel@tonic-gate */ 41387c478bd9Sstevel@tonic-gate void 4139bd670b35SErik Nordmark ipsec_out_to_in(ip_xmit_attr_t *ixa, ill_t *ill, ip_recv_attr_t *ira) 41407c478bd9Sstevel@tonic-gate { 41417c478bd9Sstevel@tonic-gate ipsec_policy_t *pol; 41427c478bd9Sstevel@tonic-gate ipsec_action_t *act; 41437c478bd9Sstevel@tonic-gate 4144bd670b35SErik Nordmark /* Non-IPsec operations */ 4145bd670b35SErik Nordmark ira->ira_free_flags = 0; 4146bd670b35SErik Nordmark ira->ira_zoneid = ixa->ixa_zoneid; 4147bd670b35SErik Nordmark ira->ira_cred = ixa->ixa_cred; 4148bd670b35SErik Nordmark ira->ira_cpid = ixa->ixa_cpid; 4149bd670b35SErik Nordmark ira->ira_tsl = ixa->ixa_tsl; 4150bd670b35SErik Nordmark ira->ira_ill = ira->ira_rill = ill; 4151bd670b35SErik Nordmark ira->ira_flags = ixa->ixa_flags & IAF_MASK; 4152bd670b35SErik Nordmark ira->ira_no_loop_zoneid = ixa->ixa_no_loop_zoneid; 4153bd670b35SErik Nordmark ira->ira_pktlen = ixa->ixa_pktlen; 4154bd670b35SErik Nordmark ira->ira_ip_hdr_length = ixa->ixa_ip_hdr_length; 4155bd670b35SErik Nordmark ira->ira_protocol = ixa->ixa_protocol; 4156bd670b35SErik Nordmark ira->ira_mhip = NULL; 41577c478bd9Sstevel@tonic-gate 4158bd670b35SErik Nordmark ira->ira_flags |= IRAF_LOOPBACK | IRAF_L2SRC_LOOPBACK; 41597c478bd9Sstevel@tonic-gate 4160bd670b35SErik Nordmark ira->ira_sqp = ixa->ixa_sqp; 4161bd670b35SErik Nordmark ira->ira_ring = NULL; 41627c478bd9Sstevel@tonic-gate 4163bd670b35SErik Nordmark ira->ira_ruifindex = ill->ill_phyint->phyint_ifindex; 4164bd670b35SErik Nordmark ira->ira_rifindex = ira->ira_ruifindex; 4165bd670b35SErik Nordmark 4166bd670b35SErik Nordmark if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) 4167bd670b35SErik Nordmark return; 4168bd670b35SErik Nordmark 4169bd670b35SErik Nordmark ira->ira_flags |= IRAF_IPSEC_SECURE; 4170bd670b35SErik Nordmark 4171bd670b35SErik Nordmark ira->ira_ipsec_ah_sa = NULL; 4172bd670b35SErik Nordmark ira->ira_ipsec_esp_sa = NULL; 4173bd670b35SErik Nordmark 4174bd670b35SErik Nordmark act = ixa->ixa_ipsec_action; 41757c478bd9Sstevel@tonic-gate if (act == NULL) { 4176bd670b35SErik Nordmark pol = ixa->ixa_ipsec_policy; 41777c478bd9Sstevel@tonic-gate if (pol != NULL) { 41787c478bd9Sstevel@tonic-gate act = pol->ipsp_act; 41797c478bd9Sstevel@tonic-gate IPACT_REFHOLD(act); 41807c478bd9Sstevel@tonic-gate } 41817c478bd9Sstevel@tonic-gate } 4182bd670b35SErik Nordmark ixa->ixa_ipsec_action = NULL; 4183bd670b35SErik Nordmark ira->ira_ipsec_action = act; 41847c478bd9Sstevel@tonic-gate } 41857c478bd9Sstevel@tonic-gate 41867c478bd9Sstevel@tonic-gate /* 4187bd670b35SErik Nordmark * Consults global policy and per-socket policy to see whether this datagram 4188bd670b35SErik Nordmark * should go out secure. If so it updates the ip_xmit_attr_t 4189bd670b35SErik Nordmark * Should not be used when connecting, since then we want to latch the policy. 4190bd670b35SErik Nordmark * 4191bd670b35SErik Nordmark * If connp is NULL we just look at the global policy. 4192bd670b35SErik Nordmark * 4193bd670b35SErik Nordmark * Returns NULL if the packet was dropped, in which case the MIB has 4194bd670b35SErik Nordmark * been incremented and ip_drop_packet done. 41957c478bd9Sstevel@tonic-gate */ 41967c478bd9Sstevel@tonic-gate mblk_t * 4197bd670b35SErik Nordmark ip_output_attach_policy(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, 4198bd670b35SErik Nordmark const conn_t *connp, ip_xmit_attr_t *ixa) 41997c478bd9Sstevel@tonic-gate { 42007c478bd9Sstevel@tonic-gate ipsec_selector_t sel; 42017c478bd9Sstevel@tonic-gate boolean_t policy_present; 4202bd670b35SErik Nordmark ip_stack_t *ipst = ixa->ixa_ipst; 4203f4b3ec61Sdh155122 netstack_t *ns = ipst->ips_netstack; 4204f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 4205bd670b35SErik Nordmark ipsec_policy_t *p; 42067c478bd9Sstevel@tonic-gate 4207bd670b35SErik Nordmark ixa->ixa_ipsec_policy_gen = ipss->ipsec_system_policy.iph_gen; 42087c478bd9Sstevel@tonic-gate ASSERT((ipha != NULL && ip6h == NULL) || 42097c478bd9Sstevel@tonic-gate (ip6h != NULL && ipha == NULL)); 42107c478bd9Sstevel@tonic-gate 42117c478bd9Sstevel@tonic-gate if (ipha != NULL) 4212f4b3ec61Sdh155122 policy_present = ipss->ipsec_outbound_v4_policy_present; 42137c478bd9Sstevel@tonic-gate else 4214f4b3ec61Sdh155122 policy_present = ipss->ipsec_outbound_v6_policy_present; 42157c478bd9Sstevel@tonic-gate 4216bd670b35SErik Nordmark if (!policy_present && (connp == NULL || connp->conn_policy == NULL)) 4217bd670b35SErik Nordmark return (mp); 4218bd670b35SErik Nordmark 4219bd670b35SErik Nordmark bzero((void*)&sel, sizeof (sel)); 42207c478bd9Sstevel@tonic-gate 42217c478bd9Sstevel@tonic-gate if (ipha != NULL) { 4222bd670b35SErik Nordmark sel.ips_local_addr_v4 = ipha->ipha_src; 42237c478bd9Sstevel@tonic-gate sel.ips_remote_addr_v4 = ip_get_dst(ipha); 42247c478bd9Sstevel@tonic-gate sel.ips_isv4 = B_TRUE; 42257c478bd9Sstevel@tonic-gate } else { 42267c478bd9Sstevel@tonic-gate sel.ips_isv4 = B_FALSE; 42277c478bd9Sstevel@tonic-gate sel.ips_local_addr_v6 = ip6h->ip6_src; 4228bd670b35SErik Nordmark sel.ips_remote_addr_v6 = ip_get_dst_v6(ip6h, mp, NULL); 42297c478bd9Sstevel@tonic-gate } 4230bd670b35SErik Nordmark sel.ips_protocol = ixa->ixa_protocol; 42317c478bd9Sstevel@tonic-gate 4232f4b3ec61Sdh155122 if (!ipsec_init_outbound_ports(&sel, mp, ipha, ip6h, 0, ipss)) { 42337c478bd9Sstevel@tonic-gate if (ipha != NULL) { 4234f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 42357c478bd9Sstevel@tonic-gate } else { 4236f4b3ec61Sdh155122 BUMP_MIB(&ipst->ips_ip6_mib, ipIfStatsOutDiscards); 42377c478bd9Sstevel@tonic-gate } 4238bd670b35SErik Nordmark /* Note: mp already consumed and ip_drop_packet done */ 42397c478bd9Sstevel@tonic-gate return (NULL); 42407c478bd9Sstevel@tonic-gate } 42417c478bd9Sstevel@tonic-gate 4242bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_policy == NULL); 4243bd670b35SErik Nordmark p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); 4244bd670b35SErik Nordmark ixa->ixa_ipsec_policy = p; 4245bd670b35SErik Nordmark if (p != NULL) { 4246bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_SECURE; 4247bd670b35SErik Nordmark if (connp == NULL || connp->conn_policy == NULL) 4248bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_GLOBAL_POLICY; 4249bd670b35SErik Nordmark } else { 4250bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 42517c478bd9Sstevel@tonic-gate } 42527c478bd9Sstevel@tonic-gate 42537c478bd9Sstevel@tonic-gate /* 42547c478bd9Sstevel@tonic-gate * Copy the right port information. 42557c478bd9Sstevel@tonic-gate */ 4256bd670b35SErik Nordmark ixa->ixa_ipsec_src_port = sel.ips_local_port; 4257bd670b35SErik Nordmark ixa->ixa_ipsec_dst_port = sel.ips_remote_port; 4258bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; 4259bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; 4260bd670b35SErik Nordmark ixa->ixa_ipsec_proto = sel.ips_protocol; 4261bd670b35SErik Nordmark return (mp); 42627c478bd9Sstevel@tonic-gate } 42637c478bd9Sstevel@tonic-gate 42647c478bd9Sstevel@tonic-gate /* 42657c478bd9Sstevel@tonic-gate * When appropriate, this function caches inbound and outbound policy 4266bd670b35SErik Nordmark * for this connection. The outbound policy is stored in conn_ixa. 4267bd670b35SErik Nordmark * Note that it can not be used for SCTP since conn_faddr isn't set for SCTP. 42687c478bd9Sstevel@tonic-gate * 42697c478bd9Sstevel@tonic-gate * XXX need to work out more details about per-interface policy and 42707c478bd9Sstevel@tonic-gate * caching here! 42717c478bd9Sstevel@tonic-gate * 42727c478bd9Sstevel@tonic-gate * XXX may want to split inbound and outbound caching for ill.. 42737c478bd9Sstevel@tonic-gate */ 42747c478bd9Sstevel@tonic-gate int 42757c478bd9Sstevel@tonic-gate ipsec_conn_cache_policy(conn_t *connp, boolean_t isv4) 42767c478bd9Sstevel@tonic-gate { 42777c478bd9Sstevel@tonic-gate boolean_t global_policy_present; 4278f4b3ec61Sdh155122 netstack_t *ns = connp->conn_netstack; 4279f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 42807c478bd9Sstevel@tonic-gate 4281bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_policy_gen = 4282bd670b35SErik Nordmark ipss->ipsec_system_policy.iph_gen; 42837c478bd9Sstevel@tonic-gate /* 42847c478bd9Sstevel@tonic-gate * There is no policy latching for ICMP sockets because we can't 42857c478bd9Sstevel@tonic-gate * decide on which policy to use until we see the packet and get 42867c478bd9Sstevel@tonic-gate * type/code selectors. 42877c478bd9Sstevel@tonic-gate */ 4288bd670b35SErik Nordmark if (connp->conn_proto == IPPROTO_ICMP || 4289bd670b35SErik Nordmark connp->conn_proto == IPPROTO_ICMPV6) { 42907c478bd9Sstevel@tonic-gate connp->conn_in_enforce_policy = 42917c478bd9Sstevel@tonic-gate connp->conn_out_enforce_policy = B_TRUE; 42927c478bd9Sstevel@tonic-gate if (connp->conn_latch != NULL) { 4293bd670b35SErik Nordmark IPLATCH_REFRELE(connp->conn_latch); 42947c478bd9Sstevel@tonic-gate connp->conn_latch = NULL; 42957c478bd9Sstevel@tonic-gate } 4296bd670b35SErik Nordmark if (connp->conn_latch_in_policy != NULL) { 4297bd670b35SErik Nordmark IPPOL_REFRELE(connp->conn_latch_in_policy); 4298bd670b35SErik Nordmark connp->conn_latch_in_policy = NULL; 4299bd670b35SErik Nordmark } 4300bd670b35SErik Nordmark if (connp->conn_latch_in_action != NULL) { 4301bd670b35SErik Nordmark IPACT_REFRELE(connp->conn_latch_in_action); 4302bd670b35SErik Nordmark connp->conn_latch_in_action = NULL; 4303bd670b35SErik Nordmark } 4304bd670b35SErik Nordmark if (connp->conn_ixa->ixa_ipsec_policy != NULL) { 4305bd670b35SErik Nordmark IPPOL_REFRELE(connp->conn_ixa->ixa_ipsec_policy); 4306bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_policy = NULL; 4307bd670b35SErik Nordmark } 4308bd670b35SErik Nordmark if (connp->conn_ixa->ixa_ipsec_action != NULL) { 4309bd670b35SErik Nordmark IPACT_REFRELE(connp->conn_ixa->ixa_ipsec_action); 4310bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_action = NULL; 4311bd670b35SErik Nordmark } 4312bd670b35SErik Nordmark connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 43137c478bd9Sstevel@tonic-gate return (0); 43147c478bd9Sstevel@tonic-gate } 43157c478bd9Sstevel@tonic-gate 43167c478bd9Sstevel@tonic-gate global_policy_present = isv4 ? 4317f4b3ec61Sdh155122 (ipss->ipsec_outbound_v4_policy_present || 4318f4b3ec61Sdh155122 ipss->ipsec_inbound_v4_policy_present) : 4319f4b3ec61Sdh155122 (ipss->ipsec_outbound_v6_policy_present || 4320f4b3ec61Sdh155122 ipss->ipsec_inbound_v6_policy_present); 43217c478bd9Sstevel@tonic-gate 43227c478bd9Sstevel@tonic-gate if ((connp->conn_policy != NULL) || global_policy_present) { 43237c478bd9Sstevel@tonic-gate ipsec_selector_t sel; 43247c478bd9Sstevel@tonic-gate ipsec_policy_t *p; 43257c478bd9Sstevel@tonic-gate 43267c478bd9Sstevel@tonic-gate if (connp->conn_latch == NULL && 43277c478bd9Sstevel@tonic-gate (connp->conn_latch = iplatch_create()) == NULL) { 43287c478bd9Sstevel@tonic-gate return (ENOMEM); 43297c478bd9Sstevel@tonic-gate } 43307c478bd9Sstevel@tonic-gate 4331bd670b35SErik Nordmark bzero((void*)&sel, sizeof (sel)); 4332bd670b35SErik Nordmark 4333bd670b35SErik Nordmark sel.ips_protocol = connp->conn_proto; 43347c478bd9Sstevel@tonic-gate sel.ips_local_port = connp->conn_lport; 43357c478bd9Sstevel@tonic-gate sel.ips_remote_port = connp->conn_fport; 43367c478bd9Sstevel@tonic-gate sel.ips_is_icmp_inv_acq = 0; 43377c478bd9Sstevel@tonic-gate sel.ips_isv4 = isv4; 43387c478bd9Sstevel@tonic-gate if (isv4) { 4339bd670b35SErik Nordmark sel.ips_local_addr_v4 = connp->conn_laddr_v4; 4340bd670b35SErik Nordmark sel.ips_remote_addr_v4 = connp->conn_faddr_v4; 43417c478bd9Sstevel@tonic-gate } else { 4342bd670b35SErik Nordmark sel.ips_local_addr_v6 = connp->conn_laddr_v6; 4343bd670b35SErik Nordmark sel.ips_remote_addr_v6 = connp->conn_faddr_v6; 43447c478bd9Sstevel@tonic-gate } 43457c478bd9Sstevel@tonic-gate 4346bd670b35SErik Nordmark p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, &sel, ns); 4347bd670b35SErik Nordmark if (connp->conn_latch_in_policy != NULL) 4348bd670b35SErik Nordmark IPPOL_REFRELE(connp->conn_latch_in_policy); 4349bd670b35SErik Nordmark connp->conn_latch_in_policy = p; 43507c478bd9Sstevel@tonic-gate connp->conn_in_enforce_policy = (p != NULL); 43517c478bd9Sstevel@tonic-gate 4352bd670b35SErik Nordmark p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); 4353bd670b35SErik Nordmark if (connp->conn_ixa->ixa_ipsec_policy != NULL) 4354bd670b35SErik Nordmark IPPOL_REFRELE(connp->conn_ixa->ixa_ipsec_policy); 4355bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_policy = p; 43567c478bd9Sstevel@tonic-gate connp->conn_out_enforce_policy = (p != NULL); 4357bd670b35SErik Nordmark if (p != NULL) { 4358bd670b35SErik Nordmark connp->conn_ixa->ixa_flags |= IXAF_IPSEC_SECURE; 4359bd670b35SErik Nordmark if (connp->conn_policy == NULL) { 4360bd670b35SErik Nordmark connp->conn_ixa->ixa_flags |= 4361bd670b35SErik Nordmark IXAF_IPSEC_GLOBAL_POLICY; 4362bd670b35SErik Nordmark } 4363bd670b35SErik Nordmark } else { 4364bd670b35SErik Nordmark connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 4365bd670b35SErik Nordmark } 43667c478bd9Sstevel@tonic-gate /* Clear the latched actions too, in case we're recaching. */ 4367bd670b35SErik Nordmark if (connp->conn_ixa->ixa_ipsec_action != NULL) { 4368bd670b35SErik Nordmark IPACT_REFRELE(connp->conn_ixa->ixa_ipsec_action); 4369bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_action = NULL; 4370bd670b35SErik Nordmark } 4371bd670b35SErik Nordmark if (connp->conn_latch_in_action != NULL) { 4372bd670b35SErik Nordmark IPACT_REFRELE(connp->conn_latch_in_action); 4373bd670b35SErik Nordmark connp->conn_latch_in_action = NULL; 4374bd670b35SErik Nordmark } 4375bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_src_port = sel.ips_local_port; 4376bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_dst_port = sel.ips_remote_port; 4377bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; 4378bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; 4379bd670b35SErik Nordmark connp->conn_ixa->ixa_ipsec_proto = sel.ips_protocol; 4380bd670b35SErik Nordmark } else { 4381bd670b35SErik Nordmark connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 43827c478bd9Sstevel@tonic-gate } 43837c478bd9Sstevel@tonic-gate 43847c478bd9Sstevel@tonic-gate /* 43857c478bd9Sstevel@tonic-gate * We may or may not have policy for this endpoint. We still set 43867c478bd9Sstevel@tonic-gate * conn_policy_cached so that inbound datagrams don't have to look 43877c478bd9Sstevel@tonic-gate * at global policy as policy is considered latched for these 43887c478bd9Sstevel@tonic-gate * endpoints. We should not set conn_policy_cached until the conn 43897c478bd9Sstevel@tonic-gate * reflects the actual policy. If we *set* this before inheriting 43907c478bd9Sstevel@tonic-gate * the policy there is a window where the check 43917c478bd9Sstevel@tonic-gate * CONN_INBOUND_POLICY_PRESENT, will neither check with the policy 43927c478bd9Sstevel@tonic-gate * on the conn (because we have not yet copied the policy on to 43937c478bd9Sstevel@tonic-gate * conn and hence not set conn_in_enforce_policy) nor with the 43947c478bd9Sstevel@tonic-gate * global policy (because conn_policy_cached is already set). 43957c478bd9Sstevel@tonic-gate */ 43967c478bd9Sstevel@tonic-gate connp->conn_policy_cached = B_TRUE; 43977c478bd9Sstevel@tonic-gate return (0); 43987c478bd9Sstevel@tonic-gate } 43997c478bd9Sstevel@tonic-gate 4400bd670b35SErik Nordmark /* 4401bd670b35SErik Nordmark * When appropriate, this function caches outbound policy for faddr/fport. 4402bd670b35SErik Nordmark * It is used when we are not connected i.e., when we can not latch the 4403bd670b35SErik Nordmark * policy. 4404bd670b35SErik Nordmark */ 44057c478bd9Sstevel@tonic-gate void 4406bd670b35SErik Nordmark ipsec_cache_outbound_policy(const conn_t *connp, const in6_addr_t *v6src, 4407bd670b35SErik Nordmark const in6_addr_t *v6dst, in_port_t dstport, ip_xmit_attr_t *ixa) 44087c478bd9Sstevel@tonic-gate { 4409bd670b35SErik Nordmark boolean_t isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0; 4410bd670b35SErik Nordmark boolean_t global_policy_present; 4411bd670b35SErik Nordmark netstack_t *ns = connp->conn_netstack; 4412bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec; 4413bd670b35SErik Nordmark 4414bd670b35SErik Nordmark ixa->ixa_ipsec_policy_gen = ipss->ipsec_system_policy.iph_gen; 4415bd670b35SErik Nordmark 4416bd670b35SErik Nordmark /* 4417bd670b35SErik Nordmark * There is no policy caching for ICMP sockets because we can't 4418bd670b35SErik Nordmark * decide on which policy to use until we see the packet and get 4419bd670b35SErik Nordmark * type/code selectors. 4420bd670b35SErik Nordmark */ 4421bd670b35SErik Nordmark if (connp->conn_proto == IPPROTO_ICMP || 4422bd670b35SErik Nordmark connp->conn_proto == IPPROTO_ICMPV6) { 4423bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 4424bd670b35SErik Nordmark if (ixa->ixa_ipsec_policy != NULL) { 4425bd670b35SErik Nordmark IPPOL_REFRELE(ixa->ixa_ipsec_policy); 4426bd670b35SErik Nordmark ixa->ixa_ipsec_policy = NULL; 4427bd670b35SErik Nordmark } 4428bd670b35SErik Nordmark if (ixa->ixa_ipsec_action != NULL) { 4429bd670b35SErik Nordmark IPACT_REFRELE(ixa->ixa_ipsec_action); 4430bd670b35SErik Nordmark ixa->ixa_ipsec_action = NULL; 4431bd670b35SErik Nordmark } 4432bd670b35SErik Nordmark return; 4433bd670b35SErik Nordmark } 4434bd670b35SErik Nordmark 4435bd670b35SErik Nordmark global_policy_present = isv4 ? 4436bd670b35SErik Nordmark (ipss->ipsec_outbound_v4_policy_present || 4437bd670b35SErik Nordmark ipss->ipsec_inbound_v4_policy_present) : 4438bd670b35SErik Nordmark (ipss->ipsec_outbound_v6_policy_present || 4439bd670b35SErik Nordmark ipss->ipsec_inbound_v6_policy_present); 4440bd670b35SErik Nordmark 4441bd670b35SErik Nordmark if ((connp->conn_policy != NULL) || global_policy_present) { 4442bd670b35SErik Nordmark ipsec_selector_t sel; 4443bd670b35SErik Nordmark ipsec_policy_t *p; 4444bd670b35SErik Nordmark 4445bd670b35SErik Nordmark bzero((void*)&sel, sizeof (sel)); 4446bd670b35SErik Nordmark 4447bd670b35SErik Nordmark sel.ips_protocol = connp->conn_proto; 4448bd670b35SErik Nordmark sel.ips_local_port = connp->conn_lport; 4449bd670b35SErik Nordmark sel.ips_remote_port = dstport; 4450bd670b35SErik Nordmark sel.ips_is_icmp_inv_acq = 0; 4451bd670b35SErik Nordmark sel.ips_isv4 = isv4; 4452bd670b35SErik Nordmark if (isv4) { 4453bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(v6src, sel.ips_local_addr_v4); 4454bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(v6dst, sel.ips_remote_addr_v4); 4455bd670b35SErik Nordmark } else { 4456bd670b35SErik Nordmark sel.ips_local_addr_v6 = *v6src; 4457bd670b35SErik Nordmark sel.ips_remote_addr_v6 = *v6dst; 4458bd670b35SErik Nordmark } 4459bd670b35SErik Nordmark 4460bd670b35SErik Nordmark p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); 4461bd670b35SErik Nordmark if (ixa->ixa_ipsec_policy != NULL) 4462bd670b35SErik Nordmark IPPOL_REFRELE(ixa->ixa_ipsec_policy); 4463bd670b35SErik Nordmark ixa->ixa_ipsec_policy = p; 4464bd670b35SErik Nordmark if (p != NULL) { 4465bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_SECURE; 4466bd670b35SErik Nordmark if (connp->conn_policy == NULL) 4467bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_GLOBAL_POLICY; 4468bd670b35SErik Nordmark } else { 4469bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 4470bd670b35SErik Nordmark } 4471bd670b35SErik Nordmark /* Clear the latched actions too, in case we're recaching. */ 4472bd670b35SErik Nordmark if (ixa->ixa_ipsec_action != NULL) { 4473bd670b35SErik Nordmark IPACT_REFRELE(ixa->ixa_ipsec_action); 4474bd670b35SErik Nordmark ixa->ixa_ipsec_action = NULL; 4475bd670b35SErik Nordmark } 4476bd670b35SErik Nordmark 4477bd670b35SErik Nordmark ixa->ixa_ipsec_src_port = sel.ips_local_port; 4478bd670b35SErik Nordmark ixa->ixa_ipsec_dst_port = sel.ips_remote_port; 4479bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; 4480bd670b35SErik Nordmark ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; 4481bd670b35SErik Nordmark ixa->ixa_ipsec_proto = sel.ips_protocol; 4482bd670b35SErik Nordmark } else { 4483bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; 4484bd670b35SErik Nordmark if (ixa->ixa_ipsec_policy != NULL) { 4485bd670b35SErik Nordmark IPPOL_REFRELE(ixa->ixa_ipsec_policy); 4486bd670b35SErik Nordmark ixa->ixa_ipsec_policy = NULL; 4487bd670b35SErik Nordmark } 4488bd670b35SErik Nordmark if (ixa->ixa_ipsec_action != NULL) { 4489bd670b35SErik Nordmark IPACT_REFRELE(ixa->ixa_ipsec_action); 4490bd670b35SErik Nordmark ixa->ixa_ipsec_action = NULL; 4491bd670b35SErik Nordmark } 4492bd670b35SErik Nordmark } 4493bd670b35SErik Nordmark } 4494bd670b35SErik Nordmark 4495bd670b35SErik Nordmark /* 4496bd670b35SErik Nordmark * Returns B_FALSE if the policy has gone stale. 4497bd670b35SErik Nordmark */ 4498bd670b35SErik Nordmark boolean_t 4499bd670b35SErik Nordmark ipsec_outbound_policy_current(ip_xmit_attr_t *ixa) 4500bd670b35SErik Nordmark { 4501bd670b35SErik Nordmark ipsec_stack_t *ipss = ixa->ixa_ipst->ips_netstack->netstack_ipsec; 4502bd670b35SErik Nordmark 4503bd670b35SErik Nordmark if (!(ixa->ixa_flags & IXAF_IPSEC_GLOBAL_POLICY)) 4504bd670b35SErik Nordmark return (B_TRUE); 4505bd670b35SErik Nordmark 4506bd670b35SErik Nordmark return (ixa->ixa_ipsec_policy_gen == ipss->ipsec_system_policy.iph_gen); 4507bd670b35SErik Nordmark } 4508bd670b35SErik Nordmark 4509bd670b35SErik Nordmark void 4510bd670b35SErik Nordmark iplatch_free(ipsec_latch_t *ipl) 4511bd670b35SErik Nordmark { 45127c478bd9Sstevel@tonic-gate if (ipl->ipl_local_cid != NULL) 45137c478bd9Sstevel@tonic-gate IPSID_REFRELE(ipl->ipl_local_cid); 45147c478bd9Sstevel@tonic-gate if (ipl->ipl_remote_cid != NULL) 45157c478bd9Sstevel@tonic-gate IPSID_REFRELE(ipl->ipl_remote_cid); 45167c478bd9Sstevel@tonic-gate mutex_destroy(&ipl->ipl_lock); 45177c478bd9Sstevel@tonic-gate kmem_free(ipl, sizeof (*ipl)); 45187c478bd9Sstevel@tonic-gate } 45197c478bd9Sstevel@tonic-gate 45207c478bd9Sstevel@tonic-gate ipsec_latch_t * 45217c478bd9Sstevel@tonic-gate iplatch_create() 45227c478bd9Sstevel@tonic-gate { 4523*a0f9c00cSJosef 'Jeff' Sipek ipsec_latch_t *ipl = kmem_zalloc(sizeof (*ipl), KM_NOSLEEP); 45247c478bd9Sstevel@tonic-gate if (ipl == NULL) 45257c478bd9Sstevel@tonic-gate return (ipl); 45267c478bd9Sstevel@tonic-gate mutex_init(&ipl->ipl_lock, NULL, MUTEX_DEFAULT, NULL); 45277c478bd9Sstevel@tonic-gate ipl->ipl_refcnt = 1; 45287c478bd9Sstevel@tonic-gate return (ipl); 45297c478bd9Sstevel@tonic-gate } 45307c478bd9Sstevel@tonic-gate 45317c478bd9Sstevel@tonic-gate /* 45327c478bd9Sstevel@tonic-gate * Hash function for ID hash table. 45337c478bd9Sstevel@tonic-gate */ 45347c478bd9Sstevel@tonic-gate static uint32_t 45357c478bd9Sstevel@tonic-gate ipsid_hash(int idtype, char *idstring) 45367c478bd9Sstevel@tonic-gate { 45377c478bd9Sstevel@tonic-gate uint32_t hval = idtype; 45387c478bd9Sstevel@tonic-gate unsigned char c; 45397c478bd9Sstevel@tonic-gate 45407c478bd9Sstevel@tonic-gate while ((c = *idstring++) != 0) { 45417c478bd9Sstevel@tonic-gate hval = (hval << 4) | (hval >> 28); 45427c478bd9Sstevel@tonic-gate hval ^= c; 45437c478bd9Sstevel@tonic-gate } 45447c478bd9Sstevel@tonic-gate hval = hval ^ (hval >> 16); 45457c478bd9Sstevel@tonic-gate return (hval & (IPSID_HASHSIZE-1)); 45467c478bd9Sstevel@tonic-gate } 45477c478bd9Sstevel@tonic-gate 45487c478bd9Sstevel@tonic-gate /* 45497c478bd9Sstevel@tonic-gate * Look up identity string in hash table. Return identity object 45507c478bd9Sstevel@tonic-gate * corresponding to the name -- either preexisting, or newly allocated. 45517c478bd9Sstevel@tonic-gate * 45527c478bd9Sstevel@tonic-gate * Return NULL if we need to allocate a new one and can't get memory. 45537c478bd9Sstevel@tonic-gate */ 45547c478bd9Sstevel@tonic-gate ipsid_t * 4555f4b3ec61Sdh155122 ipsid_lookup(int idtype, char *idstring, netstack_t *ns) 45567c478bd9Sstevel@tonic-gate { 45577c478bd9Sstevel@tonic-gate ipsid_t *retval; 45587c478bd9Sstevel@tonic-gate char *nstr; 45597c478bd9Sstevel@tonic-gate int idlen = strlen(idstring) + 1; 4560f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 4561f4b3ec61Sdh155122 ipsif_t *bucket; 45627c478bd9Sstevel@tonic-gate 4563f4b3ec61Sdh155122 bucket = &ipss->ipsec_ipsid_buckets[ipsid_hash(idtype, idstring)]; 45647c478bd9Sstevel@tonic-gate 45657c478bd9Sstevel@tonic-gate mutex_enter(&bucket->ipsif_lock); 45667c478bd9Sstevel@tonic-gate 45677c478bd9Sstevel@tonic-gate for (retval = bucket->ipsif_head; retval != NULL; 45687c478bd9Sstevel@tonic-gate retval = retval->ipsid_next) { 45697c478bd9Sstevel@tonic-gate if (idtype != retval->ipsid_type) 45707c478bd9Sstevel@tonic-gate continue; 45717c478bd9Sstevel@tonic-gate if (bcmp(idstring, retval->ipsid_cid, idlen) != 0) 45727c478bd9Sstevel@tonic-gate continue; 45737c478bd9Sstevel@tonic-gate 45747c478bd9Sstevel@tonic-gate IPSID_REFHOLD(retval); 45757c478bd9Sstevel@tonic-gate mutex_exit(&bucket->ipsif_lock); 45767c478bd9Sstevel@tonic-gate return (retval); 45777c478bd9Sstevel@tonic-gate } 45787c478bd9Sstevel@tonic-gate 45797c478bd9Sstevel@tonic-gate retval = kmem_alloc(sizeof (*retval), KM_NOSLEEP); 45807c478bd9Sstevel@tonic-gate if (!retval) { 45817c478bd9Sstevel@tonic-gate mutex_exit(&bucket->ipsif_lock); 45827c478bd9Sstevel@tonic-gate return (NULL); 45837c478bd9Sstevel@tonic-gate } 45847c478bd9Sstevel@tonic-gate 45857c478bd9Sstevel@tonic-gate nstr = kmem_alloc(idlen, KM_NOSLEEP); 45867c478bd9Sstevel@tonic-gate if (!nstr) { 45877c478bd9Sstevel@tonic-gate mutex_exit(&bucket->ipsif_lock); 45887c478bd9Sstevel@tonic-gate kmem_free(retval, sizeof (*retval)); 45897c478bd9Sstevel@tonic-gate return (NULL); 45907c478bd9Sstevel@tonic-gate } 45917c478bd9Sstevel@tonic-gate 45927c478bd9Sstevel@tonic-gate retval->ipsid_refcnt = 1; 45937c478bd9Sstevel@tonic-gate retval->ipsid_next = bucket->ipsif_head; 45947c478bd9Sstevel@tonic-gate if (retval->ipsid_next != NULL) 45957c478bd9Sstevel@tonic-gate retval->ipsid_next->ipsid_ptpn = &retval->ipsid_next; 45967c478bd9Sstevel@tonic-gate retval->ipsid_ptpn = &bucket->ipsif_head; 45977c478bd9Sstevel@tonic-gate retval->ipsid_type = idtype; 45987c478bd9Sstevel@tonic-gate retval->ipsid_cid = nstr; 45997c478bd9Sstevel@tonic-gate bucket->ipsif_head = retval; 46007c478bd9Sstevel@tonic-gate bcopy(idstring, nstr, idlen); 46017c478bd9Sstevel@tonic-gate mutex_exit(&bucket->ipsif_lock); 46027c478bd9Sstevel@tonic-gate 46037c478bd9Sstevel@tonic-gate return (retval); 46047c478bd9Sstevel@tonic-gate } 46057c478bd9Sstevel@tonic-gate 46067c478bd9Sstevel@tonic-gate /* 46077c478bd9Sstevel@tonic-gate * Garbage collect the identity hash table. 46087c478bd9Sstevel@tonic-gate */ 46097c478bd9Sstevel@tonic-gate void 4610f4b3ec61Sdh155122 ipsid_gc(netstack_t *ns) 46117c478bd9Sstevel@tonic-gate { 46127c478bd9Sstevel@tonic-gate int i, len; 46137c478bd9Sstevel@tonic-gate ipsid_t *id, *nid; 46147c478bd9Sstevel@tonic-gate ipsif_t *bucket; 4615f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 46167c478bd9Sstevel@tonic-gate 46177c478bd9Sstevel@tonic-gate for (i = 0; i < IPSID_HASHSIZE; i++) { 4618f4b3ec61Sdh155122 bucket = &ipss->ipsec_ipsid_buckets[i]; 46197c478bd9Sstevel@tonic-gate mutex_enter(&bucket->ipsif_lock); 46207c478bd9Sstevel@tonic-gate for (id = bucket->ipsif_head; id != NULL; id = nid) { 46217c478bd9Sstevel@tonic-gate nid = id->ipsid_next; 46227c478bd9Sstevel@tonic-gate if (id->ipsid_refcnt == 0) { 46237c478bd9Sstevel@tonic-gate *id->ipsid_ptpn = nid; 46247c478bd9Sstevel@tonic-gate if (nid != NULL) 46257c478bd9Sstevel@tonic-gate nid->ipsid_ptpn = id->ipsid_ptpn; 46267c478bd9Sstevel@tonic-gate len = strlen(id->ipsid_cid) + 1; 46277c478bd9Sstevel@tonic-gate kmem_free(id->ipsid_cid, len); 46287c478bd9Sstevel@tonic-gate kmem_free(id, sizeof (*id)); 46297c478bd9Sstevel@tonic-gate } 46307c478bd9Sstevel@tonic-gate } 46317c478bd9Sstevel@tonic-gate mutex_exit(&bucket->ipsif_lock); 46327c478bd9Sstevel@tonic-gate } 46337c478bd9Sstevel@tonic-gate } 46347c478bd9Sstevel@tonic-gate 46357c478bd9Sstevel@tonic-gate /* 46367c478bd9Sstevel@tonic-gate * Return true if two identities are the same. 46377c478bd9Sstevel@tonic-gate */ 46387c478bd9Sstevel@tonic-gate boolean_t 46397c478bd9Sstevel@tonic-gate ipsid_equal(ipsid_t *id1, ipsid_t *id2) 46407c478bd9Sstevel@tonic-gate { 46417c478bd9Sstevel@tonic-gate if (id1 == id2) 46427c478bd9Sstevel@tonic-gate return (B_TRUE); 46437c478bd9Sstevel@tonic-gate #ifdef DEBUG 46447c478bd9Sstevel@tonic-gate if ((id1 == NULL) || (id2 == NULL)) 46457c478bd9Sstevel@tonic-gate return (B_FALSE); 46467c478bd9Sstevel@tonic-gate /* 46477c478bd9Sstevel@tonic-gate * test that we're interning id's correctly.. 46487c478bd9Sstevel@tonic-gate */ 46497c478bd9Sstevel@tonic-gate ASSERT((strcmp(id1->ipsid_cid, id2->ipsid_cid) != 0) || 46507c478bd9Sstevel@tonic-gate (id1->ipsid_type != id2->ipsid_type)); 46517c478bd9Sstevel@tonic-gate #endif 46527c478bd9Sstevel@tonic-gate return (B_FALSE); 46537c478bd9Sstevel@tonic-gate } 46547c478bd9Sstevel@tonic-gate 46557c478bd9Sstevel@tonic-gate /* 46567c478bd9Sstevel@tonic-gate * Initialize identity table; called during module initialization. 46577c478bd9Sstevel@tonic-gate */ 46587c478bd9Sstevel@tonic-gate static void 4659f4b3ec61Sdh155122 ipsid_init(netstack_t *ns) 46607c478bd9Sstevel@tonic-gate { 46617c478bd9Sstevel@tonic-gate ipsif_t *bucket; 46627c478bd9Sstevel@tonic-gate int i; 4663f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 46647c478bd9Sstevel@tonic-gate 46657c478bd9Sstevel@tonic-gate for (i = 0; i < IPSID_HASHSIZE; i++) { 4666f4b3ec61Sdh155122 bucket = &ipss->ipsec_ipsid_buckets[i]; 46677c478bd9Sstevel@tonic-gate mutex_init(&bucket->ipsif_lock, NULL, MUTEX_DEFAULT, NULL); 46687c478bd9Sstevel@tonic-gate } 46697c478bd9Sstevel@tonic-gate } 46707c478bd9Sstevel@tonic-gate 46717c478bd9Sstevel@tonic-gate /* 46727c478bd9Sstevel@tonic-gate * Free identity table (preparatory to module unload) 46737c478bd9Sstevel@tonic-gate */ 46747c478bd9Sstevel@tonic-gate static void 4675f4b3ec61Sdh155122 ipsid_fini(netstack_t *ns) 46767c478bd9Sstevel@tonic-gate { 46777c478bd9Sstevel@tonic-gate ipsif_t *bucket; 46787c478bd9Sstevel@tonic-gate int i; 4679f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 46807c478bd9Sstevel@tonic-gate 46817c478bd9Sstevel@tonic-gate for (i = 0; i < IPSID_HASHSIZE; i++) { 4682f4b3ec61Sdh155122 bucket = &ipss->ipsec_ipsid_buckets[i]; 4683f4b3ec61Sdh155122 ASSERT(bucket->ipsif_head == NULL); 46847c478bd9Sstevel@tonic-gate mutex_destroy(&bucket->ipsif_lock); 46857c478bd9Sstevel@tonic-gate } 46867c478bd9Sstevel@tonic-gate } 46877c478bd9Sstevel@tonic-gate 46887c478bd9Sstevel@tonic-gate /* 46897c478bd9Sstevel@tonic-gate * Update the minimum and maximum supported key sizes for the 46907c478bd9Sstevel@tonic-gate * specified algorithm. Must be called while holding the algorithms lock. 46917c478bd9Sstevel@tonic-gate */ 46927c478bd9Sstevel@tonic-gate void 4693f4b3ec61Sdh155122 ipsec_alg_fix_min_max(ipsec_alginfo_t *alg, ipsec_algtype_t alg_type, 4694f4b3ec61Sdh155122 netstack_t *ns) 46957c478bd9Sstevel@tonic-gate { 46967c478bd9Sstevel@tonic-gate size_t crypto_min = (size_t)-1, crypto_max = 0; 46977c478bd9Sstevel@tonic-gate size_t cur_crypto_min, cur_crypto_max; 46987c478bd9Sstevel@tonic-gate boolean_t is_valid; 46997c478bd9Sstevel@tonic-gate crypto_mechanism_info_t *mech_infos; 47007c478bd9Sstevel@tonic-gate uint_t nmech_infos; 47017c478bd9Sstevel@tonic-gate int crypto_rc, i; 47027c478bd9Sstevel@tonic-gate crypto_mech_usage_t mask; 4703f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 47047c478bd9Sstevel@tonic-gate 4705f4b3ec61Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 47067c478bd9Sstevel@tonic-gate 47077c478bd9Sstevel@tonic-gate /* 47087c478bd9Sstevel@tonic-gate * Compute the min, max, and default key sizes (in number of 47097c478bd9Sstevel@tonic-gate * increments to the default key size in bits) as defined 47107c478bd9Sstevel@tonic-gate * by the algorithm mappings. This range of key sizes is used 47117c478bd9Sstevel@tonic-gate * for policy related operations. The effective key sizes 47127c478bd9Sstevel@tonic-gate * supported by the framework could be more limited than 47137c478bd9Sstevel@tonic-gate * those defined for an algorithm. 47147c478bd9Sstevel@tonic-gate */ 47157c478bd9Sstevel@tonic-gate alg->alg_default_bits = alg->alg_key_sizes[0]; 4716628b0c67SMark Fenwick alg->alg_default = 0; 47177c478bd9Sstevel@tonic-gate if (alg->alg_increment != 0) { 47187c478bd9Sstevel@tonic-gate /* key sizes are defined by range & increment */ 47197c478bd9Sstevel@tonic-gate alg->alg_minbits = alg->alg_key_sizes[1]; 47207c478bd9Sstevel@tonic-gate alg->alg_maxbits = alg->alg_key_sizes[2]; 47217c478bd9Sstevel@tonic-gate } else if (alg->alg_nkey_sizes == 0) { 47227c478bd9Sstevel@tonic-gate /* no specified key size for algorithm */ 47237c478bd9Sstevel@tonic-gate alg->alg_minbits = alg->alg_maxbits = 0; 47247c478bd9Sstevel@tonic-gate } else { 47257c478bd9Sstevel@tonic-gate /* key sizes are defined by enumeration */ 47267c478bd9Sstevel@tonic-gate alg->alg_minbits = (uint16_t)-1; 47277c478bd9Sstevel@tonic-gate alg->alg_maxbits = 0; 47287c478bd9Sstevel@tonic-gate 47297c478bd9Sstevel@tonic-gate for (i = 0; i < alg->alg_nkey_sizes; i++) { 47307c478bd9Sstevel@tonic-gate if (alg->alg_key_sizes[i] < alg->alg_minbits) 47317c478bd9Sstevel@tonic-gate alg->alg_minbits = alg->alg_key_sizes[i]; 47327c478bd9Sstevel@tonic-gate if (alg->alg_key_sizes[i] > alg->alg_maxbits) 47337c478bd9Sstevel@tonic-gate alg->alg_maxbits = alg->alg_key_sizes[i]; 47347c478bd9Sstevel@tonic-gate } 47357c478bd9Sstevel@tonic-gate } 47367c478bd9Sstevel@tonic-gate 47377c478bd9Sstevel@tonic-gate if (!(alg->alg_flags & ALG_FLAG_VALID)) 47387c478bd9Sstevel@tonic-gate return; 47397c478bd9Sstevel@tonic-gate 47407c478bd9Sstevel@tonic-gate /* 47417c478bd9Sstevel@tonic-gate * Mechanisms do not apply to the NULL encryption 47427c478bd9Sstevel@tonic-gate * algorithm, so simply return for this case. 47437c478bd9Sstevel@tonic-gate */ 47447c478bd9Sstevel@tonic-gate if (alg->alg_id == SADB_EALG_NULL) 47457c478bd9Sstevel@tonic-gate return; 47467c478bd9Sstevel@tonic-gate 47477c478bd9Sstevel@tonic-gate /* 47487c478bd9Sstevel@tonic-gate * Find the min and max key sizes supported by the cryptographic 47497c478bd9Sstevel@tonic-gate * framework providers. 47507c478bd9Sstevel@tonic-gate */ 47517c478bd9Sstevel@tonic-gate 47527c478bd9Sstevel@tonic-gate /* get the key sizes supported by the framework */ 47537c478bd9Sstevel@tonic-gate crypto_rc = crypto_get_all_mech_info(alg->alg_mech_type, 47547c478bd9Sstevel@tonic-gate &mech_infos, &nmech_infos, KM_SLEEP); 47557c478bd9Sstevel@tonic-gate if (crypto_rc != CRYPTO_SUCCESS || nmech_infos == 0) { 47567c478bd9Sstevel@tonic-gate alg->alg_flags &= ~ALG_FLAG_VALID; 47577c478bd9Sstevel@tonic-gate return; 47587c478bd9Sstevel@tonic-gate } 47597c478bd9Sstevel@tonic-gate 47607c478bd9Sstevel@tonic-gate /* min and max key sizes supported by framework */ 47617c478bd9Sstevel@tonic-gate for (i = 0, is_valid = B_FALSE; i < nmech_infos; i++) { 47627c478bd9Sstevel@tonic-gate int unit_bits; 47637c478bd9Sstevel@tonic-gate 47647c478bd9Sstevel@tonic-gate /* 47657c478bd9Sstevel@tonic-gate * Ignore entries that do not support the operations 47667c478bd9Sstevel@tonic-gate * needed for the algorithm type. 47677c478bd9Sstevel@tonic-gate */ 4768daa41a61Sdanmcd if (alg_type == IPSEC_ALG_AUTH) { 47697c478bd9Sstevel@tonic-gate mask = CRYPTO_MECH_USAGE_MAC; 4770daa41a61Sdanmcd } else { 47717c478bd9Sstevel@tonic-gate mask = CRYPTO_MECH_USAGE_ENCRYPT | 47727c478bd9Sstevel@tonic-gate CRYPTO_MECH_USAGE_DECRYPT; 4773daa41a61Sdanmcd } 47747c478bd9Sstevel@tonic-gate if ((mech_infos[i].mi_usage & mask) != mask) 47757c478bd9Sstevel@tonic-gate continue; 47767c478bd9Sstevel@tonic-gate 47777c478bd9Sstevel@tonic-gate unit_bits = (mech_infos[i].mi_keysize_unit == 47787c478bd9Sstevel@tonic-gate CRYPTO_KEYSIZE_UNIT_IN_BYTES) ? 8 : 1; 47797c478bd9Sstevel@tonic-gate /* adjust min/max supported by framework */ 47807c478bd9Sstevel@tonic-gate cur_crypto_min = mech_infos[i].mi_min_key_size * unit_bits; 47817c478bd9Sstevel@tonic-gate cur_crypto_max = mech_infos[i].mi_max_key_size * unit_bits; 47827c478bd9Sstevel@tonic-gate 47837c478bd9Sstevel@tonic-gate if (cur_crypto_min < crypto_min) 47847c478bd9Sstevel@tonic-gate crypto_min = cur_crypto_min; 47857c478bd9Sstevel@tonic-gate 47867c478bd9Sstevel@tonic-gate /* 47877c478bd9Sstevel@tonic-gate * CRYPTO_EFFECTIVELY_INFINITE is a special value of 47887c478bd9Sstevel@tonic-gate * the crypto framework which means "no upper limit". 47897c478bd9Sstevel@tonic-gate */ 47907c478bd9Sstevel@tonic-gate if (mech_infos[i].mi_max_key_size == 4791daa41a61Sdanmcd CRYPTO_EFFECTIVELY_INFINITE) { 47927c478bd9Sstevel@tonic-gate crypto_max = (size_t)-1; 4793daa41a61Sdanmcd } else if (cur_crypto_max > crypto_max) { 47947c478bd9Sstevel@tonic-gate crypto_max = cur_crypto_max; 4795daa41a61Sdanmcd } 47967c478bd9Sstevel@tonic-gate 47977c478bd9Sstevel@tonic-gate is_valid = B_TRUE; 47987c478bd9Sstevel@tonic-gate } 47997c478bd9Sstevel@tonic-gate 48007c478bd9Sstevel@tonic-gate kmem_free(mech_infos, sizeof (crypto_mechanism_info_t) * 48017c478bd9Sstevel@tonic-gate nmech_infos); 48027c478bd9Sstevel@tonic-gate 48037c478bd9Sstevel@tonic-gate if (!is_valid) { 48047c478bd9Sstevel@tonic-gate /* no key sizes supported by framework */ 48057c478bd9Sstevel@tonic-gate alg->alg_flags &= ~ALG_FLAG_VALID; 48067c478bd9Sstevel@tonic-gate return; 48077c478bd9Sstevel@tonic-gate } 48087c478bd9Sstevel@tonic-gate 48097c478bd9Sstevel@tonic-gate /* 48107c478bd9Sstevel@tonic-gate * Determine min and max key sizes from alg_key_sizes[]. 48117c478bd9Sstevel@tonic-gate * defined for the algorithm entry. Adjust key sizes based on 48127c478bd9Sstevel@tonic-gate * those supported by the framework. 48137c478bd9Sstevel@tonic-gate */ 48147c478bd9Sstevel@tonic-gate alg->alg_ef_default_bits = alg->alg_key_sizes[0]; 4815628b0c67SMark Fenwick 4816628b0c67SMark Fenwick /* 4817628b0c67SMark Fenwick * For backwards compatability, assume that the IV length 4818628b0c67SMark Fenwick * is the same as the data length. 4819628b0c67SMark Fenwick */ 4820628b0c67SMark Fenwick alg->alg_ivlen = alg->alg_datalen; 4821628b0c67SMark Fenwick 4822628b0c67SMark Fenwick /* 4823628b0c67SMark Fenwick * Copy any algorithm parameters (if provided) into dedicated 4824628b0c67SMark Fenwick * elements in the ipsec_alginfo_t structure. 4825628b0c67SMark Fenwick * There may be a better place to put this code. 4826628b0c67SMark Fenwick */ 4827628b0c67SMark Fenwick for (i = 0; i < alg->alg_nparams; i++) { 4828628b0c67SMark Fenwick switch (i) { 4829628b0c67SMark Fenwick case 0: 4830628b0c67SMark Fenwick /* Initialisation Vector length (bytes) */ 4831628b0c67SMark Fenwick alg->alg_ivlen = alg->alg_params[0]; 4832628b0c67SMark Fenwick break; 4833628b0c67SMark Fenwick case 1: 4834628b0c67SMark Fenwick /* Integrity Check Vector length (bytes) */ 4835628b0c67SMark Fenwick alg->alg_icvlen = alg->alg_params[1]; 4836628b0c67SMark Fenwick break; 4837628b0c67SMark Fenwick case 2: 4838628b0c67SMark Fenwick /* Salt length (bytes) */ 4839628b0c67SMark Fenwick alg->alg_saltlen = (uint8_t)alg->alg_params[2]; 4840628b0c67SMark Fenwick break; 4841628b0c67SMark Fenwick default: 4842628b0c67SMark Fenwick break; 4843628b0c67SMark Fenwick } 4844628b0c67SMark Fenwick } 4845628b0c67SMark Fenwick 4846628b0c67SMark Fenwick /* Default if the IV length is not specified. */ 4847628b0c67SMark Fenwick if (alg_type == IPSEC_ALG_ENCR && alg->alg_ivlen == 0) 4848628b0c67SMark Fenwick alg->alg_ivlen = alg->alg_datalen; 4849628b0c67SMark Fenwick 4850628b0c67SMark Fenwick alg_flag_check(alg); 4851628b0c67SMark Fenwick 48527c478bd9Sstevel@tonic-gate if (alg->alg_increment != 0) { 48537c478bd9Sstevel@tonic-gate /* supported key sizes are defined by range & increment */ 48547c478bd9Sstevel@tonic-gate crypto_min = ALGBITS_ROUND_UP(crypto_min, alg->alg_increment); 48557c478bd9Sstevel@tonic-gate crypto_max = ALGBITS_ROUND_DOWN(crypto_max, alg->alg_increment); 48567c478bd9Sstevel@tonic-gate 48577c478bd9Sstevel@tonic-gate alg->alg_ef_minbits = MAX(alg->alg_minbits, 48587c478bd9Sstevel@tonic-gate (uint16_t)crypto_min); 48597c478bd9Sstevel@tonic-gate alg->alg_ef_maxbits = MIN(alg->alg_maxbits, 48607c478bd9Sstevel@tonic-gate (uint16_t)crypto_max); 48617c478bd9Sstevel@tonic-gate 48627c478bd9Sstevel@tonic-gate /* 48637c478bd9Sstevel@tonic-gate * If the sizes supported by the framework are outside 48647c478bd9Sstevel@tonic-gate * the range of sizes defined by the algorithm mappings, 48657c478bd9Sstevel@tonic-gate * the algorithm cannot be used. Check for this 48667c478bd9Sstevel@tonic-gate * condition here. 48677c478bd9Sstevel@tonic-gate */ 48687c478bd9Sstevel@tonic-gate if (alg->alg_ef_minbits > alg->alg_ef_maxbits) { 48697c478bd9Sstevel@tonic-gate alg->alg_flags &= ~ALG_FLAG_VALID; 48707c478bd9Sstevel@tonic-gate return; 48717c478bd9Sstevel@tonic-gate } 48727c478bd9Sstevel@tonic-gate if (alg->alg_ef_default_bits < alg->alg_ef_minbits) 48737c478bd9Sstevel@tonic-gate alg->alg_ef_default_bits = alg->alg_ef_minbits; 48747c478bd9Sstevel@tonic-gate if (alg->alg_ef_default_bits > alg->alg_ef_maxbits) 48757c478bd9Sstevel@tonic-gate alg->alg_ef_default_bits = alg->alg_ef_maxbits; 48767c478bd9Sstevel@tonic-gate } else if (alg->alg_nkey_sizes == 0) { 48777c478bd9Sstevel@tonic-gate /* no specified key size for algorithm */ 48787c478bd9Sstevel@tonic-gate alg->alg_ef_minbits = alg->alg_ef_maxbits = 0; 48797c478bd9Sstevel@tonic-gate } else { 48807c478bd9Sstevel@tonic-gate /* supported key sizes are defined by enumeration */ 48817c478bd9Sstevel@tonic-gate alg->alg_ef_minbits = (uint16_t)-1; 48827c478bd9Sstevel@tonic-gate alg->alg_ef_maxbits = 0; 48837c478bd9Sstevel@tonic-gate 48847c478bd9Sstevel@tonic-gate for (i = 0, is_valid = B_FALSE; i < alg->alg_nkey_sizes; i++) { 48857c478bd9Sstevel@tonic-gate /* 48867c478bd9Sstevel@tonic-gate * Ignore the current key size if it is not in the 48877c478bd9Sstevel@tonic-gate * range of sizes supported by the framework. 48887c478bd9Sstevel@tonic-gate */ 48897c478bd9Sstevel@tonic-gate if (alg->alg_key_sizes[i] < crypto_min || 48907c478bd9Sstevel@tonic-gate alg->alg_key_sizes[i] > crypto_max) 48917c478bd9Sstevel@tonic-gate continue; 48927c478bd9Sstevel@tonic-gate if (alg->alg_key_sizes[i] < alg->alg_ef_minbits) 48937c478bd9Sstevel@tonic-gate alg->alg_ef_minbits = alg->alg_key_sizes[i]; 48947c478bd9Sstevel@tonic-gate if (alg->alg_key_sizes[i] > alg->alg_ef_maxbits) 48957c478bd9Sstevel@tonic-gate alg->alg_ef_maxbits = alg->alg_key_sizes[i]; 48967c478bd9Sstevel@tonic-gate is_valid = B_TRUE; 48977c478bd9Sstevel@tonic-gate } 48987c478bd9Sstevel@tonic-gate 48997c478bd9Sstevel@tonic-gate if (!is_valid) { 49007c478bd9Sstevel@tonic-gate alg->alg_flags &= ~ALG_FLAG_VALID; 49017c478bd9Sstevel@tonic-gate return; 49027c478bd9Sstevel@tonic-gate } 49037c478bd9Sstevel@tonic-gate alg->alg_ef_default = 0; 49047c478bd9Sstevel@tonic-gate } 49057c478bd9Sstevel@tonic-gate } 49067c478bd9Sstevel@tonic-gate 49077c478bd9Sstevel@tonic-gate /* 4908628b0c67SMark Fenwick * Sanity check parameters provided by ipsecalgs(1m). Assume that 4909628b0c67SMark Fenwick * the algoritm is marked as valid, there is a check at the top 4910628b0c67SMark Fenwick * of this function. If any of the checks below fail, the algorithm 4911628b0c67SMark Fenwick * entry is invalid. 4912628b0c67SMark Fenwick */ 4913628b0c67SMark Fenwick void 4914628b0c67SMark Fenwick alg_flag_check(ipsec_alginfo_t *alg) 4915628b0c67SMark Fenwick { 4916628b0c67SMark Fenwick alg->alg_flags &= ~ALG_FLAG_VALID; 4917628b0c67SMark Fenwick 4918628b0c67SMark Fenwick /* 4919628b0c67SMark Fenwick * Can't have the algorithm marked as CCM and GCM. 4920628b0c67SMark Fenwick * Check the ALG_FLAG_COMBINED and ALG_FLAG_COUNTERMODE 4921628b0c67SMark Fenwick * flags are set for CCM & GCM. 4922628b0c67SMark Fenwick */ 4923628b0c67SMark Fenwick if ((alg->alg_flags & (ALG_FLAG_CCM|ALG_FLAG_GCM)) == 4924628b0c67SMark Fenwick (ALG_FLAG_CCM|ALG_FLAG_GCM)) 4925628b0c67SMark Fenwick return; 4926628b0c67SMark Fenwick if (alg->alg_flags & (ALG_FLAG_CCM|ALG_FLAG_GCM)) { 4927628b0c67SMark Fenwick if (!(alg->alg_flags & ALG_FLAG_COUNTERMODE)) 4928628b0c67SMark Fenwick return; 4929628b0c67SMark Fenwick if (!(alg->alg_flags & ALG_FLAG_COMBINED)) 4930628b0c67SMark Fenwick return; 4931628b0c67SMark Fenwick } 4932628b0c67SMark Fenwick 4933628b0c67SMark Fenwick /* 4934628b0c67SMark Fenwick * For ALG_FLAG_COUNTERMODE, check the parameters 4935628b0c67SMark Fenwick * fit in the ipsec_nonce_t structure. 4936628b0c67SMark Fenwick */ 4937628b0c67SMark Fenwick if (alg->alg_flags & ALG_FLAG_COUNTERMODE) { 4938628b0c67SMark Fenwick if (alg->alg_ivlen != sizeof (((ipsec_nonce_t *)NULL)->iv)) 4939628b0c67SMark Fenwick return; 4940628b0c67SMark Fenwick if (alg->alg_saltlen > sizeof (((ipsec_nonce_t *)NULL)->salt)) 4941628b0c67SMark Fenwick return; 4942628b0c67SMark Fenwick } 4943628b0c67SMark Fenwick if ((alg->alg_flags & ALG_FLAG_COMBINED) && 4944628b0c67SMark Fenwick (alg->alg_icvlen == 0)) 4945628b0c67SMark Fenwick return; 4946628b0c67SMark Fenwick 4947628b0c67SMark Fenwick /* all is well. */ 4948628b0c67SMark Fenwick alg->alg_flags |= ALG_FLAG_VALID; 4949628b0c67SMark Fenwick } 4950628b0c67SMark Fenwick 4951628b0c67SMark Fenwick /* 49527c478bd9Sstevel@tonic-gate * Free the memory used by the specified algorithm. 49537c478bd9Sstevel@tonic-gate */ 49547c478bd9Sstevel@tonic-gate void 49557c478bd9Sstevel@tonic-gate ipsec_alg_free(ipsec_alginfo_t *alg) 49567c478bd9Sstevel@tonic-gate { 49577c478bd9Sstevel@tonic-gate if (alg == NULL) 49587c478bd9Sstevel@tonic-gate return; 49597c478bd9Sstevel@tonic-gate 4960f4b3ec61Sdh155122 if (alg->alg_key_sizes != NULL) { 49617c478bd9Sstevel@tonic-gate kmem_free(alg->alg_key_sizes, 49627c478bd9Sstevel@tonic-gate (alg->alg_nkey_sizes + 1) * sizeof (uint16_t)); 4963f4b3ec61Sdh155122 alg->alg_key_sizes = NULL; 4964f4b3ec61Sdh155122 } 4965f4b3ec61Sdh155122 if (alg->alg_block_sizes != NULL) { 49667c478bd9Sstevel@tonic-gate kmem_free(alg->alg_block_sizes, 49677c478bd9Sstevel@tonic-gate (alg->alg_nblock_sizes + 1) * sizeof (uint16_t)); 4968f4b3ec61Sdh155122 alg->alg_block_sizes = NULL; 4969f4b3ec61Sdh155122 } 4970d0115d88SMark Fenwick if (alg->alg_params != NULL) { 4971d0115d88SMark Fenwick kmem_free(alg->alg_params, 4972d0115d88SMark Fenwick (alg->alg_nparams + 1) * sizeof (uint16_t)); 4973d0115d88SMark Fenwick alg->alg_params = NULL; 4974d0115d88SMark Fenwick } 49757c478bd9Sstevel@tonic-gate kmem_free(alg, sizeof (*alg)); 49767c478bd9Sstevel@tonic-gate } 49777c478bd9Sstevel@tonic-gate 49787c478bd9Sstevel@tonic-gate /* 49797c478bd9Sstevel@tonic-gate * Check the validity of the specified key size for an algorithm. 49807c478bd9Sstevel@tonic-gate * Returns B_TRUE if key size is valid, B_FALSE otherwise. 49817c478bd9Sstevel@tonic-gate */ 49827c478bd9Sstevel@tonic-gate boolean_t 49837c478bd9Sstevel@tonic-gate ipsec_valid_key_size(uint16_t key_size, ipsec_alginfo_t *alg) 49847c478bd9Sstevel@tonic-gate { 49857c478bd9Sstevel@tonic-gate if (key_size < alg->alg_ef_minbits || key_size > alg->alg_ef_maxbits) 49867c478bd9Sstevel@tonic-gate return (B_FALSE); 49877c478bd9Sstevel@tonic-gate 49887c478bd9Sstevel@tonic-gate if (alg->alg_increment == 0 && alg->alg_nkey_sizes != 0) { 49897c478bd9Sstevel@tonic-gate /* 49907c478bd9Sstevel@tonic-gate * If the key sizes are defined by enumeration, the new 49917c478bd9Sstevel@tonic-gate * key size must be equal to one of the supported values. 49927c478bd9Sstevel@tonic-gate */ 49937c478bd9Sstevel@tonic-gate int i; 49947c478bd9Sstevel@tonic-gate 49957c478bd9Sstevel@tonic-gate for (i = 0; i < alg->alg_nkey_sizes; i++) 49967c478bd9Sstevel@tonic-gate if (key_size == alg->alg_key_sizes[i]) 49977c478bd9Sstevel@tonic-gate break; 49987c478bd9Sstevel@tonic-gate if (i == alg->alg_nkey_sizes) 49997c478bd9Sstevel@tonic-gate return (B_FALSE); 50007c478bd9Sstevel@tonic-gate } 50017c478bd9Sstevel@tonic-gate 50027c478bd9Sstevel@tonic-gate return (B_TRUE); 50037c478bd9Sstevel@tonic-gate } 50047c478bd9Sstevel@tonic-gate 50057c478bd9Sstevel@tonic-gate /* 50067c478bd9Sstevel@tonic-gate * Callback function invoked by the crypto framework when a provider 50077c478bd9Sstevel@tonic-gate * registers or unregisters. This callback updates the algorithms 50087c478bd9Sstevel@tonic-gate * tables when a crypto algorithm is no longer available or becomes 50097c478bd9Sstevel@tonic-gate * available, and triggers the freeing/creation of context templates 50107c478bd9Sstevel@tonic-gate * associated with existing SAs, if needed. 5011f4b3ec61Sdh155122 * 5012f4b3ec61Sdh155122 * Need to walk all stack instances since the callback is global 5013f4b3ec61Sdh155122 * for all instances 50147c478bd9Sstevel@tonic-gate */ 50157c478bd9Sstevel@tonic-gate void 50167c478bd9Sstevel@tonic-gate ipsec_prov_update_callback(uint32_t event, void *event_arg) 50177c478bd9Sstevel@tonic-gate { 5018f4b3ec61Sdh155122 netstack_handle_t nh; 5019f4b3ec61Sdh155122 netstack_t *ns; 5020f4b3ec61Sdh155122 5021f4b3ec61Sdh155122 netstack_next_init(&nh); 5022f4b3ec61Sdh155122 while ((ns = netstack_next(&nh)) != NULL) { 5023f4b3ec61Sdh155122 ipsec_prov_update_callback_stack(event, event_arg, ns); 5024f4b3ec61Sdh155122 netstack_rele(ns); 5025f4b3ec61Sdh155122 } 5026f4b3ec61Sdh155122 netstack_next_fini(&nh); 5027f4b3ec61Sdh155122 } 5028f4b3ec61Sdh155122 5029f4b3ec61Sdh155122 static void 5030f4b3ec61Sdh155122 ipsec_prov_update_callback_stack(uint32_t event, void *event_arg, 5031f4b3ec61Sdh155122 netstack_t *ns) 5032f4b3ec61Sdh155122 { 50337c478bd9Sstevel@tonic-gate crypto_notify_event_change_t *prov_change = 50347c478bd9Sstevel@tonic-gate (crypto_notify_event_change_t *)event_arg; 50357c478bd9Sstevel@tonic-gate uint_t algidx, algid, algtype, mech_count, mech_idx; 50367c478bd9Sstevel@tonic-gate ipsec_alginfo_t *alg; 50377c478bd9Sstevel@tonic-gate ipsec_alginfo_t oalg; 50387c478bd9Sstevel@tonic-gate crypto_mech_name_t *mechs; 50397c478bd9Sstevel@tonic-gate boolean_t alg_changed = B_FALSE; 5040f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 50417c478bd9Sstevel@tonic-gate 50427c478bd9Sstevel@tonic-gate /* ignore events for which we didn't register */ 5043c892ebf1Skrishna if (event != CRYPTO_EVENT_MECHS_CHANGED) { 50447c478bd9Sstevel@tonic-gate ip1dbg(("ipsec_prov_update_callback: unexpected event 0x%x " 50457c478bd9Sstevel@tonic-gate " received from crypto framework\n", event)); 50467c478bd9Sstevel@tonic-gate return; 50477c478bd9Sstevel@tonic-gate } 50487c478bd9Sstevel@tonic-gate 50497c478bd9Sstevel@tonic-gate mechs = crypto_get_mech_list(&mech_count, KM_SLEEP); 50507c478bd9Sstevel@tonic-gate if (mechs == NULL) 50517c478bd9Sstevel@tonic-gate return; 50527c478bd9Sstevel@tonic-gate 50537c478bd9Sstevel@tonic-gate /* 50547c478bd9Sstevel@tonic-gate * Walk the list of currently defined IPsec algorithm. Update 50557c478bd9Sstevel@tonic-gate * the algorithm valid flag and trigger an update of the 50567c478bd9Sstevel@tonic-gate * SAs that depend on that algorithm. 50577c478bd9Sstevel@tonic-gate */ 5058f4b3ec61Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 50597c478bd9Sstevel@tonic-gate for (algtype = 0; algtype < IPSEC_NALGTYPES; algtype++) { 5060f4b3ec61Sdh155122 for (algidx = 0; algidx < ipss->ipsec_nalgs[algtype]; 5061f4b3ec61Sdh155122 algidx++) { 50627c478bd9Sstevel@tonic-gate 5063f4b3ec61Sdh155122 algid = ipss->ipsec_sortlist[algtype][algidx]; 5064f4b3ec61Sdh155122 alg = ipss->ipsec_alglists[algtype][algid]; 50657c478bd9Sstevel@tonic-gate ASSERT(alg != NULL); 50667c478bd9Sstevel@tonic-gate 50677c478bd9Sstevel@tonic-gate /* 50687c478bd9Sstevel@tonic-gate * Skip the algorithms which do not map to the 50697c478bd9Sstevel@tonic-gate * crypto framework provider being added or removed. 50707c478bd9Sstevel@tonic-gate */ 50717c478bd9Sstevel@tonic-gate if (strncmp(alg->alg_mech_name, 50727c478bd9Sstevel@tonic-gate prov_change->ec_mech_name, 50737c478bd9Sstevel@tonic-gate CRYPTO_MAX_MECH_NAME) != 0) 50747c478bd9Sstevel@tonic-gate continue; 50757c478bd9Sstevel@tonic-gate 50767c478bd9Sstevel@tonic-gate /* 50777c478bd9Sstevel@tonic-gate * Determine if the mechanism is valid. If it 50787c478bd9Sstevel@tonic-gate * is not, mark the algorithm as being invalid. If 50797c478bd9Sstevel@tonic-gate * it is, mark the algorithm as being valid. 50807c478bd9Sstevel@tonic-gate */ 50817c478bd9Sstevel@tonic-gate for (mech_idx = 0; mech_idx < mech_count; mech_idx++) 50827c478bd9Sstevel@tonic-gate if (strncmp(alg->alg_mech_name, 50837c478bd9Sstevel@tonic-gate mechs[mech_idx], CRYPTO_MAX_MECH_NAME) == 0) 50847c478bd9Sstevel@tonic-gate break; 50857c478bd9Sstevel@tonic-gate if (mech_idx == mech_count && 50867c478bd9Sstevel@tonic-gate alg->alg_flags & ALG_FLAG_VALID) { 50877c478bd9Sstevel@tonic-gate alg->alg_flags &= ~ALG_FLAG_VALID; 50887c478bd9Sstevel@tonic-gate alg_changed = B_TRUE; 50897c478bd9Sstevel@tonic-gate } else if (mech_idx < mech_count && 50907c478bd9Sstevel@tonic-gate !(alg->alg_flags & ALG_FLAG_VALID)) { 50917c478bd9Sstevel@tonic-gate alg->alg_flags |= ALG_FLAG_VALID; 50927c478bd9Sstevel@tonic-gate alg_changed = B_TRUE; 50937c478bd9Sstevel@tonic-gate } 50947c478bd9Sstevel@tonic-gate 50957c478bd9Sstevel@tonic-gate /* 50967c478bd9Sstevel@tonic-gate * Update the supported key sizes, regardless 50977c478bd9Sstevel@tonic-gate * of whether a crypto provider was added or 50987c478bd9Sstevel@tonic-gate * removed. 50997c478bd9Sstevel@tonic-gate */ 51007c478bd9Sstevel@tonic-gate oalg = *alg; 5101f4b3ec61Sdh155122 ipsec_alg_fix_min_max(alg, algtype, ns); 51027c478bd9Sstevel@tonic-gate if (!alg_changed && 51037c478bd9Sstevel@tonic-gate alg->alg_ef_minbits != oalg.alg_ef_minbits || 51047c478bd9Sstevel@tonic-gate alg->alg_ef_maxbits != oalg.alg_ef_maxbits || 51057c478bd9Sstevel@tonic-gate alg->alg_ef_default != oalg.alg_ef_default || 51067c478bd9Sstevel@tonic-gate alg->alg_ef_default_bits != 51077c478bd9Sstevel@tonic-gate oalg.alg_ef_default_bits) 51087c478bd9Sstevel@tonic-gate alg_changed = B_TRUE; 51097c478bd9Sstevel@tonic-gate 51107c478bd9Sstevel@tonic-gate /* 51117c478bd9Sstevel@tonic-gate * Update the affected SAs if a software provider is 51127c478bd9Sstevel@tonic-gate * being added or removed. 51137c478bd9Sstevel@tonic-gate */ 51147c478bd9Sstevel@tonic-gate if (prov_change->ec_provider_type == 51157c478bd9Sstevel@tonic-gate CRYPTO_SW_PROVIDER) 51167c478bd9Sstevel@tonic-gate sadb_alg_update(algtype, alg->alg_id, 51177c478bd9Sstevel@tonic-gate prov_change->ec_change == 5118f4b3ec61Sdh155122 CRYPTO_MECH_ADDED, ns); 51197c478bd9Sstevel@tonic-gate } 51207c478bd9Sstevel@tonic-gate } 5121f4b3ec61Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 51227c478bd9Sstevel@tonic-gate crypto_free_mech_list(mechs, mech_count); 51237c478bd9Sstevel@tonic-gate 51247c478bd9Sstevel@tonic-gate if (alg_changed) { 51257c478bd9Sstevel@tonic-gate /* 51267c478bd9Sstevel@tonic-gate * An algorithm has changed, i.e. it became valid or 51277c478bd9Sstevel@tonic-gate * invalid, or its support key sizes have changed. 51287c478bd9Sstevel@tonic-gate * Notify ipsecah and ipsecesp of this change so 51297c478bd9Sstevel@tonic-gate * that they can send a SADB_REGISTER to their consumers. 51307c478bd9Sstevel@tonic-gate */ 5131f4b3ec61Sdh155122 ipsecah_algs_changed(ns); 5132f4b3ec61Sdh155122 ipsecesp_algs_changed(ns); 51337c478bd9Sstevel@tonic-gate } 51347c478bd9Sstevel@tonic-gate } 51357c478bd9Sstevel@tonic-gate 51367c478bd9Sstevel@tonic-gate /* 51377c478bd9Sstevel@tonic-gate * Registers with the crypto framework to be notified of crypto 51387c478bd9Sstevel@tonic-gate * providers changes. Used to update the algorithm tables and 51397c478bd9Sstevel@tonic-gate * to free or create context templates if needed. Invoked after IPsec 51407c478bd9Sstevel@tonic-gate * is loaded successfully. 5141f4b3ec61Sdh155122 * 5142f4b3ec61Sdh155122 * This is called separately for each IP instance, so we ensure we only 5143f4b3ec61Sdh155122 * register once. 51447c478bd9Sstevel@tonic-gate */ 51457c478bd9Sstevel@tonic-gate void 51467c478bd9Sstevel@tonic-gate ipsec_register_prov_update(void) 51477c478bd9Sstevel@tonic-gate { 5148f4b3ec61Sdh155122 if (prov_update_handle != NULL) 5149f4b3ec61Sdh155122 return; 5150f4b3ec61Sdh155122 51517c478bd9Sstevel@tonic-gate prov_update_handle = crypto_notify_events( 5152c892ebf1Skrishna ipsec_prov_update_callback, CRYPTO_EVENT_MECHS_CHANGED); 51537c478bd9Sstevel@tonic-gate } 51547c478bd9Sstevel@tonic-gate 51557c478bd9Sstevel@tonic-gate /* 51567c478bd9Sstevel@tonic-gate * Unregisters from the framework to be notified of crypto providers 5157f4b3ec61Sdh155122 * changes. Called from ipsec_policy_g_destroy(). 51587c478bd9Sstevel@tonic-gate */ 51597c478bd9Sstevel@tonic-gate static void 51607c478bd9Sstevel@tonic-gate ipsec_unregister_prov_update(void) 51617c478bd9Sstevel@tonic-gate { 51627c478bd9Sstevel@tonic-gate if (prov_update_handle != NULL) 51637c478bd9Sstevel@tonic-gate crypto_unnotify_events(prov_update_handle); 51647c478bd9Sstevel@tonic-gate } 51658810c16bSdanmcd 51668810c16bSdanmcd /* 51678810c16bSdanmcd * Tunnel-mode support routines. 51688810c16bSdanmcd */ 51698810c16bSdanmcd 51708810c16bSdanmcd /* 51718810c16bSdanmcd * Returns an mblk chain suitable for putnext() if policies match and IPsec 51728810c16bSdanmcd * SAs are available. If there's no per-tunnel policy, or a match comes back 51738810c16bSdanmcd * with no match, then still return the packet and have global policy take 51748810c16bSdanmcd * a crack at it in IP. 5175bd670b35SErik Nordmark * This updates the ip_xmit_attr with the IPsec policy. 51768810c16bSdanmcd * 51778810c16bSdanmcd * Remember -> we can be forwarding packets. Keep that in mind w.r.t. 51788810c16bSdanmcd * inner-packet contents. 51798810c16bSdanmcd */ 51808810c16bSdanmcd mblk_t * 51812b24ab6bSSebastien Roy ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, 5182bd670b35SErik Nordmark ip6_t *inner_ipv6, ipha_t *outer_ipv4, ip6_t *outer_ipv6, int outer_hdr_len, 5183bd670b35SErik Nordmark ip_xmit_attr_t *ixa) 51848810c16bSdanmcd { 51858810c16bSdanmcd ipsec_policy_head_t *polhead; 51868810c16bSdanmcd ipsec_selector_t sel; 5187bd670b35SErik Nordmark mblk_t *nmp; 51888810c16bSdanmcd boolean_t is_fragment; 51898810c16bSdanmcd ipsec_policy_t *pol; 51902b24ab6bSSebastien Roy ipsec_tun_pol_t *itp = iptun->iptun_itp; 51912b24ab6bSSebastien Roy netstack_t *ns = iptun->iptun_ns; 5192f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 51938810c16bSdanmcd 51948810c16bSdanmcd ASSERT(outer_ipv6 != NULL && outer_ipv4 == NULL || 51958810c16bSdanmcd outer_ipv4 != NULL && outer_ipv6 == NULL); 51968810c16bSdanmcd /* We take care of inners in a bit. */ 51978810c16bSdanmcd 5198bd670b35SErik Nordmark /* Are the IPsec fields initialized at all? */ 5199bd670b35SErik Nordmark if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) { 5200bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_policy == NULL); 5201bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_latch == NULL); 5202bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_action == NULL); 5203bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_ah_sa == NULL); 5204bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_esp_sa == NULL); 5205bd670b35SErik Nordmark } 5206bd670b35SErik Nordmark 52072b24ab6bSSebastien Roy ASSERT(itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)); 52088810c16bSdanmcd polhead = itp->itp_policy; 52098810c16bSdanmcd 52108810c16bSdanmcd bzero(&sel, sizeof (sel)); 52118810c16bSdanmcd if (inner_ipv4 != NULL) { 52128810c16bSdanmcd ASSERT(inner_ipv6 == NULL); 52138810c16bSdanmcd sel.ips_isv4 = B_TRUE; 52148810c16bSdanmcd sel.ips_local_addr_v4 = inner_ipv4->ipha_src; 52158810c16bSdanmcd sel.ips_remote_addr_v4 = inner_ipv4->ipha_dst; 52168810c16bSdanmcd sel.ips_protocol = (uint8_t)inner_ipv4->ipha_protocol; 52178810c16bSdanmcd } else { 52188810c16bSdanmcd ASSERT(inner_ipv6 != NULL); 52198810c16bSdanmcd sel.ips_isv4 = B_FALSE; 52208810c16bSdanmcd sel.ips_local_addr_v6 = inner_ipv6->ip6_src; 52218810c16bSdanmcd /* 522205d90a32SDan McDonald * We don't care about routing-header dests in the 522305d90a32SDan McDonald * forwarding/tunnel path, so just grab ip6_dst. 52248810c16bSdanmcd */ 52258810c16bSdanmcd sel.ips_remote_addr_v6 = inner_ipv6->ip6_dst; 52268810c16bSdanmcd } 52278810c16bSdanmcd 52288810c16bSdanmcd if (itp->itp_flags & ITPF_P_PER_PORT_SECURITY) { 522905d90a32SDan McDonald /* 523005d90a32SDan McDonald * Caller can prepend the outer header, which means 523105d90a32SDan McDonald * inner_ipv[46] may be stuck in the middle. Pullup the whole 523205d90a32SDan McDonald * mess now if need-be, for easier processing later. Don't 523305d90a32SDan McDonald * forget to rewire the outer header too. 523405d90a32SDan McDonald */ 523505d90a32SDan McDonald if (mp->b_cont != NULL) { 523605d90a32SDan McDonald nmp = msgpullup(mp, -1); 523705d90a32SDan McDonald if (nmp == NULL) { 5238bd670b35SErik Nordmark ip_drop_packet(mp, B_FALSE, NULL, 523905d90a32SDan McDonald DROPPER(ipss, ipds_spd_nomem), 524005d90a32SDan McDonald &ipss->ipsec_spd_dropper); 524105d90a32SDan McDonald return (NULL); 524205d90a32SDan McDonald } 524305d90a32SDan McDonald freemsg(mp); 524405d90a32SDan McDonald mp = nmp; 524505d90a32SDan McDonald if (outer_ipv4 != NULL) 524605d90a32SDan McDonald outer_ipv4 = (ipha_t *)mp->b_rptr; 524705d90a32SDan McDonald else 524805d90a32SDan McDonald outer_ipv6 = (ip6_t *)mp->b_rptr; 524905d90a32SDan McDonald if (inner_ipv4 != NULL) { 525005d90a32SDan McDonald inner_ipv4 = 525105d90a32SDan McDonald (ipha_t *)(mp->b_rptr + outer_hdr_len); 525205d90a32SDan McDonald } else { 525305d90a32SDan McDonald inner_ipv6 = 525405d90a32SDan McDonald (ip6_t *)(mp->b_rptr + outer_hdr_len); 525505d90a32SDan McDonald } 525605d90a32SDan McDonald } 525705d90a32SDan McDonald if (inner_ipv4 != NULL) { 525805d90a32SDan McDonald is_fragment = IS_V4_FRAGMENT( 525905d90a32SDan McDonald inner_ipv4->ipha_fragment_offset_and_flags); 526005d90a32SDan McDonald } else { 526105d90a32SDan McDonald sel.ips_remote_addr_v6 = ip_get_dst_v6(inner_ipv6, mp, 526205d90a32SDan McDonald &is_fragment); 526305d90a32SDan McDonald } 526405d90a32SDan McDonald 52658810c16bSdanmcd if (is_fragment) { 52668810c16bSdanmcd ipha_t *oiph; 52678810c16bSdanmcd ipha_t *iph = NULL; 52688810c16bSdanmcd ip6_t *ip6h = NULL; 52698810c16bSdanmcd int hdr_len; 52708810c16bSdanmcd uint16_t ip6_hdr_length; 52718810c16bSdanmcd uint8_t v6_proto; 52728810c16bSdanmcd uint8_t *v6_proto_p; 52738810c16bSdanmcd 52748810c16bSdanmcd /* 52758810c16bSdanmcd * We have a fragment we need to track! 52768810c16bSdanmcd */ 52778810c16bSdanmcd mp = ipsec_fragcache_add(&itp->itp_fragcache, NULL, mp, 5278f4b3ec61Sdh155122 outer_hdr_len, ipss); 52798810c16bSdanmcd if (mp == NULL) 52808810c16bSdanmcd return (NULL); 528105d90a32SDan McDonald ASSERT(mp->b_cont == NULL); 52828810c16bSdanmcd 52838810c16bSdanmcd /* 52842b24ab6bSSebastien Roy * If we get here, we have a full fragment chain 52858810c16bSdanmcd */ 52868810c16bSdanmcd 52878810c16bSdanmcd oiph = (ipha_t *)mp->b_rptr; 52888810c16bSdanmcd if (IPH_HDR_VERSION(oiph) == IPV4_VERSION) { 52898810c16bSdanmcd hdr_len = ((outer_hdr_len != 0) ? 52908810c16bSdanmcd IPH_HDR_LENGTH(oiph) : 0); 52918810c16bSdanmcd iph = (ipha_t *)(mp->b_rptr + hdr_len); 52928810c16bSdanmcd } else { 52938810c16bSdanmcd ASSERT(IPH_HDR_VERSION(oiph) == IPV6_VERSION); 529405d90a32SDan McDonald ip6h = (ip6_t *)mp->b_rptr; 529505d90a32SDan McDonald if (!ip_hdr_length_nexthdr_v6(mp, ip6h, 529605d90a32SDan McDonald &ip6_hdr_length, &v6_proto_p)) { 5297bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_FALSE, NULL, 5298bd670b35SErik Nordmark DROPPER(ipss, 529905d90a32SDan McDonald ipds_spd_malformed_packet), 5300f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 530105d90a32SDan McDonald return (NULL); 53028810c16bSdanmcd } 53038810c16bSdanmcd hdr_len = ip6_hdr_length; 53048810c16bSdanmcd } 53058810c16bSdanmcd outer_hdr_len = hdr_len; 53068810c16bSdanmcd 53078810c16bSdanmcd if (sel.ips_isv4) { 53088810c16bSdanmcd if (iph == NULL) { 53098810c16bSdanmcd /* Was v6 outer */ 53108810c16bSdanmcd iph = (ipha_t *)(mp->b_rptr + hdr_len); 53118810c16bSdanmcd } 53128810c16bSdanmcd inner_ipv4 = iph; 53138810c16bSdanmcd sel.ips_local_addr_v4 = inner_ipv4->ipha_src; 53148810c16bSdanmcd sel.ips_remote_addr_v4 = inner_ipv4->ipha_dst; 53158810c16bSdanmcd sel.ips_protocol = 53168810c16bSdanmcd (uint8_t)inner_ipv4->ipha_protocol; 53178810c16bSdanmcd } else { 531805d90a32SDan McDonald inner_ipv6 = (ip6_t *)(mp->b_rptr + 53198810c16bSdanmcd hdr_len); 53208810c16bSdanmcd sel.ips_local_addr_v6 = inner_ipv6->ip6_src; 53218810c16bSdanmcd sel.ips_remote_addr_v6 = inner_ipv6->ip6_dst; 532205d90a32SDan McDonald if (!ip_hdr_length_nexthdr_v6(mp, 532305d90a32SDan McDonald inner_ipv6, &ip6_hdr_length, &v6_proto_p)) { 5324bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_FALSE, NULL, 5325bd670b35SErik Nordmark DROPPER(ipss, 532605d90a32SDan McDonald ipds_spd_malformed_frag), 532705d90a32SDan McDonald &ipss->ipsec_spd_dropper); 532805d90a32SDan McDonald return (NULL); 532905d90a32SDan McDonald } 53308810c16bSdanmcd v6_proto = *v6_proto_p; 53318810c16bSdanmcd sel.ips_protocol = v6_proto; 53328810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 53338810c16bSdanmcd cmn_err(CE_WARN, "v6_sel.ips_protocol = %d\n", 53348810c16bSdanmcd sel.ips_protocol); 53358810c16bSdanmcd #endif 53368810c16bSdanmcd } 53378810c16bSdanmcd /* Ports are extracted below */ 53388810c16bSdanmcd } 53398810c16bSdanmcd 53408810c16bSdanmcd /* Get ports... */ 53418810c16bSdanmcd if (!ipsec_init_outbound_ports(&sel, mp, 5342f4b3ec61Sdh155122 inner_ipv4, inner_ipv6, outer_hdr_len, ipss)) { 53438810c16bSdanmcd /* callee did ip_drop_packet_chain() on mp. */ 53448810c16bSdanmcd return (NULL); 53458810c16bSdanmcd } 53468810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 53478810c16bSdanmcd if (inner_ipv4 != NULL) 53488810c16bSdanmcd cmn_err(CE_WARN, 53498810c16bSdanmcd "(v4) sel.ips_protocol = %d, " 53508810c16bSdanmcd "sel.ips_local_port = %d, " 53518810c16bSdanmcd "sel.ips_remote_port = %d\n", 53528810c16bSdanmcd sel.ips_protocol, ntohs(sel.ips_local_port), 53538810c16bSdanmcd ntohs(sel.ips_remote_port)); 53548810c16bSdanmcd if (inner_ipv6 != NULL) 53558810c16bSdanmcd cmn_err(CE_WARN, 53568810c16bSdanmcd "(v6) sel.ips_protocol = %d, " 53578810c16bSdanmcd "sel.ips_local_port = %d, " 53588810c16bSdanmcd "sel.ips_remote_port = %d\n", 53598810c16bSdanmcd sel.ips_protocol, ntohs(sel.ips_local_port), 53608810c16bSdanmcd ntohs(sel.ips_remote_port)); 53618810c16bSdanmcd #endif 536205d90a32SDan McDonald /* Success so far! */ 53638810c16bSdanmcd } 53648810c16bSdanmcd rw_enter(&polhead->iph_lock, RW_READER); 5365bd670b35SErik Nordmark pol = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_OUTBOUND, &sel); 53668810c16bSdanmcd rw_exit(&polhead->iph_lock); 53678810c16bSdanmcd if (pol == NULL) { 53688810c16bSdanmcd /* 53698810c16bSdanmcd * No matching policy on this tunnel, drop the packet. 53708810c16bSdanmcd * 53718810c16bSdanmcd * NOTE: Tunnel-mode tunnels are different from the 53728810c16bSdanmcd * IP global transport mode policy head. For a tunnel-mode 53738810c16bSdanmcd * tunnel, we drop the packet in lieu of passing it 53748810c16bSdanmcd * along accepted the way a global-policy miss would. 53758810c16bSdanmcd * 53768810c16bSdanmcd * NOTE2: "negotiate transport" tunnels should match ALL 53778810c16bSdanmcd * inbound packets, but we do not uncomment the ASSERT() 53788810c16bSdanmcd * below because if/when we open PF_POLICY, a user can 53798810c16bSdanmcd * shoot him/her-self in the foot with a 0 priority. 53808810c16bSdanmcd */ 53818810c16bSdanmcd 53828810c16bSdanmcd /* ASSERT(itp->itp_flags & ITPF_P_TUNNEL); */ 53838810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 53848810c16bSdanmcd cmn_err(CE_WARN, "ipsec_tun_outbound(): No matching tunnel " 53858810c16bSdanmcd "per-port policy\n"); 53868810c16bSdanmcd #endif 5387bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_FALSE, NULL, 5388f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_explicit), 5389f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 53908810c16bSdanmcd return (NULL); 53918810c16bSdanmcd } 53928810c16bSdanmcd 53938810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 53948810c16bSdanmcd cmn_err(CE_WARN, "Having matching tunnel per-port policy\n"); 53958810c16bSdanmcd #endif 53968810c16bSdanmcd 53978810c16bSdanmcd /* 5398bd670b35SErik Nordmark * NOTE: ixa_cleanup() function will release pol references. 53998810c16bSdanmcd */ 5400bd670b35SErik Nordmark ixa->ixa_ipsec_policy = pol; 54012b24ab6bSSebastien Roy /* 54022b24ab6bSSebastien Roy * NOTE: There is a subtle difference between iptun_zoneid and 54032b24ab6bSSebastien Roy * iptun_connp->conn_zoneid explained in iptun_conn_create(). When 54042b24ab6bSSebastien Roy * interacting with the ip module, we must use conn_zoneid. 54052b24ab6bSSebastien Roy */ 5406bd670b35SErik Nordmark ixa->ixa_zoneid = iptun->iptun_connp->conn_zoneid; 5407bd670b35SErik Nordmark 5408bd670b35SErik Nordmark ASSERT((outer_ipv4 != NULL) ? (ixa->ixa_flags & IXAF_IS_IPV4) : 5409bd670b35SErik Nordmark !(ixa->ixa_flags & IXAF_IS_IPV4)); 5410bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_policy != NULL); 5411bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_SECURE; 54128810c16bSdanmcd 54138810c16bSdanmcd if (!(itp->itp_flags & ITPF_P_TUNNEL)) { 54148810c16bSdanmcd /* Set up transport mode for tunnelled packets. */ 5415bd670b35SErik Nordmark ixa->ixa_ipsec_proto = (inner_ipv4 != NULL) ? IPPROTO_ENCAP : 54168810c16bSdanmcd IPPROTO_IPV6; 5417bd670b35SErik Nordmark return (mp); 54188810c16bSdanmcd } 54198810c16bSdanmcd 54208810c16bSdanmcd /* Fill in tunnel-mode goodies here. */ 5421bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IPSEC_TUNNEL; 54228810c16bSdanmcd /* XXX Do I need to fill in all of the goodies here? */ 54238810c16bSdanmcd if (inner_ipv4) { 5424bd670b35SErik Nordmark ixa->ixa_ipsec_inaf = AF_INET; 5425bd670b35SErik Nordmark ixa->ixa_ipsec_insrc[0] = 54268810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v4; 5427bd670b35SErik Nordmark ixa->ixa_ipsec_indst[0] = 54288810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v4; 54298810c16bSdanmcd } else { 5430bd670b35SErik Nordmark ixa->ixa_ipsec_inaf = AF_INET6; 5431bd670b35SErik Nordmark ixa->ixa_ipsec_insrc[0] = 54328810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[0]; 5433bd670b35SErik Nordmark ixa->ixa_ipsec_insrc[1] = 54348810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[1]; 5435bd670b35SErik Nordmark ixa->ixa_ipsec_insrc[2] = 54368810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[2]; 5437bd670b35SErik Nordmark ixa->ixa_ipsec_insrc[3] = 54388810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[3]; 5439bd670b35SErik Nordmark ixa->ixa_ipsec_indst[0] = 54408810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[0]; 5441bd670b35SErik Nordmark ixa->ixa_ipsec_indst[1] = 54428810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[1]; 5443bd670b35SErik Nordmark ixa->ixa_ipsec_indst[2] = 54448810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[2]; 5445bd670b35SErik Nordmark ixa->ixa_ipsec_indst[3] = 54468810c16bSdanmcd pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[3]; 54478810c16bSdanmcd } 5448bd670b35SErik Nordmark ixa->ixa_ipsec_insrcpfx = pol->ipsp_sel->ipsl_key.ipsl_local_pfxlen; 5449bd670b35SErik Nordmark ixa->ixa_ipsec_indstpfx = pol->ipsp_sel->ipsl_key.ipsl_remote_pfxlen; 54508810c16bSdanmcd /* NOTE: These are used for transport mode too. */ 5451bd670b35SErik Nordmark ixa->ixa_ipsec_src_port = pol->ipsp_sel->ipsl_key.ipsl_lport; 5452bd670b35SErik Nordmark ixa->ixa_ipsec_dst_port = pol->ipsp_sel->ipsl_key.ipsl_rport; 5453bd670b35SErik Nordmark ixa->ixa_ipsec_proto = pol->ipsp_sel->ipsl_key.ipsl_proto; 54548810c16bSdanmcd 5455bd670b35SErik Nordmark return (mp); 54568810c16bSdanmcd } 54578810c16bSdanmcd 54588810c16bSdanmcd /* 54598810c16bSdanmcd * NOTE: The following releases pol's reference and 54608810c16bSdanmcd * calls ip_drop_packet() for me on NULL returns. 54618810c16bSdanmcd */ 54628810c16bSdanmcd mblk_t * 5463bd670b35SErik Nordmark ipsec_check_ipsecin_policy_reasm(mblk_t *attr_mp, ipsec_policy_t *pol, 5464f4b3ec61Sdh155122 ipha_t *inner_ipv4, ip6_t *inner_ipv6, uint64_t pkt_unique, netstack_t *ns) 54658810c16bSdanmcd { 5466bd670b35SErik Nordmark /* Assume attr_mp is a chain of b_next-linked ip_recv_attr mblk. */ 54678810c16bSdanmcd mblk_t *data_chain = NULL, *data_tail = NULL; 5468bd670b35SErik Nordmark mblk_t *next; 5469bd670b35SErik Nordmark mblk_t *data_mp; 5470bd670b35SErik Nordmark ip_recv_attr_t iras; 54718810c16bSdanmcd 5472bd670b35SErik Nordmark while (attr_mp != NULL) { 5473bd670b35SErik Nordmark ASSERT(ip_recv_attr_is_mblk(attr_mp)); 5474bd670b35SErik Nordmark next = attr_mp->b_next; 5475bd670b35SErik Nordmark attr_mp->b_next = NULL; /* No tripping asserts. */ 5476bd670b35SErik Nordmark 5477bd670b35SErik Nordmark data_mp = attr_mp->b_cont; 5478bd670b35SErik Nordmark attr_mp->b_cont = NULL; 5479bd670b35SErik Nordmark if (!ip_recv_attr_from_mblk(attr_mp, &iras)) { 5480bd670b35SErik Nordmark /* The ill or ip_stack_t disappeared on us */ 5481bd670b35SErik Nordmark freemsg(data_mp); /* ip_drop_packet?? */ 5482bd670b35SErik Nordmark ira_cleanup(&iras, B_TRUE); 5483bd670b35SErik Nordmark goto fail; 5484bd670b35SErik Nordmark } 54858810c16bSdanmcd 54868810c16bSdanmcd /* 54878810c16bSdanmcd * Need IPPOL_REFHOLD(pol) for extras because 54888810c16bSdanmcd * ipsecin_policy does the refrele. 54898810c16bSdanmcd */ 54908810c16bSdanmcd IPPOL_REFHOLD(pol); 54918810c16bSdanmcd 5492bd670b35SErik Nordmark data_mp = ipsec_check_ipsecin_policy(data_mp, pol, inner_ipv4, 5493bd670b35SErik Nordmark inner_ipv6, pkt_unique, &iras, ns); 5494bd670b35SErik Nordmark ira_cleanup(&iras, B_TRUE); 5495bd670b35SErik Nordmark 5496bd670b35SErik Nordmark if (data_mp == NULL) 5497bd670b35SErik Nordmark goto fail; 5498bd670b35SErik Nordmark 54998810c16bSdanmcd if (data_tail == NULL) { 55008810c16bSdanmcd /* First one */ 5501bd670b35SErik Nordmark data_chain = data_tail = data_mp; 55028810c16bSdanmcd } else { 5503bd670b35SErik Nordmark data_tail->b_next = data_mp; 5504bd670b35SErik Nordmark data_tail = data_mp; 55058810c16bSdanmcd } 5506bd670b35SErik Nordmark attr_mp = next; 55078810c16bSdanmcd } 55088810c16bSdanmcd /* 55098810c16bSdanmcd * One last release because either the loop bumped it up, or we never 55108810c16bSdanmcd * called ipsec_check_ipsecin_policy(). 55118810c16bSdanmcd */ 5512bd670b35SErik Nordmark IPPOL_REFRELE(pol); 55138810c16bSdanmcd 55148810c16bSdanmcd /* data_chain is ready for return to tun module. */ 55158810c16bSdanmcd return (data_chain); 5516bd670b35SErik Nordmark 5517bd670b35SErik Nordmark fail: 5518bd670b35SErik Nordmark /* 5519bd670b35SErik Nordmark * Need to get rid of any extra pol 5520bd670b35SErik Nordmark * references, and any remaining bits as well. 5521bd670b35SErik Nordmark */ 5522bd670b35SErik Nordmark IPPOL_REFRELE(pol); 5523bd670b35SErik Nordmark ipsec_freemsg_chain(data_chain); 5524bd670b35SErik Nordmark ipsec_freemsg_chain(next); /* ipdrop stats? */ 5525bd670b35SErik Nordmark return (NULL); 55268810c16bSdanmcd } 55278810c16bSdanmcd 55288810c16bSdanmcd /* 5529bd670b35SErik Nordmark * Return a message if the inbound packet passed an IPsec policy check. Returns 5530bd670b35SErik Nordmark * NULL if it failed or if it is a fragment needing its friends before a 55318810c16bSdanmcd * policy check can be performed. 55328810c16bSdanmcd * 5533bd670b35SErik Nordmark * Expects a non-NULL data_mp, and a non-NULL polhead. 5534bd670b35SErik Nordmark * The returned mblk may be a b_next chain of packets if fragments 55358810c16bSdanmcd * neeeded to be collected for a proper policy check. 55368810c16bSdanmcd * 5537bd670b35SErik Nordmark * This function calls ip_drop_packet() on data_mp if need be. 55388810c16bSdanmcd * 55398810c16bSdanmcd * NOTE: outer_hdr_len is signed. If it's a negative value, the caller 55408810c16bSdanmcd * is inspecting an ICMP packet. 55418810c16bSdanmcd */ 5542bd670b35SErik Nordmark mblk_t * 5543bd670b35SErik Nordmark ipsec_tun_inbound(ip_recv_attr_t *ira, mblk_t *data_mp, ipsec_tun_pol_t *itp, 55448810c16bSdanmcd ipha_t *inner_ipv4, ip6_t *inner_ipv6, ipha_t *outer_ipv4, 5545f4b3ec61Sdh155122 ip6_t *outer_ipv6, int outer_hdr_len, netstack_t *ns) 55468810c16bSdanmcd { 55478810c16bSdanmcd ipsec_policy_head_t *polhead; 55488810c16bSdanmcd ipsec_selector_t sel; 55498810c16bSdanmcd ipsec_policy_t *pol; 55508810c16bSdanmcd uint16_t tmpport; 55518810c16bSdanmcd selret_t rc; 5552bd670b35SErik Nordmark boolean_t port_policy_present, is_icmp, global_present; 55538810c16bSdanmcd in6_addr_t tmpaddr; 5554a8e50c3eSdanmcd ipaddr_t tmp4; 55552b24ab6bSSebastien Roy uint8_t flags, *inner_hdr; 5556f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 55578810c16bSdanmcd 55588810c16bSdanmcd sel.ips_is_icmp_inv_acq = 0; 55598810c16bSdanmcd 556032350c00Sdanmcd if (outer_ipv4 != NULL) { 556132350c00Sdanmcd ASSERT(outer_ipv6 == NULL); 5562f4b3ec61Sdh155122 global_present = ipss->ipsec_inbound_v4_policy_present; 556332350c00Sdanmcd } else { 55642b24ab6bSSebastien Roy ASSERT(outer_ipv6 != NULL); 5565f4b3ec61Sdh155122 global_present = ipss->ipsec_inbound_v6_policy_present; 556632350c00Sdanmcd } 556732350c00Sdanmcd 55688810c16bSdanmcd ASSERT(inner_ipv4 != NULL && inner_ipv6 == NULL || 55698810c16bSdanmcd inner_ipv4 == NULL && inner_ipv6 != NULL); 55708810c16bSdanmcd 55718810c16bSdanmcd if (outer_hdr_len < 0) { 55728810c16bSdanmcd outer_hdr_len = (-outer_hdr_len); 55738810c16bSdanmcd is_icmp = B_TRUE; 55748810c16bSdanmcd } else { 55758810c16bSdanmcd is_icmp = B_FALSE; 55768810c16bSdanmcd } 55778810c16bSdanmcd 55788810c16bSdanmcd if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { 5579bd670b35SErik Nordmark mblk_t *mp = data_mp; 5580bd670b35SErik Nordmark 55818810c16bSdanmcd polhead = itp->itp_policy; 55828810c16bSdanmcd /* 55838810c16bSdanmcd * We need to perform full Tunnel-Mode enforcement, 55848810c16bSdanmcd * and we need to have inner-header data for such enforcement. 55858810c16bSdanmcd * 55868810c16bSdanmcd * See ipsec_init_inbound_sel() for the 0x80000000 on inbound 55878810c16bSdanmcd * and on return. 55888810c16bSdanmcd */ 55898810c16bSdanmcd 55908810c16bSdanmcd port_policy_present = ((itp->itp_flags & 55918810c16bSdanmcd ITPF_P_PER_PORT_SECURITY) ? B_TRUE : B_FALSE); 55922b24ab6bSSebastien Roy /* 55932b24ab6bSSebastien Roy * NOTE: Even if our policy is transport mode, set the 55942b24ab6bSSebastien Roy * SEL_TUNNEL_MODE flag so ipsec_init_inbound_sel() can 55952b24ab6bSSebastien Roy * do the right thing w.r.t. outer headers. 55962b24ab6bSSebastien Roy */ 55978810c16bSdanmcd flags = ((port_policy_present ? SEL_PORT_POLICY : SEL_NONE) | 55988810c16bSdanmcd (is_icmp ? SEL_IS_ICMP : SEL_NONE) | SEL_TUNNEL_MODE); 55998810c16bSdanmcd 5600bd670b35SErik Nordmark rc = ipsec_init_inbound_sel(&sel, data_mp, inner_ipv4, 56018810c16bSdanmcd inner_ipv6, flags); 56028810c16bSdanmcd 56038810c16bSdanmcd switch (rc) { 56048810c16bSdanmcd case SELRET_NOMEM: 5605bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, 5606f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 5607f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5608bd670b35SErik Nordmark return (NULL); 56098810c16bSdanmcd case SELRET_TUNFRAG: 56108810c16bSdanmcd /* 56118810c16bSdanmcd * At this point, if we're cleartext, we don't want 56128810c16bSdanmcd * to go there. 56138810c16bSdanmcd */ 5614bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 5615bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, 5616f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_got_clear), 5617f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5618bd670b35SErik Nordmark return (NULL); 56198810c16bSdanmcd } 5620d1a98e54SPaul Wernau 5621d1a98e54SPaul Wernau /* 5622d1a98e54SPaul Wernau * Inner and outer headers may not be contiguous. 5623d1a98e54SPaul Wernau * Pullup the data_mp now to satisfy assumptions of 5624d1a98e54SPaul Wernau * ipsec_fragcache_add() 5625d1a98e54SPaul Wernau */ 5626d1a98e54SPaul Wernau if (data_mp->b_cont != NULL) { 5627d1a98e54SPaul Wernau mblk_t *nmp; 5628d1a98e54SPaul Wernau 5629d1a98e54SPaul Wernau nmp = msgpullup(data_mp, -1); 5630d1a98e54SPaul Wernau if (nmp == NULL) { 5631d1a98e54SPaul Wernau ip_drop_packet(data_mp, B_TRUE, NULL, 5632d1a98e54SPaul Wernau DROPPER(ipss, ipds_spd_nomem), 5633d1a98e54SPaul Wernau &ipss->ipsec_spd_dropper); 5634d1a98e54SPaul Wernau return (NULL); 5635d1a98e54SPaul Wernau } 5636d1a98e54SPaul Wernau freemsg(data_mp); 5637d1a98e54SPaul Wernau data_mp = nmp; 5638d1a98e54SPaul Wernau if (outer_ipv4 != NULL) 5639d1a98e54SPaul Wernau outer_ipv4 = 5640d1a98e54SPaul Wernau (ipha_t *)data_mp->b_rptr; 5641d1a98e54SPaul Wernau else 5642d1a98e54SPaul Wernau outer_ipv6 = 5643d1a98e54SPaul Wernau (ip6_t *)data_mp->b_rptr; 5644d1a98e54SPaul Wernau if (inner_ipv4 != NULL) { 5645d1a98e54SPaul Wernau inner_ipv4 = 5646d1a98e54SPaul Wernau (ipha_t *)(data_mp->b_rptr + 5647d1a98e54SPaul Wernau outer_hdr_len); 5648d1a98e54SPaul Wernau } else { 5649d1a98e54SPaul Wernau inner_ipv6 = 5650d1a98e54SPaul Wernau (ip6_t *)(data_mp->b_rptr + 5651d1a98e54SPaul Wernau outer_hdr_len); 5652d1a98e54SPaul Wernau } 5653d1a98e54SPaul Wernau } 5654d1a98e54SPaul Wernau 5655bd670b35SErik Nordmark /* 5656bd670b35SErik Nordmark * If we need to queue the packet. First we 5657bd670b35SErik Nordmark * get an mblk with the attributes. ipsec_fragcache_add 5658bd670b35SErik Nordmark * will prepend that to the queued data and return 5659bd670b35SErik Nordmark * a list of b_next messages each of which starts with 5660bd670b35SErik Nordmark * the attribute mblk. 5661bd670b35SErik Nordmark */ 5662bd670b35SErik Nordmark mp = ip_recv_attr_to_mblk(ira); 5663bd670b35SErik Nordmark if (mp == NULL) { 5664bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, 5665bd670b35SErik Nordmark DROPPER(ipss, ipds_spd_nomem), 5666bd670b35SErik Nordmark &ipss->ipsec_spd_dropper); 5667bd670b35SErik Nordmark return (NULL); 5668bd670b35SErik Nordmark } 5669d1a98e54SPaul Wernau 5670bd670b35SErik Nordmark mp = ipsec_fragcache_add(&itp->itp_fragcache, 5671bd670b35SErik Nordmark mp, data_mp, outer_hdr_len, ipss); 56728810c16bSdanmcd 5673bd670b35SErik Nordmark if (mp == NULL) { 56748810c16bSdanmcd /* 56758810c16bSdanmcd * Data is cached, fragment chain is not 5676bd670b35SErik Nordmark * complete. 56778810c16bSdanmcd */ 5678bd670b35SErik Nordmark return (NULL); 56798810c16bSdanmcd } 56808810c16bSdanmcd 56818810c16bSdanmcd /* 56828810c16bSdanmcd * If we get here, we have a full fragment chain. 56838810c16bSdanmcd * Reacquire headers and selectors from first fragment. 56848810c16bSdanmcd */ 5685bd670b35SErik Nordmark ASSERT(ip_recv_attr_is_mblk(mp)); 5686bd670b35SErik Nordmark data_mp = mp->b_cont; 5687bd670b35SErik Nordmark inner_hdr = data_mp->b_rptr; 56882b24ab6bSSebastien Roy if (outer_ipv4 != NULL) { 56892b24ab6bSSebastien Roy inner_hdr += IPH_HDR_LENGTH( 5690bd670b35SErik Nordmark (ipha_t *)data_mp->b_rptr); 56918810c16bSdanmcd } else { 5692bd670b35SErik Nordmark inner_hdr += ip_hdr_length_v6(data_mp, 5693bd670b35SErik Nordmark (ip6_t *)data_mp->b_rptr); 56948810c16bSdanmcd } 5695bd670b35SErik Nordmark ASSERT(inner_hdr <= data_mp->b_wptr); 56962b24ab6bSSebastien Roy 56972b24ab6bSSebastien Roy if (inner_ipv4 != NULL) { 56982b24ab6bSSebastien Roy inner_ipv4 = (ipha_t *)inner_hdr; 56992b24ab6bSSebastien Roy inner_ipv6 = NULL; 57002b24ab6bSSebastien Roy } else { 57012b24ab6bSSebastien Roy inner_ipv6 = (ip6_t *)inner_hdr; 57022b24ab6bSSebastien Roy inner_ipv4 = NULL; 57032b24ab6bSSebastien Roy } 57042b24ab6bSSebastien Roy 57052b24ab6bSSebastien Roy /* 57062b24ab6bSSebastien Roy * Use SEL_TUNNEL_MODE to take into account the outer 57072b24ab6bSSebastien Roy * header. Use SEL_POST_FRAG so we always get ports. 57082b24ab6bSSebastien Roy */ 5709bd670b35SErik Nordmark rc = ipsec_init_inbound_sel(&sel, data_mp, 57102b24ab6bSSebastien Roy inner_ipv4, inner_ipv6, 57112b24ab6bSSebastien Roy SEL_TUNNEL_MODE | SEL_POST_FRAG); 57128810c16bSdanmcd switch (rc) { 57138810c16bSdanmcd case SELRET_SUCCESS: 57148810c16bSdanmcd /* 57158810c16bSdanmcd * Get to same place as first caller's 57168810c16bSdanmcd * SELRET_SUCCESS case. 57178810c16bSdanmcd */ 57188810c16bSdanmcd break; 57198810c16bSdanmcd case SELRET_NOMEM: 5720bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_TRUE, NULL, 5721f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 5722f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5723bd670b35SErik Nordmark return (NULL); 57248810c16bSdanmcd case SELRET_BADPKT: 5725bd670b35SErik Nordmark ip_drop_packet_chain(mp, B_TRUE, NULL, 5726f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_frag), 5727f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5728bd670b35SErik Nordmark return (NULL); 57298810c16bSdanmcd case SELRET_TUNFRAG: 57308810c16bSdanmcd cmn_err(CE_WARN, "(TUNFRAG on 2nd call...)"); 57318810c16bSdanmcd /* FALLTHRU */ 57328810c16bSdanmcd default: 57338810c16bSdanmcd cmn_err(CE_WARN, "ipsec_init_inbound_sel(mark2)" 57348810c16bSdanmcd " returns bizarro 0x%x", rc); 57358810c16bSdanmcd /* Guaranteed panic! */ 57368810c16bSdanmcd ASSERT(rc == SELRET_NOMEM); 5737bd670b35SErik Nordmark return (NULL); 57388810c16bSdanmcd } 57398810c16bSdanmcd /* FALLTHRU */ 57408810c16bSdanmcd case SELRET_SUCCESS: 57418810c16bSdanmcd /* 57428810c16bSdanmcd * Common case: 57438810c16bSdanmcd * No per-port policy or a non-fragment. Keep going. 57448810c16bSdanmcd */ 57458810c16bSdanmcd break; 57468810c16bSdanmcd case SELRET_BADPKT: 57478810c16bSdanmcd /* 57488810c16bSdanmcd * We may receive ICMP (with IPv6 inner) packets that 57498810c16bSdanmcd * trigger this return value. Send 'em in for 57508810c16bSdanmcd * enforcement checking. 57518810c16bSdanmcd */ 57528810c16bSdanmcd cmn_err(CE_NOTE, "ipsec_tun_inbound(): " 57538810c16bSdanmcd "sending 'bad packet' in for enforcement"); 57548810c16bSdanmcd break; 57558810c16bSdanmcd default: 57568810c16bSdanmcd cmn_err(CE_WARN, 57578810c16bSdanmcd "ipsec_init_inbound_sel() returns bizarro 0x%x", 57588810c16bSdanmcd rc); 57598810c16bSdanmcd ASSERT(rc == SELRET_NOMEM); /* Guaranteed panic! */ 5760bd670b35SErik Nordmark return (NULL); 57618810c16bSdanmcd } 57628810c16bSdanmcd 57638810c16bSdanmcd if (is_icmp) { 57648810c16bSdanmcd /* 57658810c16bSdanmcd * Swap local/remote because this is an ICMP packet. 57668810c16bSdanmcd */ 57678810c16bSdanmcd tmpaddr = sel.ips_local_addr_v6; 57688810c16bSdanmcd sel.ips_local_addr_v6 = sel.ips_remote_addr_v6; 57698810c16bSdanmcd sel.ips_remote_addr_v6 = tmpaddr; 57708810c16bSdanmcd tmpport = sel.ips_local_port; 57718810c16bSdanmcd sel.ips_local_port = sel.ips_remote_port; 57728810c16bSdanmcd sel.ips_remote_port = tmpport; 57738810c16bSdanmcd } 57748810c16bSdanmcd 57758810c16bSdanmcd /* find_policy_head() */ 57768810c16bSdanmcd rw_enter(&polhead->iph_lock, RW_READER); 57778810c16bSdanmcd pol = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_INBOUND, 5778bd670b35SErik Nordmark &sel); 57798810c16bSdanmcd rw_exit(&polhead->iph_lock); 57808810c16bSdanmcd if (pol != NULL) { 5781bd670b35SErik Nordmark uint64_t pkt_unique; 5782bd670b35SErik Nordmark 5783bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 5784bd670b35SErik Nordmark if (!pol->ipsp_act->ipa_allow_clear) { 57858810c16bSdanmcd /* 57868810c16bSdanmcd * XXX should never get here with 57878810c16bSdanmcd * tunnel reassembled fragments? 57888810c16bSdanmcd */ 5789bd670b35SErik Nordmark ASSERT(mp == data_mp); 5790bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, 5791f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_got_clear), 5792f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5793bd670b35SErik Nordmark IPPOL_REFRELE(pol); 5794bd670b35SErik Nordmark return (NULL); 5795bd670b35SErik Nordmark } else { 5796bd670b35SErik Nordmark IPPOL_REFRELE(pol); 5797bd670b35SErik Nordmark return (mp); 57988810c16bSdanmcd } 5799bd670b35SErik Nordmark } 5800bd670b35SErik Nordmark pkt_unique = SA_UNIQUE_ID(sel.ips_remote_port, 5801bd670b35SErik Nordmark sel.ips_local_port, 5802bd670b35SErik Nordmark (inner_ipv4 == NULL) ? IPPROTO_IPV6 : 5803bd670b35SErik Nordmark IPPROTO_ENCAP, sel.ips_protocol); 58048810c16bSdanmcd 58058810c16bSdanmcd /* 58068810c16bSdanmcd * NOTE: The following releases pol's reference and 58078810c16bSdanmcd * calls ip_drop_packet() for me on NULL returns. 58088810c16bSdanmcd * 58098810c16bSdanmcd * "sel" is still good here, so let's use it! 58108810c16bSdanmcd */ 5811bd670b35SErik Nordmark if (data_mp == mp) { 5812bd670b35SErik Nordmark /* A single packet without attributes */ 5813bd670b35SErik Nordmark data_mp = ipsec_check_ipsecin_policy(data_mp, 5814bd670b35SErik Nordmark pol, inner_ipv4, inner_ipv6, pkt_unique, 5815bd670b35SErik Nordmark ira, ns); 5816bd670b35SErik Nordmark } else { 5817bd670b35SErik Nordmark /* 5818bd670b35SErik Nordmark * We pass in the b_next chain of attr_mp's 5819bd670b35SErik Nordmark * and get back a b_next chain of data_mp's. 5820bd670b35SErik Nordmark */ 5821bd670b35SErik Nordmark data_mp = ipsec_check_ipsecin_policy_reasm(mp, 5822bd670b35SErik Nordmark pol, inner_ipv4, inner_ipv6, pkt_unique, 5823bd670b35SErik Nordmark ns); 5824bd670b35SErik Nordmark } 5825bd670b35SErik Nordmark return (data_mp); 58268810c16bSdanmcd } 58278810c16bSdanmcd 58288810c16bSdanmcd /* 58298810c16bSdanmcd * Else fallthru and check the global policy on the outer 58308810c16bSdanmcd * header(s) if this tunnel is an old-style transport-mode 58318810c16bSdanmcd * one. Drop the packet explicitly (no policy entry) for 58328810c16bSdanmcd * a new-style tunnel-mode tunnel. 58338810c16bSdanmcd */ 58348810c16bSdanmcd if ((itp->itp_flags & ITPF_P_TUNNEL) && !is_icmp) { 5835bd670b35SErik Nordmark ip_drop_packet_chain(data_mp, B_TRUE, NULL, 5836f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_explicit), 5837f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5838bd670b35SErik Nordmark return (NULL); 58398810c16bSdanmcd } 58408810c16bSdanmcd } 58418810c16bSdanmcd 58428810c16bSdanmcd /* 58438810c16bSdanmcd * NOTE: If we reach here, we will not have packet chains from 58448810c16bSdanmcd * fragcache_add(), because the only way I get chains is on a 58458810c16bSdanmcd * tunnel-mode tunnel, which either returns with a pass, or gets 58468810c16bSdanmcd * hit by the ip_drop_packet_chain() call right above here. 58478810c16bSdanmcd */ 5848bd670b35SErik Nordmark ASSERT(data_mp->b_next == NULL); 58498810c16bSdanmcd 58508810c16bSdanmcd /* If no per-tunnel security, check global policy now. */ 5851bd670b35SErik Nordmark if ((ira->ira_flags & IRAF_IPSEC_SECURE) && !global_present) { 5852bd670b35SErik Nordmark if (ira->ira_flags & IRAF_TRUSTED_ICMP) { 58538810c16bSdanmcd /* 5854bd670b35SErik Nordmark * This is an ICMP message that was geenrated locally. 5855bd670b35SErik Nordmark * We should accept it. 58568810c16bSdanmcd */ 5857bd670b35SErik Nordmark return (data_mp); 58588810c16bSdanmcd } 58598810c16bSdanmcd 5860bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, NULL, 5861f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_got_secure), 5862f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 5863bd670b35SErik Nordmark return (NULL); 58648810c16bSdanmcd } 58658810c16bSdanmcd 5866a8e50c3eSdanmcd if (is_icmp) { 5867a8e50c3eSdanmcd /* 5868a8e50c3eSdanmcd * For ICMP packets, "outer_ipvN" is set to the outer header 5869a8e50c3eSdanmcd * that is *INSIDE* the ICMP payload. For global policy 5870a8e50c3eSdanmcd * checking, we need to reverse src/dst on the payload in 5871a8e50c3eSdanmcd * order to construct selectors appropriately. See "ripha" 5872a8e50c3eSdanmcd * constructions in ip.c. To avoid a bug like 6478464 (see 5873a8e50c3eSdanmcd * earlier in this file), we will actually exchange src/dst 5874a8e50c3eSdanmcd * in the packet, and reverse if after the call to 5875a8e50c3eSdanmcd * ipsec_check_global_policy(). 5876a8e50c3eSdanmcd */ 5877a8e50c3eSdanmcd if (outer_ipv4 != NULL) { 5878a8e50c3eSdanmcd tmp4 = outer_ipv4->ipha_src; 5879a8e50c3eSdanmcd outer_ipv4->ipha_src = outer_ipv4->ipha_dst; 5880a8e50c3eSdanmcd outer_ipv4->ipha_dst = tmp4; 5881a8e50c3eSdanmcd } else { 5882a8e50c3eSdanmcd ASSERT(outer_ipv6 != NULL); 5883a8e50c3eSdanmcd tmpaddr = outer_ipv6->ip6_src; 5884a8e50c3eSdanmcd outer_ipv6->ip6_src = outer_ipv6->ip6_dst; 5885a8e50c3eSdanmcd outer_ipv6->ip6_dst = tmpaddr; 5886a8e50c3eSdanmcd } 5887a8e50c3eSdanmcd } 5888a8e50c3eSdanmcd 5889bd670b35SErik Nordmark data_mp = ipsec_check_global_policy(data_mp, NULL, outer_ipv4, 5890bd670b35SErik Nordmark outer_ipv6, ira, ns); 5891bd670b35SErik Nordmark if (data_mp == NULL) 5892bd670b35SErik Nordmark return (NULL); 58938810c16bSdanmcd 5894a8e50c3eSdanmcd if (is_icmp) { 5895a8e50c3eSdanmcd /* Set things back to normal. */ 5896a8e50c3eSdanmcd if (outer_ipv4 != NULL) { 5897a8e50c3eSdanmcd tmp4 = outer_ipv4->ipha_src; 5898a8e50c3eSdanmcd outer_ipv4->ipha_src = outer_ipv4->ipha_dst; 5899a8e50c3eSdanmcd outer_ipv4->ipha_dst = tmp4; 5900a8e50c3eSdanmcd } else { 5901a8e50c3eSdanmcd /* No need for ASSERT()s now. */ 5902a8e50c3eSdanmcd tmpaddr = outer_ipv6->ip6_src; 5903a8e50c3eSdanmcd outer_ipv6->ip6_src = outer_ipv6->ip6_dst; 5904a8e50c3eSdanmcd outer_ipv6->ip6_dst = tmpaddr; 5905a8e50c3eSdanmcd } 5906a8e50c3eSdanmcd } 5907a8e50c3eSdanmcd 59088810c16bSdanmcd /* 59098810c16bSdanmcd * At this point, we pretend it's a cleartext accepted 59108810c16bSdanmcd * packet. 59118810c16bSdanmcd */ 5912bd670b35SErik Nordmark return (data_mp); 59138810c16bSdanmcd } 59148810c16bSdanmcd 59158810c16bSdanmcd /* 59168810c16bSdanmcd * AVL comparison routine for our list of tunnel polheads. 59178810c16bSdanmcd */ 59188810c16bSdanmcd static int 59198810c16bSdanmcd tunnel_compare(const void *arg1, const void *arg2) 59208810c16bSdanmcd { 59218810c16bSdanmcd ipsec_tun_pol_t *left, *right; 59228810c16bSdanmcd int rc; 59238810c16bSdanmcd 59248810c16bSdanmcd left = (ipsec_tun_pol_t *)arg1; 59258810c16bSdanmcd right = (ipsec_tun_pol_t *)arg2; 59268810c16bSdanmcd 59278810c16bSdanmcd rc = strncmp(left->itp_name, right->itp_name, LIFNAMSIZ); 59288810c16bSdanmcd return (rc == 0 ? rc : (rc > 0 ? 1 : -1)); 59298810c16bSdanmcd } 59308810c16bSdanmcd 59318810c16bSdanmcd /* 59328810c16bSdanmcd * Free a tunnel policy node. 59338810c16bSdanmcd */ 59348810c16bSdanmcd void 5935f4b3ec61Sdh155122 itp_free(ipsec_tun_pol_t *node, netstack_t *ns) 59368810c16bSdanmcd { 59372b24ab6bSSebastien Roy if (node->itp_policy != NULL) { 5938f4b3ec61Sdh155122 IPPH_REFRELE(node->itp_policy, ns); 59392b24ab6bSSebastien Roy node->itp_policy = NULL; 59402b24ab6bSSebastien Roy } 59412b24ab6bSSebastien Roy if (node->itp_inactive != NULL) { 5942f4b3ec61Sdh155122 IPPH_REFRELE(node->itp_inactive, ns); 59432b24ab6bSSebastien Roy node->itp_inactive = NULL; 59442b24ab6bSSebastien Roy } 59458810c16bSdanmcd mutex_destroy(&node->itp_lock); 59468810c16bSdanmcd kmem_free(node, sizeof (*node)); 59478810c16bSdanmcd } 59488810c16bSdanmcd 59498810c16bSdanmcd void 5950f4b3ec61Sdh155122 itp_unlink(ipsec_tun_pol_t *node, netstack_t *ns) 59518810c16bSdanmcd { 5952f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 5953f4b3ec61Sdh155122 5954f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_WRITER); 5955f4b3ec61Sdh155122 ipss->ipsec_tunnel_policy_gen++; 5956bd670b35SErik Nordmark ipsec_fragcache_uninit(&node->itp_fragcache, ipss); 5957f4b3ec61Sdh155122 avl_remove(&ipss->ipsec_tunnel_policies, node); 5958f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 5959f4b3ec61Sdh155122 ITP_REFRELE(node, ns); 59608810c16bSdanmcd } 59618810c16bSdanmcd 59628810c16bSdanmcd /* 59638810c16bSdanmcd * Public interface to look up a tunnel security policy by name. Used by 59648810c16bSdanmcd * spdsock mostly. Returns "node" with a bumped refcnt. 59658810c16bSdanmcd */ 59668810c16bSdanmcd ipsec_tun_pol_t * 5967f4b3ec61Sdh155122 get_tunnel_policy(char *name, netstack_t *ns) 59688810c16bSdanmcd { 59698810c16bSdanmcd ipsec_tun_pol_t *node, lookup; 5970f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 59718810c16bSdanmcd 59728810c16bSdanmcd (void) strncpy(lookup.itp_name, name, LIFNAMSIZ); 59738810c16bSdanmcd 5974f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_READER); 5975f4b3ec61Sdh155122 node = (ipsec_tun_pol_t *)avl_find(&ipss->ipsec_tunnel_policies, 5976f4b3ec61Sdh155122 &lookup, NULL); 59778810c16bSdanmcd if (node != NULL) { 59788810c16bSdanmcd ITP_REFHOLD(node); 59798810c16bSdanmcd } 5980f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 59818810c16bSdanmcd 59828810c16bSdanmcd return (node); 59838810c16bSdanmcd } 59848810c16bSdanmcd 59858810c16bSdanmcd /* 59868810c16bSdanmcd * Public interface to walk all tunnel security polcies. Useful for spdsock 59878810c16bSdanmcd * DUMP operations. iterator() will not consume a reference. 59888810c16bSdanmcd */ 59898810c16bSdanmcd void 5990f4b3ec61Sdh155122 itp_walk(void (*iterator)(ipsec_tun_pol_t *, void *, netstack_t *), 5991f4b3ec61Sdh155122 void *arg, netstack_t *ns) 59928810c16bSdanmcd { 59938810c16bSdanmcd ipsec_tun_pol_t *node; 5994f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 59958810c16bSdanmcd 5996f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_READER); 5997f4b3ec61Sdh155122 for (node = avl_first(&ipss->ipsec_tunnel_policies); node != NULL; 5998f4b3ec61Sdh155122 node = AVL_NEXT(&ipss->ipsec_tunnel_policies, node)) { 5999f4b3ec61Sdh155122 iterator(node, arg, ns); 60008810c16bSdanmcd } 6001f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 60028810c16bSdanmcd } 60038810c16bSdanmcd 60048810c16bSdanmcd /* 60058810c16bSdanmcd * Initialize policy head. This can only fail if there's a memory problem. 60068810c16bSdanmcd */ 60078810c16bSdanmcd static boolean_t 6008f4b3ec61Sdh155122 tunnel_polhead_init(ipsec_policy_head_t *iph, netstack_t *ns) 60098810c16bSdanmcd { 6010f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 6011f4b3ec61Sdh155122 60128810c16bSdanmcd rw_init(&iph->iph_lock, NULL, RW_DEFAULT, NULL); 60138810c16bSdanmcd iph->iph_refs = 1; 60148810c16bSdanmcd iph->iph_gen = 0; 6015f4b3ec61Sdh155122 if (ipsec_alloc_table(iph, ipss->ipsec_tun_spd_hashsize, 6016f4b3ec61Sdh155122 KM_SLEEP, B_FALSE, ns) != 0) { 60178810c16bSdanmcd ipsec_polhead_free_table(iph); 60188810c16bSdanmcd return (B_FALSE); 60198810c16bSdanmcd } 6020f4b3ec61Sdh155122 ipsec_polhead_init(iph, ipss->ipsec_tun_spd_hashsize); 60218810c16bSdanmcd return (B_TRUE); 60228810c16bSdanmcd } 60238810c16bSdanmcd 60248810c16bSdanmcd /* 60258810c16bSdanmcd * Create a tunnel policy node with "name". Set errno with 60268810c16bSdanmcd * ENOMEM if there's a memory problem, and EEXIST if there's an existing 60278810c16bSdanmcd * node. 60288810c16bSdanmcd */ 60298810c16bSdanmcd ipsec_tun_pol_t * 6030f4b3ec61Sdh155122 create_tunnel_policy(char *name, int *errno, uint64_t *gen, netstack_t *ns) 60318810c16bSdanmcd { 60328810c16bSdanmcd ipsec_tun_pol_t *newbie, *existing; 60338810c16bSdanmcd avl_index_t where; 6034f4b3ec61Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 60358810c16bSdanmcd 60368810c16bSdanmcd newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP); 60378810c16bSdanmcd if (newbie == NULL) { 60388810c16bSdanmcd *errno = ENOMEM; 60398810c16bSdanmcd return (NULL); 60408810c16bSdanmcd } 60418810c16bSdanmcd if (!ipsec_fragcache_init(&newbie->itp_fragcache)) { 60428810c16bSdanmcd kmem_free(newbie, sizeof (*newbie)); 60438810c16bSdanmcd *errno = ENOMEM; 60448810c16bSdanmcd return (NULL); 60458810c16bSdanmcd } 60468810c16bSdanmcd 60478810c16bSdanmcd (void) strncpy(newbie->itp_name, name, LIFNAMSIZ); 60488810c16bSdanmcd 6049f4b3ec61Sdh155122 rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_WRITER); 6050f4b3ec61Sdh155122 existing = (ipsec_tun_pol_t *)avl_find(&ipss->ipsec_tunnel_policies, 6051f4b3ec61Sdh155122 newbie, &where); 60528810c16bSdanmcd if (existing != NULL) { 6053f4b3ec61Sdh155122 itp_free(newbie, ns); 60548810c16bSdanmcd *errno = EEXIST; 6055f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 60568810c16bSdanmcd return (NULL); 60578810c16bSdanmcd } 6058f4b3ec61Sdh155122 ipss->ipsec_tunnel_policy_gen++; 6059f4b3ec61Sdh155122 *gen = ipss->ipsec_tunnel_policy_gen; 60608810c16bSdanmcd newbie->itp_refcnt = 2; /* One for the caller, one for the tree. */ 60618810c16bSdanmcd newbie->itp_next_policy_index = 1; 6062f4b3ec61Sdh155122 avl_insert(&ipss->ipsec_tunnel_policies, newbie, where); 60638810c16bSdanmcd mutex_init(&newbie->itp_lock, NULL, MUTEX_DEFAULT, NULL); 60648810c16bSdanmcd newbie->itp_policy = kmem_zalloc(sizeof (ipsec_policy_head_t), 60658810c16bSdanmcd KM_NOSLEEP); 60668810c16bSdanmcd if (newbie->itp_policy == NULL) 60678810c16bSdanmcd goto nomem; 60688810c16bSdanmcd newbie->itp_inactive = kmem_zalloc(sizeof (ipsec_policy_head_t), 60698810c16bSdanmcd KM_NOSLEEP); 60708810c16bSdanmcd if (newbie->itp_inactive == NULL) { 6071489e6c7eSdanmcd kmem_free(newbie->itp_policy, sizeof (ipsec_policy_head_t)); 60728810c16bSdanmcd goto nomem; 60738810c16bSdanmcd } 60748810c16bSdanmcd 6075f4b3ec61Sdh155122 if (!tunnel_polhead_init(newbie->itp_policy, ns)) { 60768810c16bSdanmcd kmem_free(newbie->itp_policy, sizeof (ipsec_policy_head_t)); 60778810c16bSdanmcd kmem_free(newbie->itp_inactive, sizeof (ipsec_policy_head_t)); 60788810c16bSdanmcd goto nomem; 6079f4b3ec61Sdh155122 } else if (!tunnel_polhead_init(newbie->itp_inactive, ns)) { 6080f4b3ec61Sdh155122 IPPH_REFRELE(newbie->itp_policy, ns); 60818810c16bSdanmcd kmem_free(newbie->itp_inactive, sizeof (ipsec_policy_head_t)); 60828810c16bSdanmcd goto nomem; 60838810c16bSdanmcd } 6084f4b3ec61Sdh155122 rw_exit(&ipss->ipsec_tunnel_policy_lock); 60858810c16bSdanmcd 60868810c16bSdanmcd return (newbie); 60878810c16bSdanmcd nomem: 60888810c16bSdanmcd *errno = ENOMEM; 60898810c16bSdanmcd kmem_free(newbie, sizeof (*newbie)); 60908810c16bSdanmcd return (NULL); 60918810c16bSdanmcd } 60928810c16bSdanmcd 60938810c16bSdanmcd /* 60942b24ab6bSSebastien Roy * Given two addresses, find a tunnel instance's IPsec policy heads. 60952b24ab6bSSebastien Roy * Returns NULL on failure. 60968810c16bSdanmcd */ 60978810c16bSdanmcd ipsec_tun_pol_t * 60982b24ab6bSSebastien Roy itp_get_byaddr(uint32_t *laddr, uint32_t *faddr, int af, ip_stack_t *ipst) 60998810c16bSdanmcd { 61002b24ab6bSSebastien Roy conn_t *connp; 61012b24ab6bSSebastien Roy iptun_t *iptun; 61022b24ab6bSSebastien Roy ipsec_tun_pol_t *itp = NULL; 61032b24ab6bSSebastien Roy 61042b24ab6bSSebastien Roy /* Classifiers are used to "src" being foreign. */ 61052b24ab6bSSebastien Roy if (af == AF_INET) { 61062b24ab6bSSebastien Roy connp = ipcl_iptun_classify_v4((ipaddr_t *)faddr, 61072b24ab6bSSebastien Roy (ipaddr_t *)laddr, ipst); 61082b24ab6bSSebastien Roy } else { 61092b24ab6bSSebastien Roy ASSERT(af == AF_INET6); 61102b24ab6bSSebastien Roy ASSERT(!IN6_IS_ADDR_V4MAPPED((in6_addr_t *)laddr)); 61112b24ab6bSSebastien Roy ASSERT(!IN6_IS_ADDR_V4MAPPED((in6_addr_t *)faddr)); 61122b24ab6bSSebastien Roy connp = ipcl_iptun_classify_v6((in6_addr_t *)faddr, 61132b24ab6bSSebastien Roy (in6_addr_t *)laddr, ipst); 61142b24ab6bSSebastien Roy } 61152b24ab6bSSebastien Roy 61162b24ab6bSSebastien Roy if (connp == NULL) 61172b24ab6bSSebastien Roy return (NULL); 61182b24ab6bSSebastien Roy 61192b24ab6bSSebastien Roy if (IPCL_IS_IPTUN(connp)) { 61202b24ab6bSSebastien Roy iptun = connp->conn_iptun; 61212b24ab6bSSebastien Roy if (iptun != NULL) { 61222b24ab6bSSebastien Roy itp = iptun->iptun_itp; 61232b24ab6bSSebastien Roy if (itp != NULL) { 61242b24ab6bSSebastien Roy /* Braces due to the macro's nature... */ 61252b24ab6bSSebastien Roy ITP_REFHOLD(itp); 61262b24ab6bSSebastien Roy } 61272b24ab6bSSebastien Roy } /* Else itp is already NULL. */ 61282b24ab6bSSebastien Roy } 61292b24ab6bSSebastien Roy 61302b24ab6bSSebastien Roy CONN_DEC_REF(connp); 61312b24ab6bSSebastien Roy return (itp); 61328810c16bSdanmcd } 61338810c16bSdanmcd 61348810c16bSdanmcd /* 61358810c16bSdanmcd * Frag cache code, based on SunScreen 3.2 source 61368810c16bSdanmcd * screen/kernel/common/screen_fragcache.c 61378810c16bSdanmcd */ 61388810c16bSdanmcd 61398810c16bSdanmcd #define IPSEC_FRAG_TTL_MAX 5 61408810c16bSdanmcd /* 61418810c16bSdanmcd * Note that the following parameters create 256 hash buckets 61428810c16bSdanmcd * with 1024 free entries to be distributed. Things are cleaned 61438810c16bSdanmcd * periodically and are attempted to be cleaned when there is no 61448810c16bSdanmcd * free space, but this system errs on the side of dropping packets 61458810c16bSdanmcd * over creating memory exhaustion. We may decide to make hash 61468810c16bSdanmcd * factor a tunable if this proves to be a bad decision. 61478810c16bSdanmcd */ 61488810c16bSdanmcd #define IPSEC_FRAG_HASH_SLOTS (1<<8) 61498810c16bSdanmcd #define IPSEC_FRAG_HASH_FACTOR 4 61508810c16bSdanmcd #define IPSEC_FRAG_HASH_SIZE (IPSEC_FRAG_HASH_SLOTS * IPSEC_FRAG_HASH_FACTOR) 61518810c16bSdanmcd 61528810c16bSdanmcd #define IPSEC_FRAG_HASH_MASK (IPSEC_FRAG_HASH_SLOTS - 1) 61538810c16bSdanmcd #define IPSEC_FRAG_HASH_FUNC(id) (((id) & IPSEC_FRAG_HASH_MASK) ^ \ 61548810c16bSdanmcd (((id) / \ 61558810c16bSdanmcd (ushort_t)IPSEC_FRAG_HASH_SLOTS) & \ 61568810c16bSdanmcd IPSEC_FRAG_HASH_MASK)) 61578810c16bSdanmcd 61588810c16bSdanmcd /* Maximum fragments per packet. 48 bytes payload x 1366 packets > 64KB */ 61598810c16bSdanmcd #define IPSEC_MAX_FRAGS 1366 61608810c16bSdanmcd 61618810c16bSdanmcd #define V4_FRAG_OFFSET(ipha) ((ntohs(ipha->ipha_fragment_offset_and_flags) & \ 61628810c16bSdanmcd IPH_OFFSET) << 3) 61638810c16bSdanmcd #define V4_MORE_FRAGS(ipha) (ntohs(ipha->ipha_fragment_offset_and_flags) & \ 61648810c16bSdanmcd IPH_MF) 61658810c16bSdanmcd 61668810c16bSdanmcd /* 61678810c16bSdanmcd * Initialize an ipsec fragcache instance. 61688810c16bSdanmcd * Returns B_FALSE if memory allocation fails. 61698810c16bSdanmcd */ 61708810c16bSdanmcd boolean_t 61718810c16bSdanmcd ipsec_fragcache_init(ipsec_fragcache_t *frag) 61728810c16bSdanmcd { 61738810c16bSdanmcd ipsec_fragcache_entry_t *ftemp; 61748810c16bSdanmcd int i; 61758810c16bSdanmcd 61768810c16bSdanmcd mutex_init(&frag->itpf_lock, NULL, MUTEX_DEFAULT, NULL); 61778810c16bSdanmcd frag->itpf_ptr = (ipsec_fragcache_entry_t **) 6178daa41a61Sdanmcd kmem_zalloc(sizeof (ipsec_fragcache_entry_t *) * 61798810c16bSdanmcd IPSEC_FRAG_HASH_SLOTS, KM_NOSLEEP); 61808810c16bSdanmcd if (frag->itpf_ptr == NULL) 61818810c16bSdanmcd return (B_FALSE); 61828810c16bSdanmcd 61838810c16bSdanmcd ftemp = (ipsec_fragcache_entry_t *) 61848810c16bSdanmcd kmem_zalloc(sizeof (ipsec_fragcache_entry_t) * 61858810c16bSdanmcd IPSEC_FRAG_HASH_SIZE, KM_NOSLEEP); 61868810c16bSdanmcd if (ftemp == NULL) { 6187daa41a61Sdanmcd kmem_free(frag->itpf_ptr, sizeof (ipsec_fragcache_entry_t *) * 61888810c16bSdanmcd IPSEC_FRAG_HASH_SLOTS); 61898810c16bSdanmcd return (B_FALSE); 61908810c16bSdanmcd } 61918810c16bSdanmcd 61928810c16bSdanmcd frag->itpf_freelist = NULL; 61938810c16bSdanmcd 61948810c16bSdanmcd for (i = 0; i < IPSEC_FRAG_HASH_SIZE; i++) { 61958810c16bSdanmcd ftemp->itpfe_next = frag->itpf_freelist; 61968810c16bSdanmcd frag->itpf_freelist = ftemp; 61978810c16bSdanmcd ftemp++; 61988810c16bSdanmcd } 61998810c16bSdanmcd 62008810c16bSdanmcd frag->itpf_expire_hint = 0; 62018810c16bSdanmcd 62028810c16bSdanmcd return (B_TRUE); 62038810c16bSdanmcd } 62048810c16bSdanmcd 62058810c16bSdanmcd void 6206bd670b35SErik Nordmark ipsec_fragcache_uninit(ipsec_fragcache_t *frag, ipsec_stack_t *ipss) 62078810c16bSdanmcd { 62088810c16bSdanmcd ipsec_fragcache_entry_t *fep; 62098810c16bSdanmcd int i; 62108810c16bSdanmcd 62118810c16bSdanmcd mutex_enter(&frag->itpf_lock); 62128810c16bSdanmcd if (frag->itpf_ptr) { 62138810c16bSdanmcd /* Delete any existing fragcache entry chains */ 62148810c16bSdanmcd for (i = 0; i < IPSEC_FRAG_HASH_SLOTS; i++) { 62158810c16bSdanmcd fep = (frag->itpf_ptr)[i]; 62168810c16bSdanmcd while (fep != NULL) { 62178810c16bSdanmcd /* Returned fep is next in chain or NULL */ 6218bd670b35SErik Nordmark fep = fragcache_delentry(i, fep, frag, ipss); 62198810c16bSdanmcd } 62208810c16bSdanmcd } 62218810c16bSdanmcd /* 62228810c16bSdanmcd * Chase the pointers back to the beginning 62238810c16bSdanmcd * of the memory allocation and then 62248810c16bSdanmcd * get rid of the allocated freelist 62258810c16bSdanmcd */ 62268810c16bSdanmcd while (frag->itpf_freelist->itpfe_next != NULL) 62278810c16bSdanmcd frag->itpf_freelist = frag->itpf_freelist->itpfe_next; 62288810c16bSdanmcd /* 62298810c16bSdanmcd * XXX - If we ever dynamically grow the freelist 62308810c16bSdanmcd * then we'll have to free entries individually 62318810c16bSdanmcd * or determine how many entries or chunks we have 62328810c16bSdanmcd * grown since the initial allocation. 62338810c16bSdanmcd */ 62348810c16bSdanmcd kmem_free(frag->itpf_freelist, 62358810c16bSdanmcd sizeof (ipsec_fragcache_entry_t) * 62368810c16bSdanmcd IPSEC_FRAG_HASH_SIZE); 62378810c16bSdanmcd /* Free the fragcache structure */ 62388810c16bSdanmcd kmem_free(frag->itpf_ptr, 62398810c16bSdanmcd sizeof (ipsec_fragcache_entry_t *) * 62408810c16bSdanmcd IPSEC_FRAG_HASH_SLOTS); 62418810c16bSdanmcd } 62428810c16bSdanmcd mutex_exit(&frag->itpf_lock); 62438810c16bSdanmcd mutex_destroy(&frag->itpf_lock); 62448810c16bSdanmcd } 62458810c16bSdanmcd 62468810c16bSdanmcd /* 62478810c16bSdanmcd * Add a fragment to the fragment cache. Consumes mp if NULL is returned. 62488810c16bSdanmcd * Returns mp if a whole fragment has been assembled, NULL otherwise 6249bd670b35SErik Nordmark * The returned mp could be a b_next chain of fragments. 6250bd670b35SErik Nordmark * 6251bd670b35SErik Nordmark * The iramp argument is set on inbound; NULL if outbound. 62528810c16bSdanmcd */ 62538810c16bSdanmcd mblk_t * 6254bd670b35SErik Nordmark ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *iramp, mblk_t *mp, 6255f4b3ec61Sdh155122 int outer_hdr_len, ipsec_stack_t *ipss) 62568810c16bSdanmcd { 62578810c16bSdanmcd boolean_t is_v4; 62588810c16bSdanmcd time_t itpf_time; 62598810c16bSdanmcd ipha_t *iph; 62608810c16bSdanmcd ipha_t *oiph; 62618810c16bSdanmcd ip6_t *ip6h = NULL; 62628810c16bSdanmcd uint8_t v6_proto; 62638810c16bSdanmcd uint8_t *v6_proto_p; 62648810c16bSdanmcd uint16_t ip6_hdr_length; 6265bd670b35SErik Nordmark ip_pkt_t ipp; 62668810c16bSdanmcd ip6_frag_t *fraghdr; 62678810c16bSdanmcd ipsec_fragcache_entry_t *fep; 62688810c16bSdanmcd int i; 626905d90a32SDan McDonald mblk_t *nmp, *prevmp; 62708810c16bSdanmcd int firstbyte, lastbyte; 62718810c16bSdanmcd int offset; 62728810c16bSdanmcd int last; 6273bd670b35SErik Nordmark boolean_t inbound = (iramp != NULL); 62748810c16bSdanmcd 6275d1a98e54SPaul Wernau #ifdef FRAGCACHE_DEBUG 6276d1a98e54SPaul Wernau cmn_err(CE_WARN, "Fragcache: %s\n", inbound ? "INBOUND" : "OUTBOUND"); 6277d1a98e54SPaul Wernau #endif 627805d90a32SDan McDonald /* 627905d90a32SDan McDonald * You're on the slow path, so insure that every packet in the 628005d90a32SDan McDonald * cache is a single-mblk one. 628105d90a32SDan McDonald */ 628205d90a32SDan McDonald if (mp->b_cont != NULL) { 628305d90a32SDan McDonald nmp = msgpullup(mp, -1); 628405d90a32SDan McDonald if (nmp == NULL) { 6285bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6286f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 6287f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6288bd670b35SErik Nordmark if (inbound) 6289bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 62908810c16bSdanmcd return (NULL); 62918810c16bSdanmcd } 629205d90a32SDan McDonald freemsg(mp); 629305d90a32SDan McDonald mp = nmp; 629405d90a32SDan McDonald } 62958810c16bSdanmcd 629605d90a32SDan McDonald mutex_enter(&frag->itpf_lock); 629705d90a32SDan McDonald 629805d90a32SDan McDonald oiph = (ipha_t *)mp->b_rptr; 629905d90a32SDan McDonald iph = (ipha_t *)(mp->b_rptr + outer_hdr_len); 630005d90a32SDan McDonald 630105d90a32SDan McDonald if (IPH_HDR_VERSION(iph) == IPV4_VERSION) { 630205d90a32SDan McDonald is_v4 = B_TRUE; 630305d90a32SDan McDonald } else { 630405d90a32SDan McDonald ASSERT(IPH_HDR_VERSION(iph) == IPV6_VERSION); 630505d90a32SDan McDonald ip6h = (ip6_t *)(mp->b_rptr + outer_hdr_len); 630605d90a32SDan McDonald 630705d90a32SDan McDonald if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ip6_hdr_length, 63088810c16bSdanmcd &v6_proto_p)) { 63098810c16bSdanmcd /* 63108810c16bSdanmcd * Find upper layer protocol. 63118810c16bSdanmcd * If it fails we have a malformed packet 63128810c16bSdanmcd */ 63138810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6314bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6315f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_packet), 6316f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6317bd670b35SErik Nordmark if (inbound) 6318bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 63198810c16bSdanmcd return (NULL); 63208810c16bSdanmcd } else { 63218810c16bSdanmcd v6_proto = *v6_proto_p; 63228810c16bSdanmcd } 63238810c16bSdanmcd 63248810c16bSdanmcd 63258810c16bSdanmcd bzero(&ipp, sizeof (ipp)); 6326bd670b35SErik Nordmark (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &ipp, NULL); 63278810c16bSdanmcd if (!(ipp.ipp_fields & IPPF_FRAGHDR)) { 63288810c16bSdanmcd /* 63298810c16bSdanmcd * We think this is a fragment, but didn't find 63308810c16bSdanmcd * a fragment header. Something is wrong. 63318810c16bSdanmcd */ 63328810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6333bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6334f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_frag), 6335f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6336bd670b35SErik Nordmark if (inbound) 6337bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 63388810c16bSdanmcd return (NULL); 63398810c16bSdanmcd } 63408810c16bSdanmcd fraghdr = ipp.ipp_fraghdr; 63418810c16bSdanmcd is_v4 = B_FALSE; 63428810c16bSdanmcd } 63438810c16bSdanmcd 63448810c16bSdanmcd /* Anything to cleanup? */ 63458810c16bSdanmcd 63468810c16bSdanmcd /* 63478810c16bSdanmcd * This cleanup call could be put in a timer loop 63488810c16bSdanmcd * but it may actually be just as reasonable a decision to 63498810c16bSdanmcd * leave it here. The disadvantage is this only gets called when 63508810c16bSdanmcd * frags are added. The advantage is that it is not 63518810c16bSdanmcd * susceptible to race conditions like a time-based cleanup 63528810c16bSdanmcd * may be. 63538810c16bSdanmcd */ 63548810c16bSdanmcd itpf_time = gethrestime_sec(); 63558810c16bSdanmcd if (itpf_time >= frag->itpf_expire_hint) 6356bd670b35SErik Nordmark ipsec_fragcache_clean(frag, ipss); 63578810c16bSdanmcd 63588810c16bSdanmcd /* Lookup to see if there is an existing entry */ 63598810c16bSdanmcd 63608810c16bSdanmcd if (is_v4) 63618810c16bSdanmcd i = IPSEC_FRAG_HASH_FUNC(iph->ipha_ident); 63628810c16bSdanmcd else 63638810c16bSdanmcd i = IPSEC_FRAG_HASH_FUNC(fraghdr->ip6f_ident); 63648810c16bSdanmcd 63658810c16bSdanmcd for (fep = (frag->itpf_ptr)[i]; fep; fep = fep->itpfe_next) { 63668810c16bSdanmcd if (is_v4) { 63678810c16bSdanmcd ASSERT(iph != NULL); 63688810c16bSdanmcd if ((fep->itpfe_id == iph->ipha_ident) && 63698810c16bSdanmcd (fep->itpfe_src == iph->ipha_src) && 63708810c16bSdanmcd (fep->itpfe_dst == iph->ipha_dst) && 63718810c16bSdanmcd (fep->itpfe_proto == iph->ipha_protocol)) 63728810c16bSdanmcd break; 63738810c16bSdanmcd } else { 63748810c16bSdanmcd ASSERT(fraghdr != NULL); 63758810c16bSdanmcd ASSERT(fep != NULL); 63768810c16bSdanmcd if ((fep->itpfe_id == fraghdr->ip6f_ident) && 63778810c16bSdanmcd IN6_ARE_ADDR_EQUAL(&fep->itpfe_src6, 63788810c16bSdanmcd &ip6h->ip6_src) && 63798810c16bSdanmcd IN6_ARE_ADDR_EQUAL(&fep->itpfe_dst6, 63808810c16bSdanmcd &ip6h->ip6_dst) && (fep->itpfe_proto == v6_proto)) 63818810c16bSdanmcd break; 63828810c16bSdanmcd } 63838810c16bSdanmcd } 63848810c16bSdanmcd 63858810c16bSdanmcd if (is_v4) { 63868810c16bSdanmcd firstbyte = V4_FRAG_OFFSET(iph); 63878810c16bSdanmcd lastbyte = firstbyte + ntohs(iph->ipha_length) - 63888810c16bSdanmcd IPH_HDR_LENGTH(iph); 63898810c16bSdanmcd last = (V4_MORE_FRAGS(iph) == 0); 63908810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 63918810c16bSdanmcd cmn_err(CE_WARN, "V4 fragcache: firstbyte = %d, lastbyte = %d, " 6392d1a98e54SPaul Wernau "is_last_frag = %d, id = %d, mp = %p\n", firstbyte, 6393d1a98e54SPaul Wernau lastbyte, last, iph->ipha_ident, mp); 63948810c16bSdanmcd #endif 63958810c16bSdanmcd } else { 63968810c16bSdanmcd firstbyte = ntohs(fraghdr->ip6f_offlg & IP6F_OFF_MASK); 63978810c16bSdanmcd lastbyte = firstbyte + ntohs(ip6h->ip6_plen) + 63988810c16bSdanmcd sizeof (ip6_t) - ip6_hdr_length; 63998810c16bSdanmcd last = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG) == 0; 64008810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 64018810c16bSdanmcd cmn_err(CE_WARN, "V6 fragcache: firstbyte = %d, lastbyte = %d, " 6402d1a98e54SPaul Wernau "is_last_frag = %d, id = %d, fraghdr = %p, mp = %p\n", 6403d1a98e54SPaul Wernau firstbyte, lastbyte, last, fraghdr->ip6f_ident, fraghdr, 6404d1a98e54SPaul Wernau mp); 64058810c16bSdanmcd #endif 64068810c16bSdanmcd } 64078810c16bSdanmcd 64088810c16bSdanmcd /* check for bogus fragments and delete the entry */ 64098810c16bSdanmcd if (firstbyte > 0 && firstbyte <= 8) { 64108810c16bSdanmcd if (fep != NULL) 6411bd670b35SErik Nordmark (void) fragcache_delentry(i, fep, frag, ipss); 64128810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6413bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6414f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_frag), 6415f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6416bd670b35SErik Nordmark if (inbound) 6417bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 64188810c16bSdanmcd return (NULL); 64198810c16bSdanmcd } 64208810c16bSdanmcd 64218810c16bSdanmcd /* Not found, allocate a new entry */ 64228810c16bSdanmcd if (fep == NULL) { 64238810c16bSdanmcd if (frag->itpf_freelist == NULL) { 64248810c16bSdanmcd /* see if there is some space */ 6425bd670b35SErik Nordmark ipsec_fragcache_clean(frag, ipss); 64268810c16bSdanmcd if (frag->itpf_freelist == NULL) { 64278810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6428bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6429f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_nomem), 6430f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6431bd670b35SErik Nordmark if (inbound) 6432bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 64338810c16bSdanmcd return (NULL); 64348810c16bSdanmcd } 64358810c16bSdanmcd } 64368810c16bSdanmcd 64378810c16bSdanmcd fep = frag->itpf_freelist; 64388810c16bSdanmcd frag->itpf_freelist = fep->itpfe_next; 64398810c16bSdanmcd 64408810c16bSdanmcd if (is_v4) { 64418810c16bSdanmcd bcopy((caddr_t)&iph->ipha_src, (caddr_t)&fep->itpfe_src, 64428810c16bSdanmcd sizeof (struct in_addr)); 64438810c16bSdanmcd bcopy((caddr_t)&iph->ipha_dst, (caddr_t)&fep->itpfe_dst, 64448810c16bSdanmcd sizeof (struct in_addr)); 64458810c16bSdanmcd fep->itpfe_id = iph->ipha_ident; 64468810c16bSdanmcd fep->itpfe_proto = iph->ipha_protocol; 64478810c16bSdanmcd i = IPSEC_FRAG_HASH_FUNC(fep->itpfe_id); 64488810c16bSdanmcd } else { 64498810c16bSdanmcd bcopy((in6_addr_t *)&ip6h->ip6_src, 64508810c16bSdanmcd (in6_addr_t *)&fep->itpfe_src6, 64518810c16bSdanmcd sizeof (struct in6_addr)); 64528810c16bSdanmcd bcopy((in6_addr_t *)&ip6h->ip6_dst, 64538810c16bSdanmcd (in6_addr_t *)&fep->itpfe_dst6, 64548810c16bSdanmcd sizeof (struct in6_addr)); 64558810c16bSdanmcd fep->itpfe_id = fraghdr->ip6f_ident; 64568810c16bSdanmcd fep->itpfe_proto = v6_proto; 64578810c16bSdanmcd i = IPSEC_FRAG_HASH_FUNC(fep->itpfe_id); 64588810c16bSdanmcd } 64598810c16bSdanmcd itpf_time = gethrestime_sec(); 64608810c16bSdanmcd fep->itpfe_exp = itpf_time + IPSEC_FRAG_TTL_MAX + 1; 64618810c16bSdanmcd fep->itpfe_last = 0; 64628810c16bSdanmcd fep->itpfe_fraglist = NULL; 64638810c16bSdanmcd fep->itpfe_depth = 0; 64648810c16bSdanmcd fep->itpfe_next = (frag->itpf_ptr)[i]; 64658810c16bSdanmcd (frag->itpf_ptr)[i] = fep; 64668810c16bSdanmcd 64678810c16bSdanmcd if (frag->itpf_expire_hint > fep->itpfe_exp) 64688810c16bSdanmcd frag->itpf_expire_hint = fep->itpfe_exp; 64698810c16bSdanmcd 64708810c16bSdanmcd } 64718810c16bSdanmcd 64728810c16bSdanmcd /* Insert it in the frag list */ 64738810c16bSdanmcd /* List is in order by starting offset of fragments */ 64748810c16bSdanmcd 64758810c16bSdanmcd prevmp = NULL; 64768810c16bSdanmcd for (nmp = fep->itpfe_fraglist; nmp; nmp = nmp->b_next) { 64778810c16bSdanmcd ipha_t *niph; 64788810c16bSdanmcd ipha_t *oniph; 64798810c16bSdanmcd ip6_t *nip6h; 6480bd670b35SErik Nordmark ip_pkt_t nipp; 64818810c16bSdanmcd ip6_frag_t *nfraghdr; 64828810c16bSdanmcd uint16_t nip6_hdr_length; 64838810c16bSdanmcd uint8_t *nv6_proto_p; 64848810c16bSdanmcd int nfirstbyte, nlastbyte; 64858810c16bSdanmcd char *data, *ndata; 64868810c16bSdanmcd mblk_t *ndata_mp = (inbound ? nmp->b_cont : nmp); 64878810c16bSdanmcd int hdr_len; 64888810c16bSdanmcd 64898810c16bSdanmcd oniph = (ipha_t *)mp->b_rptr; 64908810c16bSdanmcd nip6h = NULL; 64918810c16bSdanmcd niph = NULL; 64928810c16bSdanmcd 64938810c16bSdanmcd /* 64948810c16bSdanmcd * Determine outer header type and length and set 64958810c16bSdanmcd * pointers appropriately 64968810c16bSdanmcd */ 64978810c16bSdanmcd 64988810c16bSdanmcd if (IPH_HDR_VERSION(oniph) == IPV4_VERSION) { 64998810c16bSdanmcd hdr_len = ((outer_hdr_len != 0) ? 65008810c16bSdanmcd IPH_HDR_LENGTH(oiph) : 0); 65018810c16bSdanmcd niph = (ipha_t *)(ndata_mp->b_rptr + hdr_len); 65028810c16bSdanmcd } else { 65038810c16bSdanmcd ASSERT(IPH_HDR_VERSION(oniph) == IPV6_VERSION); 650405d90a32SDan McDonald ASSERT(ndata_mp->b_cont == NULL); 650505d90a32SDan McDonald nip6h = (ip6_t *)ndata_mp->b_rptr; 650605d90a32SDan McDonald (void) ip_hdr_length_nexthdr_v6(ndata_mp, nip6h, 65078810c16bSdanmcd &nip6_hdr_length, &v6_proto_p); 65088810c16bSdanmcd hdr_len = ((outer_hdr_len != 0) ? nip6_hdr_length : 0); 65098810c16bSdanmcd } 65108810c16bSdanmcd 65118810c16bSdanmcd /* 65128810c16bSdanmcd * Determine inner header type and length and set 65138810c16bSdanmcd * pointers appropriately 65148810c16bSdanmcd */ 65158810c16bSdanmcd 65168810c16bSdanmcd if (is_v4) { 65178810c16bSdanmcd if (niph == NULL) { 65188810c16bSdanmcd /* Was v6 outer */ 65198810c16bSdanmcd niph = (ipha_t *)(ndata_mp->b_rptr + hdr_len); 65208810c16bSdanmcd } 65218810c16bSdanmcd nfirstbyte = V4_FRAG_OFFSET(niph); 65228810c16bSdanmcd nlastbyte = nfirstbyte + ntohs(niph->ipha_length) - 65238810c16bSdanmcd IPH_HDR_LENGTH(niph); 65248810c16bSdanmcd } else { 652505d90a32SDan McDonald ASSERT(ndata_mp->b_cont == NULL); 652605d90a32SDan McDonald nip6h = (ip6_t *)(ndata_mp->b_rptr + hdr_len); 652705d90a32SDan McDonald if (!ip_hdr_length_nexthdr_v6(ndata_mp, nip6h, 65288810c16bSdanmcd &nip6_hdr_length, &nv6_proto_p)) { 65298810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6530bd670b35SErik Nordmark ip_drop_packet_chain(nmp, inbound, NULL, 6531f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_frag), 6532f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 653305d90a32SDan McDonald ipsec_freemsg_chain(ndata_mp); 6534bd670b35SErik Nordmark if (inbound) 6535bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 65368810c16bSdanmcd return (NULL); 65378810c16bSdanmcd } 65388810c16bSdanmcd bzero(&nipp, sizeof (nipp)); 6539bd670b35SErik Nordmark (void) ip_find_hdr_v6(ndata_mp, nip6h, B_FALSE, &nipp, 6540bd670b35SErik Nordmark NULL); 65418810c16bSdanmcd nfraghdr = nipp.ipp_fraghdr; 65428810c16bSdanmcd nfirstbyte = ntohs(nfraghdr->ip6f_offlg & 65438810c16bSdanmcd IP6F_OFF_MASK); 65448810c16bSdanmcd nlastbyte = nfirstbyte + ntohs(nip6h->ip6_plen) + 65458810c16bSdanmcd sizeof (ip6_t) - nip6_hdr_length; 65468810c16bSdanmcd } 65478810c16bSdanmcd 65488810c16bSdanmcd /* Check for overlapping fragments */ 65498810c16bSdanmcd if (firstbyte >= nfirstbyte && firstbyte < nlastbyte) { 65508810c16bSdanmcd /* 65518810c16bSdanmcd * Overlap Check: 65528810c16bSdanmcd * ~~~~--------- # Check if the newly 65538810c16bSdanmcd * ~ ndata_mp| # received fragment 65548810c16bSdanmcd * ~~~~--------- # overlaps with the 65558810c16bSdanmcd * ---------~~~~~~ # current fragment. 65568810c16bSdanmcd * | mp ~ 65578810c16bSdanmcd * ---------~~~~~~ 65588810c16bSdanmcd */ 65598810c16bSdanmcd if (is_v4) { 65608810c16bSdanmcd data = (char *)iph + IPH_HDR_LENGTH(iph) + 65618810c16bSdanmcd firstbyte - nfirstbyte; 65628810c16bSdanmcd ndata = (char *)niph + IPH_HDR_LENGTH(niph); 65638810c16bSdanmcd } else { 65648810c16bSdanmcd data = (char *)ip6h + 65658810c16bSdanmcd nip6_hdr_length + firstbyte - 65668810c16bSdanmcd nfirstbyte; 65678810c16bSdanmcd ndata = (char *)nip6h + nip6_hdr_length; 65688810c16bSdanmcd } 6569daa41a61Sdanmcd if (bcmp(data, ndata, MIN(lastbyte, nlastbyte) - 6570daa41a61Sdanmcd firstbyte)) { 65718810c16bSdanmcd /* Overlapping data does not match */ 6572bd670b35SErik Nordmark (void) fragcache_delentry(i, fep, frag, ipss); 65738810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6574bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6575f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_overlap_frag), 6576f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6577bd670b35SErik Nordmark if (inbound) 6578bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 65798810c16bSdanmcd return (NULL); 65808810c16bSdanmcd } 65818810c16bSdanmcd /* Part of defense for jolt2.c fragmentation attack */ 65828810c16bSdanmcd if (firstbyte >= nfirstbyte && lastbyte <= nlastbyte) { 65838810c16bSdanmcd /* 65848810c16bSdanmcd * Check for identical or subset fragments: 65858810c16bSdanmcd * ---------- ~~~~--------~~~~~ 65868810c16bSdanmcd * | nmp | or ~ nmp ~ 65878810c16bSdanmcd * ---------- ~~~~--------~~~~~ 65888810c16bSdanmcd * ---------- ------ 65898810c16bSdanmcd * | mp | | mp | 65908810c16bSdanmcd * ---------- ------ 65918810c16bSdanmcd */ 65928810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6593bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6594f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_evil_frag), 6595f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6596bd670b35SErik Nordmark if (inbound) 6597bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(iramp); 65988810c16bSdanmcd return (NULL); 65998810c16bSdanmcd } 66008810c16bSdanmcd 66018810c16bSdanmcd } 66028810c16bSdanmcd 66038810c16bSdanmcd /* Correct location for this fragment? */ 66048810c16bSdanmcd if (firstbyte <= nfirstbyte) { 66058810c16bSdanmcd /* 66068810c16bSdanmcd * Check if the tail end of the new fragment overlaps 66078810c16bSdanmcd * with the head of the current fragment. 66088810c16bSdanmcd * --------~~~~~~~ 66098810c16bSdanmcd * | nmp ~ 66108810c16bSdanmcd * --------~~~~~~~ 66118810c16bSdanmcd * ~~~~~-------- 66128810c16bSdanmcd * ~ mp | 66138810c16bSdanmcd * ~~~~~-------- 66148810c16bSdanmcd */ 66158810c16bSdanmcd if (lastbyte > nfirstbyte) { 66168810c16bSdanmcd /* Fragments overlap */ 66178810c16bSdanmcd data = (char *)iph + IPH_HDR_LENGTH(iph) + 66188810c16bSdanmcd firstbyte - nfirstbyte; 66198810c16bSdanmcd ndata = (char *)niph + IPH_HDR_LENGTH(niph); 66208810c16bSdanmcd if (is_v4) { 66218810c16bSdanmcd data = (char *)iph + 66228810c16bSdanmcd IPH_HDR_LENGTH(iph) + firstbyte - 66238810c16bSdanmcd nfirstbyte; 66248810c16bSdanmcd ndata = (char *)niph + 66258810c16bSdanmcd IPH_HDR_LENGTH(niph); 66268810c16bSdanmcd } else { 66278810c16bSdanmcd data = (char *)ip6h + 66288810c16bSdanmcd nip6_hdr_length + firstbyte - 66298810c16bSdanmcd nfirstbyte; 66308810c16bSdanmcd ndata = (char *)nip6h + nip6_hdr_length; 66318810c16bSdanmcd } 66328810c16bSdanmcd if (bcmp(data, ndata, MIN(lastbyte, nlastbyte) 66338810c16bSdanmcd - nfirstbyte)) { 66348810c16bSdanmcd /* Overlap mismatch */ 6635bd670b35SErik Nordmark (void) fragcache_delentry(i, fep, frag, 6636bd670b35SErik Nordmark ipss); 66378810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6638bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6639bd670b35SErik Nordmark DROPPER(ipss, 6640daa41a61Sdanmcd ipds_spd_overlap_frag), 6641f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 6642bd670b35SErik Nordmark if (inbound) { 6643bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk( 6644bd670b35SErik Nordmark iramp); 6645bd670b35SErik Nordmark } 66468810c16bSdanmcd return (NULL); 66478810c16bSdanmcd } 66488810c16bSdanmcd } 66498810c16bSdanmcd 66508810c16bSdanmcd /* 66518810c16bSdanmcd * Fragment does not illegally overlap and can now 66528810c16bSdanmcd * be inserted into the chain 66538810c16bSdanmcd */ 66548810c16bSdanmcd break; 66558810c16bSdanmcd } 66568810c16bSdanmcd 66578810c16bSdanmcd prevmp = nmp; 66588810c16bSdanmcd } 6659bd670b35SErik Nordmark /* Prepend the attributes before we link it in */ 6660bd670b35SErik Nordmark if (iramp != NULL) { 6661bd670b35SErik Nordmark ASSERT(iramp->b_cont == NULL); 6662bd670b35SErik Nordmark iramp->b_cont = mp; 6663bd670b35SErik Nordmark mp = iramp; 6664bd670b35SErik Nordmark iramp = NULL; 6665bd670b35SErik Nordmark } 6666bd670b35SErik Nordmark mp->b_next = nmp; 66678810c16bSdanmcd 66688810c16bSdanmcd if (prevmp == NULL) { 6669bd670b35SErik Nordmark fep->itpfe_fraglist = mp; 66708810c16bSdanmcd } else { 6671bd670b35SErik Nordmark prevmp->b_next = mp; 66728810c16bSdanmcd } 66738810c16bSdanmcd if (last) 66748810c16bSdanmcd fep->itpfe_last = 1; 66758810c16bSdanmcd 66768810c16bSdanmcd /* Part of defense for jolt2.c fragmentation attack */ 66778810c16bSdanmcd if (++(fep->itpfe_depth) > IPSEC_MAX_FRAGS) { 6678bd670b35SErik Nordmark (void) fragcache_delentry(i, fep, frag, ipss); 66798810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6680bd670b35SErik Nordmark if (inbound) 6681bd670b35SErik Nordmark mp = ip_recv_attr_free_mblk(mp); 6682bd670b35SErik Nordmark 6683bd670b35SErik Nordmark ip_drop_packet(mp, inbound, NULL, 6684f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_max_frags), 6685f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 66868810c16bSdanmcd return (NULL); 66878810c16bSdanmcd } 66888810c16bSdanmcd 66898810c16bSdanmcd /* Check for complete packet */ 66908810c16bSdanmcd 66918810c16bSdanmcd if (!fep->itpfe_last) { 66928810c16bSdanmcd mutex_exit(&frag->itpf_lock); 66938810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 6694d1a98e54SPaul Wernau cmn_err(CE_WARN, "Fragment cached, last not yet seen.\n"); 66958810c16bSdanmcd #endif 66968810c16bSdanmcd return (NULL); 66978810c16bSdanmcd } 66988810c16bSdanmcd 66998810c16bSdanmcd offset = 0; 67008810c16bSdanmcd for (mp = fep->itpfe_fraglist; mp; mp = mp->b_next) { 67018810c16bSdanmcd mblk_t *data_mp = (inbound ? mp->b_cont : mp); 67028810c16bSdanmcd int hdr_len; 67038810c16bSdanmcd 67048810c16bSdanmcd oiph = (ipha_t *)data_mp->b_rptr; 67058810c16bSdanmcd ip6h = NULL; 67068810c16bSdanmcd iph = NULL; 67078810c16bSdanmcd 67088810c16bSdanmcd if (IPH_HDR_VERSION(oiph) == IPV4_VERSION) { 67098810c16bSdanmcd hdr_len = ((outer_hdr_len != 0) ? 67108810c16bSdanmcd IPH_HDR_LENGTH(oiph) : 0); 67118810c16bSdanmcd iph = (ipha_t *)(data_mp->b_rptr + hdr_len); 67128810c16bSdanmcd } else { 67138810c16bSdanmcd ASSERT(IPH_HDR_VERSION(oiph) == IPV6_VERSION); 671405d90a32SDan McDonald ASSERT(data_mp->b_cont == NULL); 671505d90a32SDan McDonald ip6h = (ip6_t *)data_mp->b_rptr; 671605d90a32SDan McDonald (void) ip_hdr_length_nexthdr_v6(data_mp, ip6h, 67178810c16bSdanmcd &ip6_hdr_length, &v6_proto_p); 67188810c16bSdanmcd hdr_len = ((outer_hdr_len != 0) ? ip6_hdr_length : 0); 67198810c16bSdanmcd } 67208810c16bSdanmcd 67218810c16bSdanmcd /* Calculate current fragment start/end */ 67228810c16bSdanmcd if (is_v4) { 67238810c16bSdanmcd if (iph == NULL) { 67248810c16bSdanmcd /* Was v6 outer */ 67258810c16bSdanmcd iph = (ipha_t *)(data_mp->b_rptr + hdr_len); 67268810c16bSdanmcd } 67278810c16bSdanmcd firstbyte = V4_FRAG_OFFSET(iph); 67288810c16bSdanmcd lastbyte = firstbyte + ntohs(iph->ipha_length) - 67298810c16bSdanmcd IPH_HDR_LENGTH(iph); 67308810c16bSdanmcd } else { 673105d90a32SDan McDonald ASSERT(data_mp->b_cont == NULL); 673205d90a32SDan McDonald ip6h = (ip6_t *)(data_mp->b_rptr + hdr_len); 673305d90a32SDan McDonald if (!ip_hdr_length_nexthdr_v6(data_mp, ip6h, 67348810c16bSdanmcd &ip6_hdr_length, &v6_proto_p)) { 67358810c16bSdanmcd mutex_exit(&frag->itpf_lock); 6736bd670b35SErik Nordmark ip_drop_packet_chain(mp, inbound, NULL, 6737f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_malformed_frag), 6738f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 67398810c16bSdanmcd return (NULL); 67408810c16bSdanmcd } 67418810c16bSdanmcd v6_proto = *v6_proto_p; 67428810c16bSdanmcd bzero(&ipp, sizeof (ipp)); 6743bd670b35SErik Nordmark (void) ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp, 6744bd670b35SErik Nordmark NULL); 67458810c16bSdanmcd fraghdr = ipp.ipp_fraghdr; 67468810c16bSdanmcd firstbyte = ntohs(fraghdr->ip6f_offlg & 67478810c16bSdanmcd IP6F_OFF_MASK); 67488810c16bSdanmcd lastbyte = firstbyte + ntohs(ip6h->ip6_plen) + 67498810c16bSdanmcd sizeof (ip6_t) - ip6_hdr_length; 67508810c16bSdanmcd } 67518810c16bSdanmcd 67528810c16bSdanmcd /* 67538810c16bSdanmcd * If this fragment is greater than current offset, 67548810c16bSdanmcd * we have a missing fragment so return NULL 67558810c16bSdanmcd */ 67568810c16bSdanmcd if (firstbyte > offset) { 67578810c16bSdanmcd mutex_exit(&frag->itpf_lock); 67588810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 67598810c16bSdanmcd /* 67608810c16bSdanmcd * Note, this can happen when the last frag 67618810c16bSdanmcd * gets sent through because it is smaller 67628810c16bSdanmcd * than the MTU. It is not necessarily an 67638810c16bSdanmcd * error condition. 67648810c16bSdanmcd */ 67658810c16bSdanmcd cmn_err(CE_WARN, "Frag greater than offset! : " 67668810c16bSdanmcd "missing fragment: firstbyte = %d, offset = %d, " 67678810c16bSdanmcd "mp = %p\n", firstbyte, offset, mp); 67688810c16bSdanmcd #endif 67698810c16bSdanmcd return (NULL); 67708810c16bSdanmcd } 6771d1a98e54SPaul Wernau #ifdef FRAGCACHE_DEBUG 6772d1a98e54SPaul Wernau cmn_err(CE_WARN, "Frag offsets : " 6773d1a98e54SPaul Wernau "firstbyte = %d, offset = %d, mp = %p\n", 6774d1a98e54SPaul Wernau firstbyte, offset, mp); 6775d1a98e54SPaul Wernau #endif 67768810c16bSdanmcd 67778810c16bSdanmcd /* 67788810c16bSdanmcd * If we are at the last fragment, we have the complete 67798810c16bSdanmcd * packet, so rechain things and return it to caller 67808810c16bSdanmcd * for processing 67818810c16bSdanmcd */ 67828810c16bSdanmcd 67838810c16bSdanmcd if ((is_v4 && !V4_MORE_FRAGS(iph)) || 67848810c16bSdanmcd (!is_v4 && !(fraghdr->ip6f_offlg & IP6F_MORE_FRAG))) { 67858810c16bSdanmcd mp = fep->itpfe_fraglist; 67868810c16bSdanmcd fep->itpfe_fraglist = NULL; 6787bd670b35SErik Nordmark (void) fragcache_delentry(i, fep, frag, ipss); 67888810c16bSdanmcd mutex_exit(&frag->itpf_lock); 67898810c16bSdanmcd 67908810c16bSdanmcd if ((is_v4 && (firstbyte + ntohs(iph->ipha_length) > 67918810c16bSdanmcd 65535)) || (!is_v4 && (firstbyte + 67928810c16bSdanmcd ntohs(ip6h->ip6_plen) > 65535))) { 67938810c16bSdanmcd /* It is an invalid "ping-o-death" packet */ 67948810c16bSdanmcd /* Discard it */ 6795bd670b35SErik Nordmark ip_drop_packet_chain(mp, inbound, NULL, 6796f4b3ec61Sdh155122 DROPPER(ipss, ipds_spd_evil_frag), 6797f4b3ec61Sdh155122 &ipss->ipsec_spd_dropper); 67988810c16bSdanmcd return (NULL); 67998810c16bSdanmcd } 68008810c16bSdanmcd #ifdef FRAGCACHE_DEBUG 68018810c16bSdanmcd cmn_err(CE_WARN, "Fragcache returning mp = %p, " 68028810c16bSdanmcd "mp->b_next = %p", mp, mp->b_next); 68038810c16bSdanmcd #endif 68048810c16bSdanmcd /* 6805bd670b35SErik Nordmark * For inbound case, mp has attrmp b_next'd chain 68068810c16bSdanmcd * For outbound case, it is just data mp chain 68078810c16bSdanmcd */ 68088810c16bSdanmcd return (mp); 68098810c16bSdanmcd } 68108810c16bSdanmcd 68118810c16bSdanmcd /* 68128810c16bSdanmcd * Update new ending offset if this 68138810c16bSdanmcd * fragment extends the packet 68148810c16bSdanmcd */ 68158810c16bSdanmcd if (offset < lastbyte) 68168810c16bSdanmcd offset = lastbyte; 68178810c16bSdanmcd } 68188810c16bSdanmcd 68198810c16bSdanmcd mutex_exit(&frag->itpf_lock); 68208810c16bSdanmcd 68218810c16bSdanmcd /* Didn't find last fragment, so return NULL */ 68228810c16bSdanmcd return (NULL); 68238810c16bSdanmcd } 68248810c16bSdanmcd 68258810c16bSdanmcd static void 6826bd670b35SErik Nordmark ipsec_fragcache_clean(ipsec_fragcache_t *frag, ipsec_stack_t *ipss) 68278810c16bSdanmcd { 68288810c16bSdanmcd ipsec_fragcache_entry_t *fep; 68298810c16bSdanmcd int i; 68308810c16bSdanmcd ipsec_fragcache_entry_t *earlyfep = NULL; 68318810c16bSdanmcd time_t itpf_time; 68328810c16bSdanmcd int earlyexp; 68338810c16bSdanmcd int earlyi = 0; 68348810c16bSdanmcd 68358810c16bSdanmcd ASSERT(MUTEX_HELD(&frag->itpf_lock)); 68368810c16bSdanmcd 68378810c16bSdanmcd itpf_time = gethrestime_sec(); 68388810c16bSdanmcd earlyexp = itpf_time + 10000; 68398810c16bSdanmcd 68408810c16bSdanmcd for (i = 0; i < IPSEC_FRAG_HASH_SLOTS; i++) { 68418810c16bSdanmcd fep = (frag->itpf_ptr)[i]; 68428810c16bSdanmcd while (fep) { 68438810c16bSdanmcd if (fep->itpfe_exp < itpf_time) { 68448810c16bSdanmcd /* found */ 6845bd670b35SErik Nordmark fep = fragcache_delentry(i, fep, frag, ipss); 68468810c16bSdanmcd } else { 68478810c16bSdanmcd if (fep->itpfe_exp < earlyexp) { 68488810c16bSdanmcd earlyfep = fep; 68498810c16bSdanmcd earlyexp = fep->itpfe_exp; 68508810c16bSdanmcd earlyi = i; 68518810c16bSdanmcd } 68528810c16bSdanmcd fep = fep->itpfe_next; 68538810c16bSdanmcd } 68548810c16bSdanmcd } 68558810c16bSdanmcd } 68568810c16bSdanmcd 68578810c16bSdanmcd frag->itpf_expire_hint = earlyexp; 68588810c16bSdanmcd 68598810c16bSdanmcd /* if (!found) */ 68608810c16bSdanmcd if (frag->itpf_freelist == NULL) 6861bd670b35SErik Nordmark (void) fragcache_delentry(earlyi, earlyfep, frag, ipss); 68628810c16bSdanmcd } 68638810c16bSdanmcd 68648810c16bSdanmcd static ipsec_fragcache_entry_t * 68658810c16bSdanmcd fragcache_delentry(int slot, ipsec_fragcache_entry_t *fep, 6866bd670b35SErik Nordmark ipsec_fragcache_t *frag, ipsec_stack_t *ipss) 68678810c16bSdanmcd { 68688810c16bSdanmcd ipsec_fragcache_entry_t *targp; 68698810c16bSdanmcd ipsec_fragcache_entry_t *nextp = fep->itpfe_next; 68708810c16bSdanmcd 68718810c16bSdanmcd ASSERT(MUTEX_HELD(&frag->itpf_lock)); 68728810c16bSdanmcd 68738810c16bSdanmcd /* Free up any fragment list still in cache entry */ 6874bd670b35SErik Nordmark if (fep->itpfe_fraglist != NULL) { 6875bd670b35SErik Nordmark ip_drop_packet_chain(fep->itpfe_fraglist, 6876bd670b35SErik Nordmark ip_recv_attr_is_mblk(fep->itpfe_fraglist), NULL, 6877d1a98e54SPaul Wernau DROPPER(ipss, ipds_spd_expired_frags), 6878d1a98e54SPaul Wernau &ipss->ipsec_spd_dropper); 6879bd670b35SErik Nordmark } 6880bd670b35SErik Nordmark fep->itpfe_fraglist = NULL; 68818810c16bSdanmcd 68828810c16bSdanmcd targp = (frag->itpf_ptr)[slot]; 68838810c16bSdanmcd ASSERT(targp != 0); 68848810c16bSdanmcd 68858810c16bSdanmcd if (targp == fep) { 68868810c16bSdanmcd /* unlink from head of hash chain */ 68878810c16bSdanmcd (frag->itpf_ptr)[slot] = nextp; 68888810c16bSdanmcd /* link into free list */ 68898810c16bSdanmcd fep->itpfe_next = frag->itpf_freelist; 68908810c16bSdanmcd frag->itpf_freelist = fep; 68918810c16bSdanmcd return (nextp); 68928810c16bSdanmcd } 68938810c16bSdanmcd 68948810c16bSdanmcd /* maybe should use double linked list to make update faster */ 68958810c16bSdanmcd /* must be past front of chain */ 68968810c16bSdanmcd while (targp) { 68978810c16bSdanmcd if (targp->itpfe_next == fep) { 68988810c16bSdanmcd /* unlink from hash chain */ 68998810c16bSdanmcd targp->itpfe_next = nextp; 69008810c16bSdanmcd /* link into free list */ 69018810c16bSdanmcd fep->itpfe_next = frag->itpf_freelist; 69028810c16bSdanmcd frag->itpf_freelist = fep; 69038810c16bSdanmcd return (nextp); 69048810c16bSdanmcd } 69058810c16bSdanmcd targp = targp->itpfe_next; 69068810c16bSdanmcd ASSERT(targp != 0); 69078810c16bSdanmcd } 69088810c16bSdanmcd /* NOTREACHED */ 69098810c16bSdanmcd return (NULL); 69108810c16bSdanmcd } 6911