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