1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/stream.h> 30 #include <sys/sysmacros.h> 31 #include <sys/callb.h> 32 #include <sys/ddi.h> 33 #include <sys/sunddi.h> 34 #include <sys/proc.h> 35 #include <sys/modctl.h> 36 #include <sys/disp.h> 37 #include <inet/ip.h> 38 #include <inet/ipsec_impl.h> 39 #include <inet/optcom.h> 40 #include <inet/keysock.h> 41 42 /* 43 * Loader commands for ipsec_loader_sig 44 */ 45 #define IPSEC_LOADER_EXITNOW -1 46 #define IPSEC_LOADER_LOADNOW 1 47 48 /* 49 * NOTE: This function is entered w/o holding any STREAMS perimeters. 50 */ 51 static void 52 ipsec_loader(void *arg) 53 { 54 callb_cpr_t cprinfo; 55 boolean_t ipsec_failure = B_FALSE; 56 ipsec_stack_t *ipss = (ipsec_stack_t *)arg; 57 58 CALLB_CPR_INIT(&cprinfo, &ipss->ipsec_loader_lock, callb_generic_cpr, 59 "ipsec_loader"); 60 mutex_enter(&ipss->ipsec_loader_lock); 61 for (;;) { 62 63 /* 64 * Wait for someone to tell me to continue. 65 */ 66 while (ipss->ipsec_loader_sig == IPSEC_LOADER_WAIT) { 67 CALLB_CPR_SAFE_BEGIN(&cprinfo); 68 cv_wait(&ipss->ipsec_loader_sig_cv, 69 &ipss->ipsec_loader_lock); 70 CALLB_CPR_SAFE_END(&cprinfo, &ipss->ipsec_loader_lock); 71 } 72 73 /* IPSEC_LOADER_EXITNOW implies signal by _fini(). */ 74 if (ipss->ipsec_loader_sig == IPSEC_LOADER_EXITNOW) { 75 /* 76 * Let user patch ipsec_loader_tid to 77 * 0 to try again. 78 */ 79 ipss->ipsec_loader_state = IPSEC_LOADER_FAILED; 80 ipss->ipsec_loader_sig = IPSEC_LOADER_WAIT; 81 82 /* ipsec_loader_lock is held at this point! */ 83 ASSERT(MUTEX_HELD(&ipss->ipsec_loader_lock)); 84 CALLB_CPR_EXIT(&cprinfo); 85 ASSERT(!MUTEX_HELD(&ipss->ipsec_loader_lock)); 86 thread_exit(); 87 } 88 mutex_exit(&ipss->ipsec_loader_lock); 89 90 /* 91 * Load IPsec, which is done by modloading keysock and calling 92 * keysock_plumb_ipsec(). 93 */ 94 95 /* Pardon my hardcoding... */ 96 if (modload("drv", "keysock") == -1) { 97 cmn_err(CE_WARN, "IP: Cannot load keysock."); 98 /* 99 * Only this function can set ipsec_failure. If the 100 * damage can be repaired, use adb to set this to 101 * B_FALSE and try again. 102 */ 103 ipsec_failure = B_TRUE; 104 } else if (keysock_plumb_ipsec(ipss->ipsec_netstack) != 0) { 105 cmn_err(CE_WARN, "IP: Cannot plumb IPsec."); 106 /* 107 * Only this function can set ipsec_failure. If the 108 * damage can be repaired, use adb to set this to 109 * B_FALSE and try again. 110 */ 111 ipsec_failure = B_TRUE; 112 } else { 113 ipsec_failure = B_FALSE; 114 } 115 116 mutex_enter(&ipss->ipsec_loader_lock); 117 if (ipsec_failure) { 118 if (ipss->ipsec_loader_sig == IPSEC_LOADER_LOADNOW) 119 ipss->ipsec_loader_sig = IPSEC_LOADER_WAIT; 120 ipss->ipsec_loader_state = IPSEC_LOADER_FAILED; 121 } else { 122 ipss->ipsec_loader_state = IPSEC_LOADER_SUCCEEDED; 123 } 124 mutex_exit(&ipss->ipsec_loader_lock); 125 126 ip_ipsec_load_complete(ipss); 127 128 mutex_enter(&ipss->ipsec_loader_lock); 129 if (!ipsec_failure) { 130 CALLB_CPR_EXIT(&cprinfo); 131 ASSERT(!MUTEX_HELD(&ipss->ipsec_loader_lock)); 132 ipsec_register_prov_update(); 133 thread_exit(); 134 } 135 } 136 } 137 138 /* 139 * Called from ip_ddi_init() to initialize ipsec loader thread. 140 */ 141 void 142 ipsec_loader_init(ipsec_stack_t *ipss) 143 { 144 mutex_init(&ipss->ipsec_loader_lock, NULL, MUTEX_DEFAULT, NULL); 145 cv_init(&ipss->ipsec_loader_sig_cv, NULL, CV_DEFAULT, NULL); 146 } 147 148 /* 149 * Called from ip_ddi_destroy() to take down ipsec loader thread. 150 */ 151 void 152 ipsec_loader_destroy(ipsec_stack_t *ipss) 153 { 154 kt_did_t tid; 155 156 mutex_enter(&ipss->ipsec_loader_lock); 157 tid = ipss->ipsec_loader_tid; 158 if (tid != 0) { 159 ipss->ipsec_loader_sig = IPSEC_LOADER_EXITNOW; 160 cv_signal(&ipss->ipsec_loader_sig_cv); 161 ipss->ipsec_loader_tid = 0; 162 } 163 mutex_exit(&ipss->ipsec_loader_lock); 164 165 /* 166 * Wait for ipsec_loader() to finish before we destroy 167 * cvs and mutexes. 168 */ 169 if (tid != 0) 170 thread_join(tid); 171 172 mutex_destroy(&ipss->ipsec_loader_lock); 173 cv_destroy(&ipss->ipsec_loader_sig_cv); 174 } 175 176 void 177 ipsec_loader_start(ipsec_stack_t *ipss) 178 { 179 kthread_t *tp; 180 181 mutex_enter(&ipss->ipsec_loader_lock); 182 183 if (ipss->ipsec_loader_tid == 0) { 184 tp = thread_create(NULL, 0, ipsec_loader, ipss, 0, &p0, 185 TS_RUN, MAXCLSYSPRI); 186 ipss->ipsec_loader_tid = tp->t_did; 187 } 188 /* Else we lost the race, oh well. */ 189 mutex_exit(&ipss->ipsec_loader_lock); 190 } 191 192 void 193 ipsec_loader_loadnow(ipsec_stack_t *ipss) 194 { 195 /* 196 * It is possible that an algorithm update message was 197 * received before IPsec is loaded. Such messages are 198 * saved in spdsock for later processing. Since IPsec 199 * loading can be initiated by interfaces different 200 * than spdsock, we must trigger the processing of 201 * update messages from the ipsec loader. 202 */ 203 spdsock_update_pending_algs(ipss->ipsec_netstack); 204 205 mutex_enter(&ipss->ipsec_loader_lock); 206 if ((ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) && 207 (ipss->ipsec_loader_sig == IPSEC_LOADER_WAIT)) { 208 ipss->ipsec_loader_sig = IPSEC_LOADER_LOADNOW; 209 cv_signal(&ipss->ipsec_loader_sig_cv); 210 } 211 mutex_exit(&ipss->ipsec_loader_lock); 212 } 213 214 /* 215 * Dummy callback routine (placeholder) to avoid keysock plumbing 216 * races. Used in conjunction with qtimeout() and qwait() to wait 217 * until ipsec has loaded -- the qwait() in ipsec_loader_loadwait will 218 * wake up once this routine returns. 219 */ 220 221 /* ARGSUSED */ 222 static void 223 loader_nop(void *ignoreme) 224 { 225 } 226 227 /* 228 * Called from keysock driver open to delay until ipsec is done loading. 229 * Returns B_TRUE if it worked, B_FALSE if it didn't. 230 */ 231 boolean_t 232 ipsec_loader_wait(queue_t *q, ipsec_stack_t *ipss) 233 { 234 /* 235 * 30ms delay per loop is arbitrary; it takes ~300ms to 236 * load and plumb ipsec on an ultra-1. 237 */ 238 239 while (ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) { 240 (void) qtimeout(q, loader_nop, 0, drv_usectohz(30000)); 241 qwait(q); 242 } 243 244 return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED); 245 } 246 247 /* 248 * Just check to see if IPsec is loaded (or not). 249 */ 250 boolean_t 251 ipsec_loaded(ipsec_stack_t *ipss) 252 { 253 return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED); 254 } 255 256 /* 257 * Check to see if IPsec loading failed. 258 */ 259 boolean_t 260 ipsec_failed(ipsec_stack_t *ipss) 261 { 262 return (ipss->ipsec_loader_state == IPSEC_LOADER_FAILED); 263 } 264