Home | History | Annotate | Download | only in ip
      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