/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Loader commands for ipsec_loader_sig */ #define IPSEC_LOADER_EXITNOW -1 #define IPSEC_LOADER_LOADNOW 1 /* * NOTE: This function is entered w/o holding any STREAMS perimeters. */ static void ipsec_loader(void *arg) { callb_cpr_t cprinfo; boolean_t ipsec_failure = B_FALSE; ipsec_stack_t *ipss = (ipsec_stack_t *)arg; CALLB_CPR_INIT(&cprinfo, &ipss->ipsec_loader_lock, callb_generic_cpr, "ipsec_loader"); mutex_enter(&ipss->ipsec_loader_lock); for (;;) { /* * Wait for someone to tell me to continue. */ while (ipss->ipsec_loader_sig == IPSEC_LOADER_WAIT) { CALLB_CPR_SAFE_BEGIN(&cprinfo); cv_wait(&ipss->ipsec_loader_sig_cv, &ipss->ipsec_loader_lock); CALLB_CPR_SAFE_END(&cprinfo, &ipss->ipsec_loader_lock); } /* IPSEC_LOADER_EXITNOW implies signal by _fini(). */ if (ipss->ipsec_loader_sig == IPSEC_LOADER_EXITNOW) { /* * Let user patch ipsec_loader_tid to * 0 to try again. */ ipss->ipsec_loader_state = IPSEC_LOADER_FAILED; ipss->ipsec_loader_sig = IPSEC_LOADER_WAIT; /* ipsec_loader_lock is held at this point! */ ASSERT(MUTEX_HELD(&ipss->ipsec_loader_lock)); CALLB_CPR_EXIT(&cprinfo); ASSERT(MUTEX_NOT_HELD(&ipss->ipsec_loader_lock)); thread_exit(); } mutex_exit(&ipss->ipsec_loader_lock); /* * Load IPsec, which is done by modloading keysock and calling * keysock_plumb_ipsec(). */ /* Pardon my hardcoding... */ if (modload("drv", "keysock") == -1) { cmn_err(CE_WARN, "IP: Cannot load keysock."); /* * Only this function can set ipsec_failure. If the * damage can be repaired, use adb to set this to * B_FALSE and try again. */ ipsec_failure = B_TRUE; } else if (keysock_plumb_ipsec(ipss->ipsec_netstack) != 0) { cmn_err(CE_WARN, "IP: Cannot plumb IPsec."); /* * Only this function can set ipsec_failure. If the * damage can be repaired, use adb to set this to * B_FALSE and try again. */ ipsec_failure = B_TRUE; } else { ipsec_failure = B_FALSE; } mutex_enter(&ipss->ipsec_loader_lock); if (ipsec_failure) { if (ipss->ipsec_loader_sig == IPSEC_LOADER_LOADNOW) ipss->ipsec_loader_sig = IPSEC_LOADER_WAIT; ipss->ipsec_loader_state = IPSEC_LOADER_FAILED; } else { ipss->ipsec_loader_state = IPSEC_LOADER_SUCCEEDED; } mutex_exit(&ipss->ipsec_loader_lock); ip_ipsec_load_complete(ipss); mutex_enter(&ipss->ipsec_loader_lock); if (!ipsec_failure) { CALLB_CPR_EXIT(&cprinfo); ASSERT(MUTEX_NOT_HELD(&ipss->ipsec_loader_lock)); ipsec_register_prov_update(); thread_exit(); } } } /* * Called from ip_ddi_init() to initialize ipsec loader thread. */ void ipsec_loader_init(ipsec_stack_t *ipss) { mutex_init(&ipss->ipsec_loader_lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&ipss->ipsec_loader_sig_cv, NULL, CV_DEFAULT, NULL); } /* * Called from ip_ddi_destroy() to take down ipsec loader thread. */ void ipsec_loader_destroy(ipsec_stack_t *ipss) { kt_did_t tid; mutex_enter(&ipss->ipsec_loader_lock); tid = ipss->ipsec_loader_tid; if (tid != 0) { ipss->ipsec_loader_sig = IPSEC_LOADER_EXITNOW; cv_signal(&ipss->ipsec_loader_sig_cv); ipss->ipsec_loader_tid = 0; } mutex_exit(&ipss->ipsec_loader_lock); /* * Wait for ipsec_loader() to finish before we destroy * cvs and mutexes. */ if (tid != 0) thread_join(tid); mutex_destroy(&ipss->ipsec_loader_lock); cv_destroy(&ipss->ipsec_loader_sig_cv); } void ipsec_loader_start(ipsec_stack_t *ipss) { kthread_t *tp; mutex_enter(&ipss->ipsec_loader_lock); if (ipss->ipsec_loader_tid == 0) { tp = thread_create(NULL, 0, ipsec_loader, ipss, 0, &p0, TS_RUN, MAXCLSYSPRI); ipss->ipsec_loader_tid = tp->t_did; } /* Else we lost the race, oh well. */ mutex_exit(&ipss->ipsec_loader_lock); } void ipsec_loader_loadnow(ipsec_stack_t *ipss) { /* * It is possible that an algorithm update message was * received before IPsec is loaded. Such messages are * saved in spdsock for later processing. Since IPsec * loading can be initiated by interfaces different * than spdsock, we must trigger the processing of * update messages from the ipsec loader. */ spdsock_update_pending_algs(ipss->ipsec_netstack); mutex_enter(&ipss->ipsec_loader_lock); if ((ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) && (ipss->ipsec_loader_sig == IPSEC_LOADER_WAIT)) { ipss->ipsec_loader_sig = IPSEC_LOADER_LOADNOW; cv_signal(&ipss->ipsec_loader_sig_cv); } mutex_exit(&ipss->ipsec_loader_lock); } /* * Dummy callback routine (placeholder) to avoid keysock plumbing * races. Used in conjunction with qtimeout() and qwait() to wait * until ipsec has loaded -- the qwait() in ipsec_loader_loadwait will * wake up once this routine returns. */ /* ARGSUSED */ static void loader_nop(void *ignoreme) { } /* * Called from keysock driver open to delay until ipsec is done loading. * Returns B_TRUE if it worked, B_FALSE if it didn't. */ boolean_t ipsec_loader_wait(queue_t *q, ipsec_stack_t *ipss) { /* * 30ms delay per loop is arbitrary; it takes ~300ms to * load and plumb ipsec on an ultra-1. */ while (ipss->ipsec_loader_state == IPSEC_LOADER_WAIT) { (void) qtimeout(q, loader_nop, 0, drv_usectohz(30000)); qwait(q); } return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED); } /* * Just check to see if IPsec is loaded (or not). */ boolean_t ipsec_loaded(ipsec_stack_t *ipss) { return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED); } /* * Check to see if IPsec loading failed. */ boolean_t ipsec_failed(ipsec_stack_t *ipss) { return (ipss->ipsec_loader_state == IPSEC_LOADER_FAILED); }