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