xref: /illumos-gate/usr/src/uts/common/inet/ip/ipsec_loader.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
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