/* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Starcat IPSec Key Management Driver. * * This driver runs on a Starcat Domain. It processes requests received * from the System Controller (SC) from IOSRAM, passes these requests * to the sckmd daemon by means of an open/close/ioctl interface, and * sends corresponding status information back to the SC. * * Requests received from the SC consist of IPsec security associations * (SAs) needed to secure the communication between SC and Domain daemons * communicating using the Management Network (MAN). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG static uint_t sckm_debug_flags = 0x0; #define SCKM_DEBUG0(f, s) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s) #define SCKM_DEBUG1(f, s, a) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a) #define SCKM_DEBUG2(f, s, a, b) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a, b) #define SCKM_DEBUG3(f, s, a, b, c) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a, b, c) #define SCKM_DEBUG4(f, s, a, b, c, d) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a, b, c, d) #define SCKM_DEBUG5(f, s, a, b, c, d, e) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a, b, c, d, e) #define SCKM_DEBUG6(f, s, a, b, c, d, e, ff) if ((f)& sckm_debug_flags) \ cmn_err(CE_CONT, s, a, b, c, d, e, ff) #else #define SCKM_DEBUG0(f, s) #define SCKM_DEBUG1(f, s, a) #define SCKM_DEBUG2(f, s, a, b) #define SCKM_DEBUG3(f, s, a, b, c) #define SCKM_DEBUG4(f, s, a, b, c, d) #define SCKM_DEBUG5(f, s, a, b, c, d, e) #define SCKM_DEBUG6(f, s, a, b, c, d, e, ff) #endif /* DEBUG */ #define D_INIT 0x00000001 /* _init/_fini/_info */ #define D_ATTACH 0x00000002 /* attach/detach */ #define D_OPEN 0x00000008 /* open/close */ #define D_IOCTL 0x00010000 /* ioctl */ #define D_TASK 0x00100000 /* mailbox task processing */ #define D_CALLBACK 0x00200000 /* mailbox callback */ static int sckm_open(dev_t *, int, int, struct cred *); static int sckm_close(dev_t, int, int, struct cred *); static int sckm_ioctl(dev_t, int, intptr_t, int, struct cred *, int *); static struct cb_ops sckm_cb_ops = { sckm_open, /* open */ sckm_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ sckm_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* prop_op */ 0, /* streamtab */ D_NEW | D_MP /* Driver compatibility flag */ }; static int sckm_attach(dev_info_t *, ddi_attach_cmd_t); static int sckm_detach(dev_info_t *, ddi_detach_cmd_t); static int sckm_info(dev_info_t *, ddi_info_cmd_t, void *, void **); static struct dev_ops sckm_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ sckm_info, /* get_dev_info */ nulldev, /* identify */ nulldev, /* probe */ sckm_attach, /* attach */ sckm_detach, /* detach */ nodev, /* reset */ &sckm_cb_ops, /* driver operations */ (struct bus_ops *)0 /* no bus operations */ }; static struct modldrv modldrv = { &mod_driverops, "Key Management Driver v%I%", &sckm_ops, }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; /* * Private definitions. */ #define SCKM_DEF_GETMSG_TIMEOUT 60 /* in seconds */ #define SCKM_DAEMON_TIMEOUT 4000000 /* in microseconds */ #define SCKM_NUM_TASKQ 2 /* # of task queue entries */ /* * For processing mailbox layer events. */ static kmutex_t sckm_task_mutex; static kmutex_t sckm_taskq_ptr_mutex; static clock_t sckm_getmsg_timeout = SCKM_DEF_GETMSG_TIMEOUT*1000; static taskq_t *sckm_taskq = NULL; static sckm_mbox_req_hdr_t *req_data = NULL; static sckm_mbox_rep_hdr_t *rep_data = NULL; /* * For synchronization with key management daemon. */ static kmutex_t sckm_umutex; static kcondvar_t sckm_udata_cv; /* daemon waits on data */ static kcondvar_t sckm_cons_cv; /* wait for daemon to consume data */ static boolean_t sckm_udata_req = B_FALSE; /* data available for daemon */ static sckm_ioctl_getreq_t sckm_udata; /* request for daemon */ static sckm_ioctl_status_t sckm_udata_status; /* status from daemon */ /* * Other misc private variables. */ static dev_info_t *sckm_devi = NULL; static boolean_t sckm_oflag = B_FALSE; /* * Private functions prototypes. */ static void sckm_mbox_callback(void); static void sckm_mbox_task(void *arg); static void sckm_process_msg(uint32_t cmd, uint64_t transid, uint32_t len, sckm_mbox_req_hdr_t *req_data, sckm_mbox_rep_hdr_t *rep_data); int _init(void) { mboxsc_timeout_range_t timeout_range; int ret; SCKM_DEBUG0(D_INIT, "in _init"); /* * Initialize outgoing mailbox (KDSC) */ if ((ret = mboxsc_init(KEY_KDSC, MBOXSC_MBOX_OUT, NULL)) != 0) { cmn_err(CE_WARN, "failed initializing outgoing mailbox " "(%d)", ret); return (ret); } /* * Initialize incoming mailbox (SCKD) */ if ((ret = mboxsc_init(KEY_SCKD, MBOXSC_MBOX_IN, sckm_mbox_callback)) != 0) { cmn_err(CE_WARN, "failed initializing incoming mailbox " "(%d)\n", ret); mboxsc_fini(KEY_KDSC); return (ret); } if ((ret = mboxsc_ctrl(KEY_SCKD, MBOXSC_CMD_GETMSG_TIMEOUT_RANGE, (void *)&timeout_range)) != 0) { mboxsc_fini(KEY_SCKD); mboxsc_fini(KEY_KDSC); return (ret); } if (sckm_getmsg_timeout < timeout_range.min_timeout) { sckm_getmsg_timeout = timeout_range.min_timeout; cmn_err(CE_WARN, "resetting getmsg timeout to %lx", sckm_getmsg_timeout); } if (sckm_getmsg_timeout > timeout_range.max_timeout) { sckm_getmsg_timeout = timeout_range.max_timeout; cmn_err(CE_WARN, "resetting getmsg timeout to %lx", sckm_getmsg_timeout); } if ((ret = mod_install(&modlinkage)) != 0) { mboxsc_fini(KEY_KDSC); mboxsc_fini(KEY_SCKD); return (ret); } /* * Initialize variables needed for synchronization with daemon. */ sckm_udata.buf = kmem_alloc(SCKM_SCKD_MAXDATA, KM_SLEEP); req_data = (sckm_mbox_req_hdr_t *)kmem_alloc(SCKM_SCKD_MAXDATA, KM_SLEEP); rep_data = (sckm_mbox_rep_hdr_t *)kmem_alloc(SCKM_KDSC_MAXDATA, KM_SLEEP); if ((sckm_udata.buf == NULL) || (req_data == NULL) || (rep_data == NULL)) { cmn_err(CE_WARN, "not enough memory during _init"); /* free what was successfully allocated */ if (sckm_udata.buf != NULL) kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA); if (req_data != NULL) kmem_free(req_data, SCKM_SCKD_MAXDATA); if (rep_data != NULL) kmem_free(rep_data, SCKM_KDSC_MAXDATA); sckm_udata.buf = NULL; req_data = NULL; rep_data = NULL; /* uninitialize mailboxes, remove module, and return error */ mboxsc_fini(KEY_KDSC); mboxsc_fini(KEY_SCKD); mod_remove(&modlinkage); return (-1); } cv_init(&sckm_udata_cv, NULL, CV_DRIVER, NULL); cv_init(&sckm_cons_cv, NULL, CV_DRIVER, NULL); mutex_init(&sckm_umutex, NULL, MUTEX_DRIVER, NULL); /* * Create mutex for task processing, protection of taskq * pointer, and create taskq. */ mutex_init(&sckm_task_mutex, NULL, MUTEX_DRIVER, NULL); mutex_init(&sckm_taskq_ptr_mutex, NULL, MUTEX_DRIVER, NULL); sckm_taskq = taskq_create("sckm_taskq", 1, minclsyspri, SCKM_NUM_TASKQ, SCKM_NUM_TASKQ, TASKQ_PREPOPULATE); SCKM_DEBUG1(D_INIT, "out _init ret=%d\n", ret); return (ret); } int _fini(void) { int ret; SCKM_DEBUG0(D_INIT, "in _fini"); if ((ret = mod_remove(&modlinkage)) != 0) { return (ret); } /* * Wait for scheduled tasks to complete, then destroy task queue. */ mutex_enter(&sckm_taskq_ptr_mutex); if (sckm_taskq != NULL) { taskq_destroy(sckm_taskq); sckm_taskq = NULL; } mutex_exit(&sckm_taskq_ptr_mutex); /* * Terminate incoming and outgoing IOSRAM mailboxes */ mboxsc_fini(KEY_KDSC); mboxsc_fini(KEY_SCKD); /* * Destroy module synchronization objects and free memory */ mutex_destroy(&sckm_task_mutex); mutex_destroy(&sckm_taskq_ptr_mutex); mutex_destroy(&sckm_umutex); cv_destroy(&sckm_cons_cv); if (sckm_udata.buf != NULL) { kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA); sckm_udata.buf = NULL; } if (rep_data != NULL) { kmem_free(rep_data, SCKM_KDSC_MAXDATA); rep_data = NULL; } if (req_data != NULL) { kmem_free(req_data, SCKM_SCKD_MAXDATA); req_data = NULL; } return (ret); } int _info(struct modinfo *modinfop) { SCKM_DEBUG0(D_INIT, "in _info"); return (mod_info(&modlinkage, modinfop)); } static int sckm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { SCKM_DEBUG1(D_ATTACH, "in sckm_attach, cmd=%d", cmd); switch (cmd) { case DDI_ATTACH: SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_ATTACH"); if (ddi_create_minor_node(devi, "sckmdrv", S_IFCHR, 0, NULL, NULL) == DDI_FAILURE) { cmn_err(CE_WARN, "ddi_create_minor_node failed"); ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } sckm_devi = devi; break; case DDI_SUSPEND: SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_SUSPEND"); break; default: cmn_err(CE_WARN, "sckm_attach: bad cmd %d\n", cmd); return (DDI_FAILURE); } SCKM_DEBUG0(D_ATTACH, "out sckm_attach (DDI_SUCCESS)"); return (DDI_SUCCESS); } static int sckm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) { SCKM_DEBUG1(D_ATTACH, "in sckm_detach, cmd=%d", cmd); switch (cmd) { case DDI_DETACH: SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH"); ddi_remove_minor_node(devi, NULL); break; case DDI_SUSPEND: SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH"); break; default: cmn_err(CE_WARN, "sckm_detach: bad cmd %d\n", cmd); return (DDI_FAILURE); } SCKM_DEBUG0(D_ATTACH, "out sckm_detach (DDI_SUCCESS)"); return (DDI_SUCCESS); } /* ARGSUSED */ static int sckm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int rv; SCKM_DEBUG1(D_ATTACH, "in sckm_info, infocmd=%d", infocmd); switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: *result = (void *)sckm_devi; rv = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)0; rv = DDI_SUCCESS; break; default: rv = DDI_FAILURE; } SCKM_DEBUG1(D_ATTACH, "out sckm_info, rv=%d", rv); return (rv); } /*ARGSUSED*/ static int sckm_open(dev_t *devp, int flag, int otyp, struct cred *cred) { SCKM_DEBUG0(D_OPEN, "in sckm_open"); /* check credentials of calling process */ if (drv_priv(cred)) { SCKM_DEBUG0(D_OPEN, "sckm_open: attempt by non-root proc"); return (EPERM); } /* enforce exclusive access */ mutex_enter(&sckm_umutex); if (sckm_oflag == B_TRUE) { SCKM_DEBUG0(D_OPEN, "sckm_open: already open"); mutex_exit(&sckm_umutex); return (EBUSY); } sckm_oflag = B_TRUE; mutex_exit(&sckm_umutex); SCKM_DEBUG0(D_OPEN, "sckm_open: succcess"); return (0); } /*ARGSUSED*/ static int sckm_close(dev_t dev, int flag, int otyp, struct cred *cred) { SCKM_DEBUG0(D_OPEN, "in sckm_close"); mutex_enter(&sckm_umutex); sckm_oflag = B_FALSE; mutex_exit(&sckm_umutex); return (0); } static int sckm_copyin_ioctl_getreq(intptr_t userarg, sckm_ioctl_getreq_t *driverarg, int flag) { #ifdef _MULTI_DATAMODEL switch (ddi_model_convert_from(flag & FMODELS)) { case DDI_MODEL_ILP32: { sckm_ioctl_getreq32_t driverarg32; if (ddi_copyin((caddr_t)userarg, &driverarg32, sizeof (sckm_ioctl_getreq32_t), flag)) { return (EFAULT); } driverarg->transid = driverarg32.transid; driverarg->type = driverarg32.type; driverarg->buf = (caddr_t)(uintptr_t)driverarg32.buf; driverarg->buf_len = driverarg32.buf_len; break; } case DDI_MODEL_NONE: { if (ddi_copyin((caddr_t)userarg, &driverarg, sizeof (sckm_ioctl_getreq_t), flag)) { return (EFAULT); } break; } } #else /* ! _MULTI_DATAMODEL */ if (ddi_copyin((caddr_t)userarg, &driverarg, sizeof (sckm_ioctl_getreq_t), flag)) { return (EFAULT); } #endif /* _MULTI_DATAMODEL */ return (0); } static int sckm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg, intptr_t userarg, int flag) { #ifdef _MULTI_DATAMODEL switch (ddi_model_convert_from(flag & FMODELS)) { case DDI_MODEL_ILP32: { sckm_ioctl_getreq32_t driverarg32; driverarg32.transid = driverarg->transid; driverarg32.type = driverarg->type; driverarg32.buf = (caddr32_t)(uintptr_t)driverarg->buf; driverarg32.buf_len = driverarg->buf_len; if (ddi_copyout(&driverarg32, (caddr_t)userarg, sizeof (sckm_ioctl_getreq32_t), flag)) { return (EFAULT); } break; } case DDI_MODEL_NONE: if (ddi_copyout(driverarg, (caddr_t)userarg, sizeof (sckm_ioctl_getreq_t), flag)) { return (EFAULT); } break; } #else /* ! _MULTI_DATAMODEL */ if (ddi_copyout(driverarg, (caddr_t)userarg, sizeof (sckm_ioctl_getreq_t), flag)) { return (EFAULT); } #endif /* _MULTI_DATAMODEL */ return (0); } /*ARGSUSED*/ static int sckm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp) { int rval = 0; SCKM_DEBUG0(D_IOCTL, "in sckm_ioctl"); switch (cmd) { case SCKM_IOCTL_GETREQ: { sckm_ioctl_getreq_t arg; SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_GETREQ"); if (sckm_copyin_ioctl_getreq(data, &arg, flag)) { return (EFAULT); } /* sanity check argument */ if (arg.buf_len < SCKM_SCKD_MAXDATA) { SCKM_DEBUG2(D_IOCTL, "sckm_ioctl: usr buffer too " "small (%d < %d)", arg.buf_len, SCKM_SCKD_MAXDATA); return (ENOSPC); } mutex_enter(&sckm_umutex); /* wait for request from SC */ while (!sckm_udata_req) { SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: waiting for msg"); if (cv_wait_sig(&sckm_udata_cv, &sckm_umutex) == 0) { mutex_exit(&sckm_umutex); return (EINTR); } } SCKM_DEBUG1(D_IOCTL, "sckm_ioctl: msg available " "transid = 0x%lx", sckm_udata.transid); arg.transid = sckm_udata.transid; arg.type = sckm_udata.type; if (ddi_copyout(sckm_udata.buf, arg.buf, sckm_udata.buf_len, flag)) { mutex_exit(&sckm_umutex); return (EFAULT); } arg.buf_len = sckm_udata.buf_len; mutex_exit(&sckm_umutex); if (sckm_copyout_ioctl_getreq(&arg, data, flag)) { return (EFAULT); } break; } case SCKM_IOCTL_STATUS: { sckm_ioctl_status_t arg; SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_STATUS"); if (ddi_copyin((caddr_t)data, &arg, sizeof (sckm_ioctl_status_t), flag)) { cmn_err(CE_WARN, "sckm_ioctl: ddi_copyin failed"); return (EFAULT); } SCKM_DEBUG3(D_IOCTL, "sckm_ioctl: arg transid=0x%lx, " "status=%d, sadb_msg_errno=%d", arg.transid, arg.status, arg.sadb_msg_errno); mutex_enter(&sckm_umutex); /* fail if no status is expected, or if it does not match */ if (!sckm_udata_req || sckm_udata.transid != arg.transid) { mutex_exit(&sckm_umutex); return (EINVAL); } /* update status information for event handler */ bcopy(&arg, &sckm_udata_status, sizeof (sckm_ioctl_status_t)); /* signal event handler that request has been processed */ SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: signaling event handler" " that data has been processed"); cv_signal(&sckm_cons_cv); sckm_udata_req = B_FALSE; mutex_exit(&sckm_umutex); break; } default: SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: unknown command"); rval = EINVAL; } SCKM_DEBUG1(D_IOCTL, "out sckm_ioctl, rval=%d", rval); return (rval); } /* * sckm_mbox_callback * * Callback routine registered with the IOSRAM mailbox protocol driver. * Invoked when a message is received on the mailbox. */ static void sckm_mbox_callback(void) { SCKM_DEBUG0(D_CALLBACK, "in sckm_mbox_callback()"); mutex_enter(&sckm_taskq_ptr_mutex); if (sckm_taskq == NULL) { mutex_exit(&sckm_taskq_ptr_mutex); return; } if (!taskq_dispatch(sckm_taskq, sckm_mbox_task, NULL, KM_NOSLEEP)) { /* * Too many tasks already pending. Do not queue a new * request. */ SCKM_DEBUG0(D_CALLBACK, "failed dispatching task"); } mutex_exit(&sckm_taskq_ptr_mutex); SCKM_DEBUG0(D_CALLBACK, "out sckm_mbox_callback()"); } /* * sckm_mbox_task * * Dispatched on taskq from the IOSRAM mailbox callback * sckm_mbox_callback when a message is received on the incoming * mailbox. */ static void sckm_mbox_task(void *ignored) { _NOTE(ARGUNUSED(ignored)) uint32_t type, cmd, length; uint64_t transid; int rval; SCKM_DEBUG0(D_TASK, "in sckm_mbox_task\n"); mutex_enter(&sckm_task_mutex); if (req_data == NULL || rep_data == NULL) { SCKM_DEBUG0(D_TASK, "sckm_mbox_task: no buffers"); mutex_exit(&sckm_task_mutex); return; } /* * Get mailbox message. */ type = MBOXSC_MSG_REQUEST; length = SCKM_SCKD_MAXDATA; cmd = 0; transid = 0; SCKM_DEBUG0(D_TASK, "sckm_mbox_task: " "calling mboxsc_getmsg()\n"); rval = mboxsc_getmsg(KEY_SCKD, &type, &cmd, &transid, &length, req_data, sckm_getmsg_timeout); if (rval != 0) { SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " "mboxsc_getmsg() failed (%d)\n", rval); mutex_exit(&sckm_task_mutex); return; } SCKM_DEBUG4(D_TASK, "sckm_mbox_task: " "type=0x%x cmd=0x%x length=%d transid=0x%lx\n", type, cmd, length, transid); /* check message length */ if (length < sizeof (sckm_mbox_req_hdr_t)) { /* protocol error, drop message */ SCKM_DEBUG2(D_TASK, "received short " "message of length %d, min %lu", length, sizeof (sckm_mbox_req_hdr_t)); mutex_exit(&sckm_task_mutex); return; } /* check version of message received */ if (req_data->sckm_version != SCKM_PROTOCOL_VERSION) { SCKM_DEBUG2(D_TASK, "received protocol " "version %d, expected %d", req_data->sckm_version, SCKM_PROTOCOL_VERSION); /* * Send reply with SCKM_SADB_ERR_VERSION error * so that SC can adopt correct protocol version * for this domain. */ rep_data->sckm_version = SCKM_PROTOCOL_VERSION; rep_data->status = SCKM_ERR_VERSION; rval = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT); if (rval != 0) { SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " "mboxsc_putmsg() failed (%d)\n", rval); mutex_exit(&sckm_task_mutex); return; } } /* process message */ sckm_process_msg(cmd, transid, length, req_data, rep_data); mutex_exit(&sckm_task_mutex); } /* * sckm_process_msg * * Process a message received from the SC. Invoked by sckm_event_task(). */ static void sckm_process_msg(uint32_t cmd, uint64_t transid, uint32_t len, sckm_mbox_req_hdr_t *req_data, sckm_mbox_rep_hdr_t *rep_data) { int rv; mutex_enter(&sckm_umutex); switch (cmd) { case SCKM_MSG_SADB: { int sadb_msglen; sadb_msglen = len-sizeof (sckm_mbox_req_hdr_t); SCKM_DEBUG1(D_TASK, "received SCKM_MSG_SADB len=%d", sadb_msglen); /* sanity check request */ if (len-sizeof (sckm_mbox_req_hdr_t) <= 0) { SCKM_DEBUG0(D_TASK, "bad SADB message, " "zero length"); /* * SADB message is too short, send corresponding * error message to SC. */ rep_data->sckm_version = SCKM_PROTOCOL_VERSION; rep_data->status = SCKM_ERR_SADB_MSG; if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " "mboxsc_putmsg() failed (%d)\n", rv); } mutex_exit(&sckm_umutex); return; } /* initialize request for daemon */ sckm_udata.transid = transid; sckm_udata.type = SCKM_IOCTL_REQ_SADB; sckm_udata.buf_len = len-sizeof (sckm_mbox_req_hdr_t); bcopy(req_data+1, sckm_udata.buf, sckm_udata.buf_len); break; } default: cmn_err(CE_WARN, "unknown cmd %x received from SC", cmd); /* * Received unknown command from SC. Send corresponding * error message to SC. */ rep_data->sckm_version = SCKM_PROTOCOL_VERSION; rep_data->status = SCKM_ERR_BAD_CMD; if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " "mboxsc_putmsg() failed (%d)\n", rv); } mutex_exit(&sckm_umutex); return; } /* * At this point, we know that the request is valid, so pass * the request to the daemon. */ SCKM_DEBUG0(D_TASK, "waking up daemon"); sckm_udata_req = B_TRUE; cv_signal(&sckm_udata_cv); /* wait for daemon to process request */ if (cv_timedwait(&sckm_cons_cv, &sckm_umutex, ddi_get_lbolt()+drv_usectohz(SCKM_DAEMON_TIMEOUT)) == -1) { /* * Daemon did not process the data, report this * error to the SC. */ SCKM_DEBUG0(D_TASK, "daemon timeout!!"); rep_data->sckm_version = SCKM_PROTOCOL_VERSION; rep_data->status = SCKM_ERR_DAEMON; } else { /* Daemon processed data, return status to SC */ SCKM_DEBUG0(D_TASK, "daemon processed data"); rep_data->sckm_version = SCKM_PROTOCOL_VERSION; switch (sckm_udata_status.status) { case SCKM_IOCTL_STAT_SUCCESS: SCKM_DEBUG0(D_TASK, "daemon returned success"); rep_data->status = SCKM_SUCCESS; break; case SCKM_IOCTL_STAT_ERR_PFKEY: SCKM_DEBUG1(D_TASK, "daemon returned PF_KEY " "error, errno=%d", sckm_udata_status.sadb_msg_errno); rep_data->status = SCKM_ERR_SADB_PFKEY; rep_data->sadb_msg_errno = sckm_udata_status.sadb_msg_errno; break; case SCKM_IOCTL_STAT_ERR_REQ: SCKM_DEBUG0(D_TASK, "daemon returned " "bad request"); rep_data->status = SCKM_ERR_DAEMON; break; case SCKM_IOCTL_STAT_ERR_VERSION: SCKM_DEBUG0(D_TASK, "PF_KEY version not " "supported"); rep_data->status = SCKM_ERR_SADB_VERSION; rep_data->sadb_msg_version = sckm_udata_status.sadb_msg_version; break; case SCKM_IOCTL_STAT_ERR_TIMEOUT: SCKM_DEBUG0(D_TASK, "no response received " "from key engine"); rep_data->status = SCKM_ERR_SADB_TIMEOUT; break; case SCKM_IOCTL_STAT_ERR_OTHER: SCKM_DEBUG0(D_TASK, "daemon encountered " "an error"); rep_data->status = SCKM_ERR_DAEMON; break; case SCKM_IOCTL_STAT_ERR_SADB_TYPE: SCKM_DEBUG0(D_TASK, "daemon returned bad " "SADB message type"); rep_data->status = SCKM_ERR_SADB_BAD_TYPE; break; default: cmn_err(CE_WARN, "SCKM daemon returned " "invalid status %d", sckm_udata_status.status); rep_data->status = SCKM_ERR_DAEMON; } } /* send reply back to SC */ if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { SCKM_DEBUG1(D_TASK, "failed sending reply to SC (%d)", rv); } else { SCKM_DEBUG0(D_TASK, "reply sent to SC"); } sckm_udata_req = B_FALSE; mutex_exit(&sckm_umutex); }