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