#include <sys/types.h>
#include <sys/stream.h>
#include <sys/sysmacros.h>
#include <sys/callb.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/proc.h>
#include <sys/modctl.h>
#include <sys/disp.h>
#include <inet/ip.h>
#include <inet/ipsec_impl.h>
#include <inet/optcom.h>
#include <inet/keysock.h>
#define IPSEC_LOADER_EXITNOW -1
#define IPSEC_LOADER_LOADNOW 1
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 (;;) {
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);
}
if (ipss->ipsec_loader_sig == IPSEC_LOADER_EXITNOW) {
ipss->ipsec_loader_state = IPSEC_LOADER_FAILED;
ipss->ipsec_loader_sig = IPSEC_LOADER_WAIT;
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);
if (modload("drv", "keysock") == -1) {
cmn_err(CE_WARN, "IP: Cannot load keysock.");
ipsec_failure = B_TRUE;
} else if (keysock_plumb_ipsec(ipss->ipsec_netstack) != 0) {
cmn_err(CE_WARN, "IP: Cannot plumb IPsec.");
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);
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();
}
}
}
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);
}
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);
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;
}
mutex_exit(&ipss->ipsec_loader_lock);
}
void
ipsec_loader_loadnow(ipsec_stack_t *ipss)
{
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);
}
static void
loader_nop(void *ignoreme)
{
}
boolean_t
ipsec_loader_wait(queue_t *q, ipsec_stack_t *ipss)
{
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);
}
boolean_t
ipsec_loaded(ipsec_stack_t *ipss)
{
return (ipss->ipsec_loader_state == IPSEC_LOADER_SUCCEEDED);
}
boolean_t
ipsec_failed(ipsec_stack_t *ipss)
{
return (ipss->ipsec_loader_state == IPSEC_LOADER_FAILED);
}