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 2009 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
ipsec_loader(void * arg)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 mutex_enter(&ipss->ipsec_loader_lock);
125 if (!ipsec_failure) {
126 CALLB_CPR_EXIT(&cprinfo);
127 ASSERT(MUTEX_NOT_HELD(&ipss->ipsec_loader_lock));
128 ipsec_register_prov_update();
129 thread_exit();
130 }
131 }
132 }
133
134 /*
135 * Called from ip_ddi_init() to initialize ipsec loader thread.
136 */
137 void
ipsec_loader_init(ipsec_stack_t * ipss)138 ipsec_loader_init(ipsec_stack_t *ipss)
139 {
140 mutex_init(&ipss->ipsec_loader_lock, NULL, MUTEX_DEFAULT, NULL);
141 cv_init(&ipss->ipsec_loader_sig_cv, NULL, CV_DEFAULT, NULL);
142 }
143
144 /*
145 * Called from ip_ddi_destroy() to take down ipsec loader thread.
146 */
147 void
ipsec_loader_destroy(ipsec_stack_t * ipss)148 ipsec_loader_destroy(ipsec_stack_t *ipss)
149 {
150 kt_did_t tid;
151
152 mutex_enter(&ipss->ipsec_loader_lock);
153 tid = ipss->ipsec_loader_tid;
154 if (tid != 0) {
155 ipss->ipsec_loader_sig = IPSEC_LOADER_EXITNOW;
156 cv_signal(&ipss->ipsec_loader_sig_cv);
157 ipss->ipsec_loader_tid = 0;
158 }
159 mutex_exit(&ipss->ipsec_loader_lock);
160
161 /*
162 * Wait for ipsec_loader() to finish before we destroy
163 * cvs and mutexes.
164 */
165 if (tid != 0)
166 thread_join(tid);
167
168 mutex_destroy(&ipss->ipsec_loader_lock);
169 cv_destroy(&ipss->ipsec_loader_sig_cv);
170 }
171
172 void
ipsec_loader_start(ipsec_stack_t * ipss)173 ipsec_loader_start(ipsec_stack_t *ipss)
174 {
175 kthread_t *tp;
176
177 mutex_enter(&ipss->ipsec_loader_lock);
178
179 if (ipss->ipsec_loader_tid == 0) {
180 tp = thread_create(NULL, 0, ipsec_loader, ipss, 0, &p0,
181 TS_RUN, MAXCLSYSPRI);
182 ipss->ipsec_loader_tid = tp->t_did;
183 }
184 /* Else we lost the race, oh well. */
185 mutex_exit(&ipss->ipsec_loader_lock);
186 }
187
188 void
ipsec_loader_loadnow(ipsec_stack_t * ipss)189 ipsec_loader_loadnow(ipsec_stack_t *ipss)
190 {
191 /*
192 * It is possible that an algorithm update message was
193 * received before IPsec is loaded. Such messages are
194 * saved in spdsock for later processing. Since IPsec
195 * loading can be initiated by interfaces different
196 * than spdsock, we must trigger the processing of
197 * update messages from the ipsec loader.
198 */
199 spdsock_update_pending_algs(ipss->ipsec_netstack);
200
201 mutex_enter(&ipss->ipsec_loader_lock);
202 if ((ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) &&
203 (ipss->ipsec_loader_sig == IPSEC_LOADER_WAIT)) {
204 ipss->ipsec_loader_sig = IPSEC_LOADER_LOADNOW;
205 cv_signal(&ipss->ipsec_loader_sig_cv);
206 }
207 mutex_exit(&ipss->ipsec_loader_lock);
208 }
209
210 /*
211 * Dummy callback routine (placeholder) to avoid keysock plumbing
212 * races. Used in conjunction with qtimeout() and qwait() to wait
213 * until ipsec has loaded -- the qwait() in ipsec_loader_loadwait will
214 * wake up once this routine returns.
215 */
216
217 /* ARGSUSED */
218 static void
loader_nop(void * ignoreme)219 loader_nop(void *ignoreme)
220 {
221 }
222
223 /*
224 * Called from keysock driver open to delay until ipsec is done loading.
225 * Returns B_TRUE if it worked, B_FALSE if it didn't.
226 */
227 boolean_t
ipsec_loader_wait(queue_t * q,ipsec_stack_t * ipss)228 ipsec_loader_wait(queue_t *q, ipsec_stack_t *ipss)
229 {
230 /*
231 * 30ms delay per loop is arbitrary; it takes ~300ms to
232 * load and plumb ipsec on an ultra-1.
233 */
234
235 while (ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) {
236 (void) qtimeout(q, loader_nop, 0, drv_usectohz(30000));
237 qwait(q);
238 }
239
240 return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED);
241 }
242
243 /*
244 * Just check to see if IPsec is loaded (or not).
245 */
246 boolean_t
ipsec_loaded(ipsec_stack_t * ipss)247 ipsec_loaded(ipsec_stack_t *ipss)
248 {
249 return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED);
250 }
251
252 /*
253 * Check to see if IPsec loading failed.
254 */
255 boolean_t
ipsec_failed(ipsec_stack_t * ipss)256 ipsec_failed(ipsec_stack_t *ipss)
257 {
258 return (ipss->ipsec_loader_state == IPSEC_LOADER_FAILED);
259 }
260