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