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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * OPL IPSec Key Management Driver.
29 *
30 * This driver runs on a OPL Domain. It processes requests received
31 * from the OPL Service Processor (SP) via mailbox message. It passes
32 * these requests to the sckmd daemon by means of an /ioctl interface.
33 *
34 * Requests received from the SP consist of IPsec security associations
35 * (SAs) needed to secure the communication between SC and Domain daemons
36 * communicating using DSCP.
37 */
38
39 #include <sys/types.h>
40 #include <sys/cmn_err.h>
41 #include <sys/kmem.h>
42 #include <sys/errno.h>
43 #include <sys/file.h>
44 #include <sys/open.h>
45 #include <sys/stat.h>
46 #include <sys/conf.h>
47 #include <sys/ddi.h>
48 #include <sys/cmn_err.h>
49 #include <sys/sunddi.h>
50 #include <sys/sunndi.h>
51 #include <sys/ddi_impldefs.h>
52 #include <sys/ndi_impldefs.h>
53 #include <sys/modctl.h>
54 #include <sys/disp.h>
55 #include <sys/note.h>
56 #include <sys/byteorder.h>
57 #include <sys/sdt.h>
58
59 #include <sys/scfd/scfdscpif.h>
60 #include <sys/oplkm_msg.h>
61 #include <sys/sckm_io.h>
62 #include <sys/oplkm.h>
63
64 #define OKM_NODENAME "oplkmdrv" /* Node name */
65 #define OKM_TARGET_ID 0 /* Target ID */
66 #define OKM_SM_TOUT 5000 /* small timeout (5msec) */
67 #define OKM_LG_TOUT 50000 /* large timeout (50msec) */
68 #define OKM_MB_TOUT 10000000 /* Mailbox timeout (10sec) */
69
70 okms_t okms_global; /* Global instance structure */
71
72 #ifdef DEBUG
73 uint32_t okm_debug = DBG_WARN;
74 #endif
75
76 /*
77 * Prototypes for the module related functions.
78 */
79 int okm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
80 int okm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
81 int okm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
82 int okm_open(dev_t *devp, int flag, int otyp, struct cred *cred);
83 int okm_close(dev_t dev, int flag, int otyp, struct cred *cred);
84 int okm_ioctl(dev_t dev, int cmd, intptr_t data, int flag,
85 cred_t *cred, int *rvalp);
86
87 /*
88 * Prototypes for the internal functions.
89 */
90 int okm_get_req(okms_t *okmsp, sckm_ioctl_getreq_t *ireqp,
91 intptr_t data, int flag);
92 int okm_process_req(okms_t *okmsp, okm_req_hdr_t *reqp, uint32_t len,
93 sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag);
94 int okm_process_status(okms_t *okmsp, sckm_ioctl_status_t *ireply);
95 void okm_event_handler(scf_event_t event, void *arg);
96 int okm_send_reply(okms_t *okmsp, uint32_t transid, uint32_t status,
97 uint32_t sadb_err, uint32_t sadb_ver);
98 int block_until_ready(okms_t *okmsp);
99 static int okm_copyin_ioctl_getreq(intptr_t userarg,
100 sckm_ioctl_getreq_t *driverarg, int flag);
101 static int okm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg,
102 intptr_t userarg, int flag);
103 static void okm_cleanup(okms_t *okmsp);
104 static int okm_mbox_init(okms_t *okmsp);
105 static void okm_mbox_fini(okms_t *okmsp);
106 static clock_t okm_timeout_val(int error);
107
108
109 struct cb_ops okm_cb_ops = {
110 okm_open, /* open */
111 okm_close, /* close */
112 nodev, /* strategy */
113 nodev, /* print */
114 nodev, /* dump */
115 nodev, /* read */
116 nodev, /* write */
117 okm_ioctl, /* ioctl */
118 nodev, /* devmap */
119 nodev, /* mmap */
120 nodev, /* segmap */
121 nochpoll, /* poll */
122 ddi_prop_op, /* prop_op */
123 0, /* streamtab */
124 D_NEW | D_MP /* Driver compatibility flag */
125 };
126
127 struct dev_ops okm_ops = {
128 DEVO_REV, /* devo_rev, */
129 0, /* refcnt */
130 okm_info, /* get_dev_info */
131 nulldev, /* identify */
132 nulldev, /* probe */
133 okm_attach, /* attach */
134 okm_detach, /* detach */
135 nodev, /* reset */
136 &okm_cb_ops, /* driver operations */
137 (struct bus_ops *)0, /* no bus operations */
138 NULL, /* power */
139 ddi_quiesce_not_needed, /* quiesce */
140 };
141
142 struct modldrv modldrv = {
143 &mod_driverops,
144 "OPL Key Management Driver",
145 &okm_ops,
146 };
147
148 struct modlinkage modlinkage = {
149 MODREV_1,
150 &modldrv,
151 NULL
152 };
153
154
155 /*
156 * _init - Module's init routine.
157 */
158 int
_init(void)159 _init(void)
160 {
161 int ret;
162
163 if ((ret = mod_install(&modlinkage)) != 0) {
164 cmn_err(CE_WARN, "mod_install failed, error = %d", ret);
165 }
166 return (ret);
167 }
168
169 /*
170 * _fini - Module's fini routine.
171 */
172 int
_fini(void)173 _fini(void)
174 {
175 int ret;
176
177 if ((ret = mod_remove(&modlinkage)) != 0) {
178 return (ret);
179 }
180 return (ret);
181 }
182
183 /*
184 * _info - Module's info routine.
185 */
186 int
_info(struct modinfo * modinfop)187 _info(struct modinfo *modinfop)
188 {
189 return (mod_info(&modlinkage, modinfop));
190 }
191
192 /*
193 * okm_attach - Module's attach routine.
194 *
195 * Description: Initializes the modules state structure and create
196 * the minor device node.
197 */
198 int
okm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)199 okm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
200 {
201 int instance;
202 okms_t *okmsp = &okms_global;
203
204 instance = ddi_get_instance(dip);
205
206 /* Only one instance is supported. */
207 if (instance != 0) {
208 return (DDI_FAILURE);
209 }
210
211 if (cmd != DDI_ATTACH) {
212 return (DDI_FAILURE);
213 }
214
215 okmsp->km_dip = dip;
216 okmsp->km_major = ddi_driver_major(dip);
217 okmsp->km_inst = instance;
218
219 /*
220 * Get an interrupt block cookie corresponding to the
221 * interrupt priority of the event handler.
222 * Assert that the event priority is not redefined to
223 * some other priority.
224 */
225 /* LINTED */
226 ASSERT(SCF_EVENT_PRI == DDI_SOFTINT_LOW);
227 if (ddi_get_soft_iblock_cookie(dip, SCF_EVENT_PRI,
228 &okmsp->km_ibcookie) != DDI_SUCCESS) {
229 cmn_err(CE_WARN, "ddi_get_soft_iblock_cookie failed.");
230 return (DDI_FAILURE);
231 }
232 mutex_init(&okmsp->km_lock, NULL, MUTEX_DRIVER,
233 (void *)okmsp->km_ibcookie);
234 okmsp->km_clean |= OKM_CLEAN_LOCK;
235 cv_init(&okmsp->km_wait, NULL, CV_DRIVER, NULL);
236 okmsp->km_clean |= OKM_CLEAN_CV;
237
238 /*
239 * set clean_node ahead as remove_node has to be called even
240 * if create node fails.
241 */
242 okmsp->km_clean |= OKM_CLEAN_NODE;
243 if (ddi_create_minor_node(dip, OKM_NODENAME, S_IFCHR,
244 instance, NULL, NULL) == DDI_FAILURE) {
245 cmn_err(CE_WARN, "Device node creation failed");
246 okm_cleanup(okmsp);
247 return (DDI_FAILURE);
248 }
249
250 ddi_set_driver_private(dip, (caddr_t)okmsp);
251 ddi_report_dev(dip);
252 return (DDI_SUCCESS);
253 }
254
255 /*
256 * okm_detach - Module's detach routine.
257 *
258 * Description: Cleans up the module's state structures and any other
259 * relevant data.
260 */
261 int
okm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)262 okm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
263 {
264 okms_t *okmsp;
265
266 if (cmd != DDI_DETACH) {
267 return (DDI_FAILURE);
268 }
269
270 if ((okmsp = ddi_get_driver_private(dip)) == NULL) {
271 return (DDI_FAILURE);
272 }
273
274 mutex_enter(&okmsp->km_lock);
275 /*
276 * Check if the mailbox is still in use.
277 */
278 if (okmsp->km_state & OKM_MB_INITED) {
279 mutex_exit(&okmsp->km_lock);
280 cmn_err(CE_WARN, "Detach failure: Mailbox in use");
281 return (DDI_FAILURE);
282 }
283 mutex_exit(&okmsp->km_lock);
284 okm_cleanup(okmsp);
285 ddi_set_driver_private(dip, NULL);
286 return (DDI_SUCCESS);
287 }
288
289 /*
290 * okm_info - Module's info routine.
291 */
292 /* ARGSUSED */
293 int
okm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)294 okm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
295 {
296 okms_t *okmsp = &okms_global;
297 minor_t minor;
298 int ret = DDI_FAILURE;
299
300 switch (infocmd) {
301 case DDI_INFO_DEVT2DEVINFO:
302 /*
303 * We have the case here where the minor number
304 * is the same as the instance number. So, just
305 * make sure we have the right minor node in our
306 * global state. If we don't, set the result to NULL.
307 */
308 minor = getminor((dev_t)arg);
309 if (okmsp->km_inst != minor) {
310 *result = NULL;
311 } else {
312 *result = okmsp->km_dip;
313 ret = DDI_SUCCESS;
314 }
315 break;
316
317 case DDI_INFO_DEVT2INSTANCE:
318 minor = getminor((dev_t)arg);
319 *result = (void *)(uintptr_t)minor;
320 ret = DDI_SUCCESS;
321
322 default:
323 break;
324 }
325 return (ret);
326 }
327
328 /*
329 * okm_open - Device open routine.
330 *
331 * Description: Initializes the mailbox and waits until the mailbox
332 * gets connected. Only one open at a time is supported.
333 */
334 /*ARGSUSED*/
335 int
okm_open(dev_t * devp,int flag,int otyp,struct cred * cred)336 okm_open(dev_t *devp, int flag, int otyp, struct cred *cred)
337 {
338 okms_t *okmsp = &okms_global;
339 int ret = 0;
340
341 DPRINTF(DBG_DRV, ("okm_open: called\n"));
342 mutex_enter(&okmsp->km_lock);
343 if (okmsp->km_state & OKM_OPENED) {
344 /* Only one open supported */
345 mutex_exit(&okmsp->km_lock);
346 DPRINTF(DBG_WARN, ("okm_open: already opened\n"));
347 return (EBUSY);
348 }
349 okmsp->km_state |= OKM_OPENED;
350 ret = block_until_ready(okmsp);
351 if (ret != 0) {
352 okmsp->km_state &= ~OKM_OPENED;
353 }
354 mutex_exit(&okmsp->km_lock);
355 DPRINTF(DBG_DRV, ("okm_open: ret=%d\n", ret));
356 return (ret);
357 }
358
359 /*
360 * block_until_ready - Function to wait until the mailbox is ready to use.
361 *
362 * Description: It initializes the mailbox and waits for the mailbox
363 * state to transition to connected.
364 */
365 int
block_until_ready(okms_t * okmsp)366 block_until_ready(okms_t *okmsp)
367 {
368 int ret = 0;
369
370 DPRINTF(DBG_DRV, ("block_until_ready: called\n"));
371 ASSERT(MUTEX_HELD(&okmsp->km_lock));
372
373 if (okmsp->km_state & OKM_MB_DISC) {
374 DPRINTF(DBG_DRV, ("block_until_ready: closing the mailbox\n"));
375 okm_mbox_fini(okmsp);
376 }
377 if (okmsp->km_state & OKM_MB_CONN) {
378 DPRINTF(DBG_DRV, ("block_until_ready: mailbox connected\n"));
379 return (0);
380 }
381 /*
382 * Initialize mailbox.
383 */
384 if ((ret = okm_mbox_init(okmsp)) != 0) {
385 DPRINTF(DBG_MBOX,
386 ("block_until_ready: mailbox init failed ret=%d\n", ret));
387 return (ret);
388 }
389 DPRINTF(DBG_DRV, ("block_until_ready: ret=%d", ret));
390 return (ret);
391 }
392
393 /*
394 * okm_close - Device close routine.
395 *
396 * Description: Closes the mailbox.
397 */
398 /*ARGSUSED*/
399 int
okm_close(dev_t dev,int flag,int otyp,struct cred * cred)400 okm_close(dev_t dev, int flag, int otyp, struct cred *cred)
401 {
402 okms_t *okmsp = &okms_global;
403
404 DPRINTF(DBG_DRV, ("okm_close: called\n"));
405 /* Close the lower layer first */
406 mutex_enter(&okmsp->km_lock);
407 okm_mbox_fini(okmsp);
408 okmsp->km_state = 0;
409 mutex_exit(&okmsp->km_lock);
410 return (0);
411 }
412
413
414 /*
415 * okm_ioctl - Device ioctl routine.
416 *
417 * Description: Processes ioctls from the daemon.
418 */
419 /*ARGSUSED*/
420 int
okm_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cred,int * rvalp)421 okm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp)
422 {
423 okms_t *okmsp = &okms_global;
424 sckm_ioctl_getreq_t ireq;
425 sckm_ioctl_status_t istatus;
426 int ret = 0;
427
428 switch (cmd) {
429 case SCKM_IOCTL_GETREQ:
430
431 DPRINTF(DBG_DRV, ("okm_ioctl: GETREQ\n"));
432 if (okm_copyin_ioctl_getreq(data, &ireq, flag)) {
433 return (EFAULT);
434 }
435
436 ret = okm_get_req(okmsp, &ireq, data, flag);
437 DPRINTF(DBG_DRV, ("okm_ioctl: GETREQ ret=%d\n", ret));
438 break;
439
440 case SCKM_IOCTL_STATUS:
441
442 DPRINTF(DBG_DRV, ("okm_ioctl: STATUS\n"));
443 if (ddi_copyin((caddr_t)data, &istatus,
444 sizeof (sckm_ioctl_status_t), flag)) {
445 return (EFAULT);
446 }
447 ret = okm_process_status(okmsp, &istatus);
448 DPRINTF(DBG_DRV, ("okm_ioctl: STATUS ret=%d\n", ret));
449 break;
450
451 default:
452 DPRINTF(DBG_DRV, ("okm_ioctl: UNKNOWN ioctl\n"));
453 ret = EINVAL;
454 }
455 return (ret);
456 }
457
458 /*
459 * okm_get_req - Get a request from the mailbox.
460 *
461 * Description: It blocks until a message is received, then processes
462 * the message and returns it to the requestor.
463 */
464 int
okm_get_req(okms_t * okmsp,sckm_ioctl_getreq_t * ireqp,intptr_t data,int flag)465 okm_get_req(okms_t *okmsp, sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag)
466 {
467 okm_req_hdr_t *reqp;
468 caddr_t msgbuf;
469 uint32_t len;
470 int ret;
471
472 DPRINTF(DBG_DRV, ("okm_getreq: called\n"));
473 mutex_enter(&okmsp->km_lock);
474 if ((ret = block_until_ready(okmsp)) != 0) {
475 mutex_exit(&okmsp->km_lock);
476 DPRINTF(DBG_WARN, ("okm_getreq: failed ret=%d\n", ret));
477 return (ret);
478 }
479
480 if (okmsp->km_reqp != NULL) {
481 DPRINTF(DBG_DRV, ("okm_getreq: req cached\n"));
482 reqp = okmsp->km_reqp;
483 len = okmsp->km_reqlen;
484 okmsp->km_reqp = NULL;
485 okmsp->km_reqlen = 0;
486 } else {
487 retry:
488 while (OKM_MBOX_READY(okmsp) &&
489 ((ret = scf_mb_canget(okmsp->km_target,
490 okmsp->km_key, &len)) != 0)) {
491 if (ret != ENOMSG) {
492 DPRINTF(DBG_WARN, ("okm_getreq: Unknown "
493 "mbox failure=%d\n", ret));
494 mutex_exit(&okmsp->km_lock);
495 return (EIO);
496 }
497 DPRINTF(DBG_MBOX, ("okm_getreq: waiting for mesg\n"));
498 if (cv_wait_sig(&okmsp->km_wait,
499 &okmsp->km_lock) <= 0) {
500 mutex_exit(&okmsp->km_lock);
501 DPRINTF(DBG_DRV, ("okm_getreq:interrupted\n"));
502 return (EINTR);
503 }
504 }
505 if (!OKM_MBOX_READY(okmsp)) {
506 mutex_exit(&okmsp->km_lock);
507 DPRINTF(DBG_WARN, ("okm_getreq: mailbox not ready\n"));
508 return (EIO);
509 }
510 ASSERT(len != 0);
511 msgbuf = kmem_alloc(len, KM_SLEEP);
512 okmsp->km_sg_rcv.msc_dptr = msgbuf;
513 okmsp->km_sg_rcv.msc_len = len;
514
515 DPRINTF(DBG_MBOX, ("okm_getreq: getmsg\n"));
516 ret = scf_mb_getmsg(okmsp->km_target, okmsp->km_key, len, 1,
517 &okmsp->km_sg_rcv, 0);
518 if (ret == ENOMSG || ret == EMSGSIZE) {
519 kmem_free(msgbuf, len);
520 DPRINTF(DBG_MBOX, ("okm_getreq: nomsg ret=%d\n", ret));
521 goto retry;
522 } else if (ret != 0) {
523 kmem_free(msgbuf, len);
524 mutex_exit(&okmsp->km_lock);
525 DPRINTF(DBG_WARN,
526 ("okm_getreq: Unknown mbox failure=%d\n", ret));
527 return (EIO);
528 }
529
530 /* check message length */
531 if (len < sizeof (okm_req_hdr_t)) {
532 /* protocol error, drop message */
533 kmem_free(msgbuf, len);
534 mutex_exit(&okmsp->km_lock);
535 DPRINTF(DBG_WARN, ("okm_getreq: Bad message\n"));
536 return (EBADMSG);
537 }
538
539 reqp = (okm_req_hdr_t *)msgbuf;
540 reqp->krq_version = ntohl(reqp->krq_version);
541 reqp->krq_transid = ntohl(reqp->krq_transid);
542 reqp->krq_cmd = ntohl(reqp->krq_cmd);
543 reqp->krq_reserved = ntohl(reqp->krq_reserved);
544
545 /* check version of the message received */
546 if (reqp->krq_version != OKM_PROTOCOL_VERSION) {
547 (void) okm_send_reply(okmsp, reqp->krq_transid,
548 OKM_ERR_VERSION, 0, 0);
549 kmem_free(msgbuf, len);
550 mutex_exit(&okmsp->km_lock);
551 DPRINTF(DBG_WARN, ("okm_getreq: Unknown version=%d\n",
552 reqp->krq_version));
553 return (EBADMSG);
554 }
555 }
556
557 /* process message */
558 ret = okm_process_req(okmsp, reqp, len, ireqp, data, flag);
559 if (okmsp->km_reqp == NULL) {
560 /*
561 * The message is not saved, so free the buffer.
562 */
563 kmem_free(reqp, len);
564 }
565 mutex_exit(&okmsp->km_lock);
566 DPRINTF(DBG_DRV, ("okm_getreq: ret=%d\n", ret));
567 return (ret);
568 }
569
570
571 /*
572 * okm_process_req - Process the request.
573 *
574 * Description: Validate the request and then give the request to the
575 * daemon.
576 */
577 int
okm_process_req(okms_t * okmsp,okm_req_hdr_t * reqp,uint32_t len,sckm_ioctl_getreq_t * ireqp,intptr_t data,int flag)578 okm_process_req(okms_t *okmsp, okm_req_hdr_t *reqp, uint32_t len,
579 sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag)
580 {
581 void *req_datap = (void *)(((char *)reqp) + sizeof (okm_req_hdr_t));
582 int sadb_msglen = len - sizeof (okm_req_hdr_t);
583
584 DPRINTF(DBG_DRV, ("okm_process_req: called\n"));
585 DUMP_REQ(reqp, len);
586
587 switch (reqp->krq_cmd) {
588 case OKM_MSG_SADB:
589 /* sanity check request */
590 if (sadb_msglen <= 0) {
591 (void) okm_send_reply(okmsp, reqp->krq_transid,
592 OKM_ERR_SADB_MSG, 0, 0);
593 DPRINTF(DBG_WARN, ("okm_process_req: bad message\n"));
594 return (EBADMSG);
595 }
596
597 /*
598 * Save the message, prior to giving it to the daemon.
599 */
600 okmsp->km_reqp = reqp;
601 okmsp->km_reqlen = len;
602
603 if (ireqp->buf_len < len) {
604 DPRINTF(DBG_WARN,
605 ("okm_process_req: not enough space\n"));
606 return (ENOSPC);
607 }
608
609 ireqp->transid = reqp->krq_transid;
610 ireqp->type = SCKM_IOCTL_REQ_SADB;
611 if (ddi_copyout(req_datap, ireqp->buf, sadb_msglen, flag)) {
612 DPRINTF(DBG_WARN,
613 ("okm_process_req: copyout failed\n"));
614 return (EFAULT);
615 }
616 ireqp->buf_len = sadb_msglen;
617 if (okm_copyout_ioctl_getreq(ireqp, data, flag)) {
618 DPRINTF(DBG_WARN,
619 ("okm_process_req: copyout failed\n"));
620 return (EFAULT);
621 }
622 break;
623
624 default:
625 cmn_err(CE_WARN, "Unknown cmd 0x%x received", reqp->krq_cmd);
626 /*
627 * Received an unknown command, send corresponding
628 * error message.
629 */
630 (void) okm_send_reply(okmsp, reqp->krq_transid,
631 OKM_ERR_BAD_CMD, 0, 0);
632 return (EBADMSG);
633 }
634 DPRINTF(DBG_DRV, ("okm_process_req: ret=0\n"));
635 return (0);
636 }
637
638 /*
639 * okm_process_status - Process the status from the daemon.
640 *
641 * Description: Processes the status received from the daemon and sends
642 * corresponding message to the SP.
643 */
644 int
okm_process_status(okms_t * okmsp,sckm_ioctl_status_t * ireply)645 okm_process_status(okms_t *okmsp, sckm_ioctl_status_t *ireply)
646 {
647 uint32_t status;
648 uint32_t sadb_msg_errno = 0;
649 uint32_t sadb_msg_version = 0;
650 okm_req_hdr_t *reqp = okmsp->km_reqp;
651 int ret;
652
653 DPRINTF(DBG_DRV, ("okm_process_status: called\n"));
654 mutex_enter(&okmsp->km_lock);
655 if ((ret = block_until_ready(okmsp)) != 0) {
656 mutex_exit(&okmsp->km_lock);
657 DPRINTF(DBG_WARN,
658 ("okm_process_status: Unknown failure=%d\n", ret));
659 return (ret);
660 }
661
662 /* fail if no status is expected, or if it does not match */
663 if (!okmsp->km_reqp || (reqp->krq_transid != ireply->transid)) {
664 mutex_exit(&okmsp->km_lock);
665 DPRINTF(DBG_WARN,
666 ("okm_process_status: req/transid mismatch\n"));
667 return (EINVAL);
668 }
669
670 switch (ireply->status) {
671 case SCKM_IOCTL_STAT_SUCCESS:
672 DPRINTF(DBG_DRV, ("okm_process_status: SUCCESS\n"));
673 status = OKM_SUCCESS;
674 break;
675 case SCKM_IOCTL_STAT_ERR_PFKEY:
676 DPRINTF(DBG_DRV, ("okm_process_status: PFKEY ERROR\n"));
677 status = OKM_ERR_SADB_PFKEY;
678 sadb_msg_errno = ireply->sadb_msg_errno;
679 break;
680 case SCKM_IOCTL_STAT_ERR_REQ:
681 DPRINTF(DBG_DRV, ("okm_process_status: REQ ERROR\n"));
682 status = OKM_ERR_DAEMON;
683 break;
684 case SCKM_IOCTL_STAT_ERR_VERSION:
685 DPRINTF(DBG_DRV, ("okm_process_status: SADB VERSION ERROR\n"));
686 status = OKM_ERR_SADB_VERSION;
687 sadb_msg_version = ireply->sadb_msg_version;
688 break;
689 case SCKM_IOCTL_STAT_ERR_TIMEOUT:
690 DPRINTF(DBG_DRV, ("okm_process_status: TIMEOUT ERR\n"));
691 status = OKM_ERR_SADB_TIMEOUT;
692 break;
693 case SCKM_IOCTL_STAT_ERR_OTHER:
694 DPRINTF(DBG_DRV, ("okm_process_status: OTHER ERR\n"));
695 status = OKM_ERR_DAEMON;
696 break;
697 case SCKM_IOCTL_STAT_ERR_SADB_TYPE:
698 DPRINTF(DBG_DRV, ("okm_process_status: SADB TYPE ERR\n"));
699 status = OKM_ERR_SADB_BAD_TYPE;
700 break;
701 default:
702 cmn_err(CE_WARN, "SCKM daemon returned invalid status %d\n",
703 ireply->status);
704 status = OKM_ERR_DAEMON;
705 }
706 ret = okm_send_reply(okmsp, ireply->transid, status,
707 sadb_msg_errno, sadb_msg_version);
708 /*
709 * Clean up the cached request now.
710 */
711 if (ret == 0) {
712 kmem_free(okmsp->km_reqp, okmsp->km_reqlen);
713 okmsp->km_reqp = NULL;
714 okmsp->km_reqlen = 0;
715 }
716 mutex_exit(&okmsp->km_lock);
717 DPRINTF(DBG_DRV, ("okm_process_status: ret=%d\n", ret));
718 return (ret);
719 }
720
721 /*
722 * okm_copyin_ioctl_getreq - copy-in the ioctl request from the daemon.
723 */
724
725 static int
okm_copyin_ioctl_getreq(intptr_t userarg,sckm_ioctl_getreq_t * driverarg,int flag)726 okm_copyin_ioctl_getreq(intptr_t userarg, sckm_ioctl_getreq_t *driverarg,
727 int flag)
728 {
729 #ifdef _MULTI_DATAMODEL
730 switch (ddi_model_convert_from(flag & FMODELS)) {
731 case DDI_MODEL_ILP32: {
732 sckm_ioctl_getreq32_t driverarg32;
733 if (ddi_copyin((caddr_t)userarg, &driverarg32,
734 sizeof (sckm_ioctl_getreq32_t), flag)) {
735 return (EFAULT);
736 }
737 driverarg->transid = driverarg32.transid;
738 driverarg->type = driverarg32.type;
739 driverarg->buf = (caddr_t)(uintptr_t)driverarg32.buf;
740 driverarg->buf_len = driverarg32.buf_len;
741 break;
742 }
743 case DDI_MODEL_NONE: {
744 if (ddi_copyin((caddr_t)userarg, &driverarg,
745 sizeof (sckm_ioctl_getreq_t), flag)) {
746 return (EFAULT);
747 }
748 break;
749 }
750 }
751 #else /* ! _MULTI_DATAMODEL */
752 if (ddi_copyin((caddr_t)userarg, &driverarg,
753 sizeof (sckm_ioctl_getreq_t), flag)) {
754 return (EFAULT);
755 }
756 #endif /* _MULTI_DATAMODEL */
757 return (0);
758 }
759
760
761 /*
762 * okm_copyout_ioctl_getreq - copy-out the request to the daemon.
763 */
764 static int
okm_copyout_ioctl_getreq(sckm_ioctl_getreq_t * driverarg,intptr_t userarg,int flag)765 okm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg, intptr_t userarg,
766 int flag)
767 {
768 #ifdef _MULTI_DATAMODEL
769 switch (ddi_model_convert_from(flag & FMODELS)) {
770 case DDI_MODEL_ILP32: {
771 sckm_ioctl_getreq32_t driverarg32;
772 driverarg32.transid = driverarg->transid;
773 driverarg32.type = driverarg->type;
774 driverarg32.buf = (caddr32_t)(uintptr_t)driverarg->buf;
775 driverarg32.buf_len = driverarg->buf_len;
776 if (ddi_copyout(&driverarg32, (caddr_t)userarg,
777 sizeof (sckm_ioctl_getreq32_t), flag)) {
778 return (EFAULT);
779 }
780 break;
781 }
782 case DDI_MODEL_NONE:
783 if (ddi_copyout(driverarg, (caddr_t)userarg,
784 sizeof (sckm_ioctl_getreq_t), flag)) {
785 return (EFAULT);
786 }
787 break;
788 }
789 #else /* ! _MULTI_DATAMODEL */
790 if (ddi_copyout(driverarg, (caddr_t)userarg,
791 sizeof (sckm_ioctl_getreq_t), flag)) {
792 return (EFAULT);
793 }
794 #endif /* _MULTI_DATAMODEL */
795 return (0);
796 }
797
798 /*
799 * okm_cleanup - Cleanup routine.
800 */
801 static void
okm_cleanup(okms_t * okmsp)802 okm_cleanup(okms_t *okmsp)
803 {
804
805 ASSERT(okmsp != NULL);
806 if (okmsp->km_clean & OKM_CLEAN_NODE) {
807 ddi_remove_minor_node(okmsp->km_dip, NULL);
808 }
809 if (okmsp->km_clean & OKM_CLEAN_LOCK)
810 mutex_destroy(&okmsp->km_lock);
811 if (okmsp->km_clean & OKM_CLEAN_CV)
812 cv_destroy(&okmsp->km_wait);
813 if (okmsp->km_reqp != NULL) {
814 kmem_free(okmsp->km_reqp, okmsp->km_reqlen);
815 okmsp->km_reqp = NULL;
816 okmsp->km_reqlen = 0;
817 }
818 ddi_set_driver_private(okmsp->km_dip, NULL);
819 }
820
821 /*
822 * okm_mbox_init - Mailbox specific initialization.
823 */
824 static int
okm_mbox_init(okms_t * okmsp)825 okm_mbox_init(okms_t *okmsp)
826 {
827 int ret;
828 clock_t tout;
829
830 ASSERT(MUTEX_HELD(&okmsp->km_lock));
831 okmsp->km_target = OKM_TARGET_ID;
832 okmsp->km_key = DKMD_KEY;
833 okmsp->km_state &= ~OKM_MB_INITED;
834
835 /* Iterate until mailbox gets connected */
836 while (!(okmsp->km_state & OKM_MB_CONN)) {
837 DPRINTF(DBG_MBOX, ("okm_mbox_init: calling mb_init\n"));
838 ret = scf_mb_init(okmsp->km_target, okmsp->km_key,
839 okm_event_handler, (void *)okmsp);
840 DPRINTF(DBG_MBOX, ("okm_mbox_init: mb_init ret=%d\n", ret));
841
842 if (ret != 0) {
843 DPRINTF(DBG_MBOX,
844 ("okm_mbox_init: failed ret =%d\n", ret));
845 DTRACE_PROBE1(okm_mbox_fail, int, ret);
846 } else {
847 okmsp->km_state |= OKM_MB_INITED;
848
849 /* Block until the mailbox is ready to communicate. */
850 while (!(okmsp->km_state &
851 (OKM_MB_CONN | OKM_MB_DISC))) {
852
853 if (cv_wait_sig(&okmsp->km_wait,
854 &okmsp->km_lock) <= 0) {
855 /* interrupted */
856 ret = EINTR;
857 break;
858 }
859 }
860 }
861
862 if ((ret != 0) || (okmsp->km_state & OKM_MB_DISC)) {
863
864 if (okmsp->km_state & OKM_MB_INITED) {
865 (void) scf_mb_fini(okmsp->km_target,
866 okmsp->km_key);
867 }
868 if (okmsp->km_state & OKM_MB_DISC) {
869 DPRINTF(DBG_WARN,
870 ("okm_mbox_init: mbox DISC_ERROR\n"));
871 DTRACE_PROBE1(okm_mbox_fail,
872 int, OKM_MB_DISC);
873 }
874
875 okmsp->km_state &= ~(OKM_MB_INITED | OKM_MB_DISC |
876 OKM_MB_CONN);
877
878 if (ret == EINTR) {
879 return (ret);
880 }
881
882 /*
883 * If there was failure, then wait for
884 * OKM_MB_TOUT secs and retry again.
885 */
886
887 DPRINTF(DBG_MBOX, ("okm_mbox_init: waiting...\n"));
888 tout = drv_usectohz(OKM_MB_TOUT);
889 ret = cv_reltimedwait_sig(&okmsp->km_wait,
890 &okmsp->km_lock, tout, TR_CLOCK_TICK);
891 if (ret == 0) {
892 /* if interrupted, return immediately. */
893 DPRINTF(DBG_MBOX,
894 ("okm_mbox_init: interrupted\n"));
895 return (EINTR);
896 }
897 }
898 }
899
900 ret = scf_mb_ctrl(okmsp->km_target, okmsp->km_key,
901 SCF_MBOP_MAXMSGSIZE, &okmsp->km_maxsz);
902
903 /*
904 * The max msg size should be at least the size of reply
905 * we need to send.
906 */
907 if ((ret == 0) && (okmsp->km_maxsz < sizeof (okm_rep_hdr_t))) {
908 cmn_err(CE_WARN, "Max message size expected >= %ld "
909 "but found %d\n", sizeof (okm_rep_hdr_t), okmsp->km_maxsz);
910 ret = EIO;
911 }
912 if (ret != 0) {
913 okmsp->km_state &= ~OKM_MB_INITED;
914 (void) scf_mb_fini(okmsp->km_target, okmsp->km_key);
915 }
916 DPRINTF(DBG_MBOX, ("okm_mbox_init: mb_init ret=%d\n", ret));
917 return (ret);
918 }
919
920 /*
921 * okm_mbox_fini - Mailbox de-initialization.
922 */
923 static void
okm_mbox_fini(okms_t * okmsp)924 okm_mbox_fini(okms_t *okmsp)
925 {
926 int ret = 0;
927
928 ASSERT(MUTEX_HELD(&okmsp->km_lock));
929 if (okmsp->km_state & OKM_MB_INITED) {
930 DPRINTF(DBG_MBOX, ("okm_mbox_fini: calling mb_fini\n"));
931 ret = scf_mb_fini(okmsp->km_target, okmsp->km_key);
932 DPRINTF(DBG_MBOX, ("okm_mbox_fini: mb_fini ret=%d\n", ret));
933 if (ret != 0) {
934 cmn_err(CE_WARN,
935 "Failed to close the Mailbox error=%d", ret);
936 }
937 okmsp->km_state &= ~(OKM_MB_INITED | OKM_MB_CONN | OKM_MB_DISC);
938 }
939 }
940
941 /*
942 * okm_event_handler - Mailbox event handler.
943 *
944 * Description: Implements a state machine to handle all the mailbox
945 * events. For each event, it sets the appropriate state
946 * flag and wakes up the threads waiting for that event.
947 */
948 void
okm_event_handler(scf_event_t event,void * arg)949 okm_event_handler(scf_event_t event, void *arg)
950 {
951 okms_t *okmsp = (okms_t *)arg;
952
953 DPRINTF(DBG_MBOX, ("okm_event_handler: called\n"));
954 ASSERT(okmsp != NULL);
955 mutex_enter(&okmsp->km_lock);
956 if (!(okmsp->km_state & OKM_MB_INITED)) {
957 /*
958 * Ignore all events if the state flag indicates that the
959 * mailbox not initialized, this may happen during the close.
960 */
961 mutex_exit(&okmsp->km_lock);
962 DPRINTF(DBG_MBOX,
963 ("okm_event_handler: event=0x%X - mailbox not inited \n",
964 event));
965 return;
966 }
967 switch (event) {
968 case SCF_MB_CONN_OK:
969 DPRINTF(DBG_MBOX, ("okm_event_handler: Event CONN_OK\n"));
970 /*
971 * Now the mailbox is ready to use, lets wake up
972 * any one waiting for this event.
973 */
974 okmsp->km_state |= OKM_MB_CONN;
975 cv_broadcast(&okmsp->km_wait);
976 break;
977
978 case SCF_MB_MSG_DATA:
979 DPRINTF(DBG_MBOX, ("okm_event_handler: Event MSG_DATA\n"));
980 /*
981 * A message is available in the mailbox,
982 * wakeup if any one is ready to read the message.
983 */
984 if (OKM_MBOX_READY(okmsp)) {
985 cv_broadcast(&okmsp->km_wait);
986 }
987 break;
988
989 case SCF_MB_SPACE:
990 DPRINTF(DBG_MBOX, ("okm_event_handler: Event MB_SPACE\n"));
991 /*
992 * Now the mailbox is ready to transmit, lets
993 * wakeup if any one is waiting to write.
994 */
995 if (OKM_MBOX_READY(okmsp)) {
996 cv_broadcast(&okmsp->km_wait);
997 }
998 break;
999 case SCF_MB_DISC_ERROR:
1000 DPRINTF(DBG_MBOX, ("okm_event_handler: Event DISC_ERROR\n"));
1001 okmsp->km_state &= ~OKM_MB_CONN;
1002 okmsp->km_state |= OKM_MB_DISC;
1003 cv_broadcast(&okmsp->km_wait);
1004 break;
1005 default:
1006 cmn_err(CE_WARN, "Unexpected event received\n");
1007 }
1008 mutex_exit(&okmsp->km_lock);
1009 }
1010
1011 /*
1012 * okm_send_reply - Send a mailbox reply message.
1013 */
1014 int
okm_send_reply(okms_t * okmsp,uint32_t transid,uint32_t status,uint32_t sadb_err,uint32_t sadb_ver)1015 okm_send_reply(okms_t *okmsp, uint32_t transid,
1016 uint32_t status, uint32_t sadb_err, uint32_t sadb_ver)
1017 {
1018 okm_rep_hdr_t reply;
1019 int ret = EIO;
1020
1021 DPRINTF(DBG_DRV, ("okm_send_reply: called\n"));
1022 ASSERT(MUTEX_HELD(&okmsp->km_lock));
1023 reply.krp_version = htonl(OKM_PROTOCOL_VERSION);
1024 reply.krp_transid = htonl(transid);
1025 reply.krp_status = htonl(status);
1026 reply.krp_sadb_errno = htonl(sadb_err);
1027 reply.krp_sadb_version = htonl(sadb_ver);
1028 okmsp->km_sg_tx.msc_dptr = (caddr_t)&reply;
1029 okmsp->km_sg_tx.msc_len = sizeof (reply);
1030 DUMP_REPLY(&reply);
1031
1032 while (OKM_MBOX_READY(okmsp)) {
1033 DPRINTF(DBG_MBOX, ("okm_send_reply: sending reply\n"));
1034 ret = scf_mb_putmsg(okmsp->km_target, okmsp->km_key,
1035 sizeof (reply), 1, &okmsp->km_sg_tx, 0);
1036 DPRINTF(DBG_MBOX, ("okm_send_reply: putmsg ret=%d\n", ret));
1037 if (ret == EBUSY || ret == ENOSPC) {
1038 /* mailbox is busy, poll/retry */
1039 if (cv_timedwait_sig(&okmsp->km_wait,
1040 &okmsp->km_lock, okm_timeout_val(ret)) == 0) {
1041 /* interrupted */
1042 ret = EINTR;
1043 DPRINTF(DBG_DRV,
1044 ("okm_send_reply: interrupted\n"));
1045 break;
1046 }
1047 } else {
1048 break;
1049 }
1050 }
1051 DPRINTF(DBG_DRV, ("okm_send_reply: ret=%d\n", ret));
1052 return (ret);
1053 }
1054
1055 /*
1056 * okm_timeout_val -- Return appropriate timeout value.
1057 *
1058 * A small timeout value is returned for EBUSY as the mailbox busy
1059 * condition may go away sooner and we are expected to poll.
1060 *
1061 * A larger timeout value is returned for ENOSPC case, as the condition
1062 * depends on the peer to release buffer space.
1063 * NOTE: there will also be an event(SCF_MB_SPACE) but a timeout is
1064 * used for reliability purposes.
1065 */
1066 static clock_t
okm_timeout_val(int error)1067 okm_timeout_val(int error)
1068 {
1069 clock_t tval;
1070
1071 ASSERT(error == EBUSY || error == ENOSPC);
1072
1073 if (error == EBUSY) {
1074 tval = OKM_SM_TOUT;
1075 } else {
1076 tval = OKM_LG_TOUT;
1077 }
1078 return (drv_usectohz(tval));
1079 }
1080
1081 #ifdef DEBUG
1082 static void
okm_print_req(okm_req_hdr_t * reqp,uint32_t len)1083 okm_print_req(okm_req_hdr_t *reqp, uint32_t len)
1084 {
1085 uint8_t *datap = (uint8_t *)(((char *)reqp) + sizeof (okm_req_hdr_t));
1086 int msglen = len - sizeof (okm_req_hdr_t);
1087 int i, j;
1088 #define BYTES_PER_LINE 20
1089 char bytestr[BYTES_PER_LINE * 3 + 1];
1090
1091 if (!(okm_debug & DBG_MESG))
1092 return;
1093 printf("OKM: Request ver=%d transid=%d cmd=%s\n",
1094 reqp->krq_version, reqp->krq_transid,
1095 ((reqp->krq_cmd == OKM_MSG_SADB) ? "MSG_SADB" : "UNKNOWN"));
1096 for (i = 0; i < msglen; ) {
1097 for (j = 0; (j < BYTES_PER_LINE) && (i < msglen); j++, i++) {
1098 (void) sprintf(&bytestr[j * 3], "%02X ", datap[i]);
1099 }
1100 if (j != 0) {
1101 printf("\t%s\n", bytestr);
1102 }
1103 }
1104 }
1105
1106 static void
okm_print_rep(okm_rep_hdr_t * repp)1107 okm_print_rep(okm_rep_hdr_t *repp)
1108 {
1109 if (!(okm_debug & DBG_MESG))
1110 return;
1111 printf("OKM: Reply Ver=%d Transid=%d Status=%d ",
1112 repp->krp_version, repp->krp_transid, repp->krp_status);
1113 printf("Sadb_errno=%d Sadb_ver=%d\n", repp->krp_sadb_errno,
1114 repp->krp_sadb_version);
1115 }
1116 #endif
1117