xref: /illumos-gate/usr/src/lib/libdlpi/common/libdlpi.c (revision 5703ae8931e75b07e7f34f6ed5b484dbb05a83d4)
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 2006 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 /*
29  * Data-Link Provider Interface (Version 2)
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <poll.h>
40 #include <stropts.h>
41 #include <sys/dlpi.h>
42 #include <errno.h>
43 #include <sys/sysmacros.h>
44 #include <ctype.h>
45 #include <libdlpi.h>
46 #include <libdladm.h>
47 
48 typedef enum dlpi_multi_op {
49 	DLPI_MULTI_DISABLE = 0,
50 	DLPI_MULTI_ENABLE
51 } dlpi_multi_op_t;
52 
53 typedef enum dlpi_promisc_op {
54 	DLPI_PROMISC_OFF = 0,
55 	DLPI_PROMISC_ON
56 } dlpi_promisc_op_t;
57 
58 const char	*i_dlpi_mac_type[] = {
59 	"CSMA/CD",		/* 0x00 */
60 	"Token Bus",		/* 0x01 */
61 	"Token Ring",		/* 0x02 */
62 	"Metro Net",		/* 0x03 */
63 	"Ethernet",		/* 0x04 */
64 	"HDLC",			/* 0x05 */
65 	"Sync Character",	/* 0x06 */
66 	"CTCA",			/* 0x07 */
67 	"FDDI",			/* 0x08 */
68 	"unknown",		/* 0x09 */
69 	"Frame Relay (LAPF)",	/* 0x0a */
70 	"MP Frame Relay",	/* 0x0b */
71 	"Async Character",	/* 0x0c */
72 	"X.25 (Classic IP)",	/* 0x0d */
73 	"Software Loopback",	/* 0x0e */
74 	"undefined",		/* 0x0f */
75 	"Fiber Channel",	/* 0x10 */
76 	"ATM",			/* 0x11 */
77 	"ATM (Classic IP)",	/* 0x12 */
78 	"X.25 (LAPB)",		/* 0x13 */
79 	"ISDN",			/* 0x14 */
80 	"HIPPI",		/* 0x15 */
81 	"100BaseVG Ethernet",	/* 0x16 */
82 	"100BaseVG Token Ring",	/* 0x17 */
83 	"Ethernet/IEEE 802.3",	/* 0x18 */
84 	"100BaseT",		/* 0x19 */
85 	"Infiniband"		/* 0x1a */
86 };
87 
88 static int i_dlpi_ifrm_num(char *, unsigned int *);
89 
90 const char *
91 dlpi_mac_type(uint_t type)
92 {
93 	if (type >= sizeof (i_dlpi_mac_type) / sizeof (i_dlpi_mac_type[0]))
94 		return ("ERROR");
95 
96 	return (i_dlpi_mac_type[type]);
97 }
98 
99 static int
100 strputmsg(int fd, uint8_t *ctl_buf, size_t ctl_len, int flags)
101 {
102 	struct strbuf	ctl;
103 
104 	ctl.buf = (char *)ctl_buf;
105 	ctl.len = ctl_len;
106 
107 	return (putmsg(fd, &ctl, NULL, flags));
108 }
109 
110 static int
111 strgetmsg(int fd, int timeout, char *ctl_buf,
112     size_t *ctl_lenp, char *data_buf, size_t *data_lenp)
113 {
114 	struct strbuf	ctl;
115 	struct strbuf	data;
116 	int		res;
117 	struct pollfd	pfd;
118 	int		flags = 0;
119 
120 	pfd.fd = fd;
121 	pfd.events = POLLIN | POLLPRI;
122 
123 	switch (poll(&pfd, 1, timeout)) {
124 	default:
125 		ctl.buf = ctl_buf;
126 		ctl.len = 0;
127 		ctl.maxlen = (ctl_lenp != NULL) ? *ctl_lenp : 0;
128 
129 		data.buf = data_buf;
130 		data.len = 0;
131 		data.maxlen = (data_lenp != NULL) ? *data_lenp : 0;
132 
133 		if ((res = getmsg(fd, &ctl, &data, &flags)) < 0)
134 			goto failed;
135 
136 		if (ctl_buf != NULL) {
137 			if (res & MORECTL) {
138 				errno = E2BIG;
139 				goto failed;
140 			}
141 
142 			*ctl_lenp = ctl.len;
143 		}
144 
145 		if (data_buf != NULL) {
146 			if (res & MOREDATA) {
147 				errno = E2BIG;
148 				goto failed;
149 			}
150 
151 			*data_lenp = data.len;
152 		}
153 
154 		break;
155 	case 0:
156 		errno = ETIME;
157 		/*FALLTHRU*/
158 	case -1:
159 		goto failed;
160 	}
161 
162 	return (0);
163 failed:
164 	return (-1);
165 }
166 
167 int
168 dlpi_open(const char *provider)
169 {
170 	char		devname[MAXPATHLEN];
171 	char		path[MAXPATHLEN];
172 	int		fd;
173 	struct stat	st;
174 
175 	(void) snprintf(devname, MAXPATHLEN, "/dev/%s", provider);
176 
177 	if ((fd = open(devname, O_RDWR)) != -1)
178 		return (fd);
179 
180 	(void) snprintf(path, MAXPATHLEN, "/devices/pseudo/clone@0:%s",
181 	    provider);
182 
183 	if (stat(path, &st) == 0) {
184 		(void) strlcpy(devname, path, sizeof (devname));
185 		if ((fd = open(devname, O_RDWR)) != -1)
186 			return (fd);
187 	}
188 
189 	return (-1);
190 }
191 
192 int
193 dlpi_close(int fd)
194 {
195 	return (close(fd));
196 }
197 
198 int
199 dlpi_info(int fd, int timeout, dl_info_ack_t *ackp,
200     union DL_qos_types *selp, union DL_qos_types *rangep,
201     uint8_t *addrp, size_t *addrlenp, uint8_t *brdcst_addrp,
202     size_t *brdcst_addrlenp)
203 {
204 	int			rc = -1;
205 	size_t			size;
206 	dl_info_ack_t		*buf;
207 	dl_info_req_t		dlir;
208 	dl_info_ack_t		*dliap;
209 	union DL_qos_types	*uqtp;
210 
211 	size = sizeof (dl_info_ack_t);		/* DL_INFO_ACK */
212 	size += sizeof (union DL_qos_types);	/* QoS selections */
213 	size += sizeof (union DL_qos_types);	/* QoS ranges */
214 	size += MAXADDRLEN + MAXSAPLEN;		/* DLSAP Address */
215 	size += MAXADDRLEN;			/* Broadcast Address */
216 
217 	if ((buf = malloc(size)) == NULL)
218 		return (-1);
219 
220 	dlir.dl_primitive = DL_INFO_REQ;
221 
222 	if (strputmsg(fd, (uint8_t *)&dlir, DL_INFO_REQ_SIZE, RS_HIPRI) == -1)
223 		goto done;
224 
225 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
226 		goto done;
227 
228 	if (size < DL_INFO_ACK_SIZE) {
229 		errno = EBADMSG;
230 		goto done;
231 	}
232 
233 	dliap = (dl_info_ack_t *)buf;
234 	if (dliap->dl_primitive != DL_INFO_ACK ||
235 	    dliap->dl_version != DL_VERSION_2) {
236 		errno = EPROTO;
237 		goto done;
238 	}
239 
240 	(void) memcpy(ackp, buf, DL_INFO_ACK_SIZE);
241 
242 	if (dliap->dl_qos_offset != 0) {
243 		if (dliap->dl_qos_length < sizeof (t_uscalar_t)) {
244 			errno = EPROTO;
245 			goto done;
246 		}
247 
248 		uqtp = (union DL_qos_types *)
249 		    ((uintptr_t)buf + dliap->dl_qos_offset);
250 		if (uqtp->dl_qos_type != DL_QOS_CO_SEL1 &&
251 		    uqtp->dl_qos_type != DL_QOS_CL_SEL1) {
252 			errno = EPROTO;
253 			goto done;
254 		}
255 
256 		if (selp != NULL)
257 			(void) memcpy(selp, (char *)buf + dliap->dl_qos_offset,
258 			    dliap->dl_qos_length);
259 	}
260 
261 	if (dliap->dl_qos_range_offset != 0) {
262 		if (dliap->dl_qos_range_length < sizeof (t_uscalar_t)) {
263 			errno = EPROTO;
264 			goto done;
265 		}
266 
267 		uqtp = (union DL_qos_types *)
268 		    ((uintptr_t)buf + dliap->dl_qos_range_offset);
269 		if (uqtp->dl_qos_type != DL_QOS_CO_RANGE1 &&
270 		    uqtp->dl_qos_type != DL_QOS_CL_RANGE1) {
271 			errno = EPROTO;
272 			goto done;
273 		}
274 
275 		if (rangep != NULL)
276 			(void) memcpy(rangep,
277 			    (char *)buf + dliap->dl_qos_range_offset,
278 			    dliap->dl_qos_range_length);
279 	}
280 
281 	if (dliap->dl_addr_offset != 0) {
282 		if (dliap->dl_addr_length == 0) {
283 			errno = EPROTO;
284 			goto done;
285 		}
286 
287 		if (addrlenp != NULL)
288 			*addrlenp = dliap->dl_addr_length;
289 		if (addrp != NULL)
290 			(void) memcpy(addrp,
291 			    (char *)buf + dliap->dl_addr_offset,
292 			    dliap->dl_addr_length);
293 	}
294 
295 	if (dliap->dl_brdcst_addr_offset != 0) {
296 		if (dliap->dl_brdcst_addr_length == 0) {
297 			errno = EPROTO;
298 			goto done;
299 		}
300 
301 		if (brdcst_addrlenp != NULL)
302 			*brdcst_addrlenp = dliap->dl_brdcst_addr_length;
303 		if (brdcst_addrp != NULL)
304 			(void) memcpy(brdcst_addrp,
305 			    (char *)buf + dliap->dl_brdcst_addr_offset,
306 			    dliap->dl_brdcst_addr_length);
307 	}
308 
309 	rc = 0;	/* success */
310 done:
311 	free(buf);
312 	return (rc);
313 }
314 
315 int
316 dlpi_attach(int fd, int timeout, uint_t ppa)
317 {
318 	int			rc = -1;
319 	size_t			size;
320 	dl_attach_req_t		dlar;
321 	dl_error_ack_t		*dleap;
322 	union DL_primitives	*buf;
323 	union DL_primitives	*udlp;
324 
325 	size = 0;
326 	size = MAX(sizeof (dl_ok_ack_t), size);
327 	size = MAX(sizeof (dl_error_ack_t), size);
328 
329 	if ((buf = malloc(size)) == NULL)
330 		return (-1);
331 
332 	dlar.dl_primitive = DL_ATTACH_REQ;
333 	dlar.dl_ppa = ppa;
334 
335 	if (strputmsg(fd, (uint8_t *)&dlar, DL_ATTACH_REQ_SIZE, 0) == -1)
336 		goto done;
337 
338 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
339 		goto done;
340 
341 	if (size < sizeof (t_uscalar_t)) {
342 		errno = EBADMSG;
343 		goto done;
344 	}
345 
346 	udlp = (union DL_primitives *)buf;
347 	switch (udlp->dl_primitive) {
348 	case DL_OK_ACK:
349 		if (size < DL_OK_ACK_SIZE) {
350 			errno = EBADMSG;
351 			goto done;
352 		}
353 		break;
354 
355 	case DL_ERROR_ACK:
356 		if (size < DL_ERROR_ACK_SIZE) {
357 			errno = EBADMSG;
358 			goto done;
359 		}
360 
361 		dleap = (dl_error_ack_t *)buf;
362 		switch (dleap->dl_errno) {
363 		case DL_BADPPA:
364 			errno = EINVAL;
365 			break;
366 
367 		case DL_ACCESS:
368 			errno = EPERM;
369 			break;
370 
371 		case DL_SYSERR:
372 			errno = dleap->dl_unix_errno;
373 			break;
374 
375 		default:
376 			errno = EPROTO;
377 			break;
378 		}
379 
380 		goto done;
381 
382 	default:
383 		errno = EBADMSG;
384 		goto done;
385 	}
386 
387 	rc = 0;	/* success */
388 done:
389 	free(buf);
390 	return (rc);
391 }
392 
393 int
394 dlpi_detach(int fd, int timeout)
395 {
396 	int			rc = -1;
397 	size_t			size;
398 	dl_detach_req_t		dldr;
399 	dl_error_ack_t		*dleap;
400 	union DL_primitives	*buf;
401 	union DL_primitives	*udlp;
402 
403 	size = 0;
404 	size = MAX(sizeof (dl_ok_ack_t), size);
405 	size = MAX(sizeof (dl_error_ack_t), size);
406 
407 	if ((buf = malloc(size)) == NULL)
408 		return (-1);
409 
410 	dldr.dl_primitive = DL_DETACH_REQ;
411 
412 	if (strputmsg(fd, (uint8_t *)&dldr, DL_DETACH_REQ_SIZE, 0) == -1)
413 		goto done;
414 
415 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
416 		goto done;
417 
418 	if (size < sizeof (t_uscalar_t)) {
419 		errno = EBADMSG;
420 		goto done;
421 	}
422 
423 	udlp = (union DL_primitives *)buf;
424 	switch (udlp->dl_primitive) {
425 	case DL_OK_ACK:
426 		if (size < DL_OK_ACK_SIZE) {
427 			errno = EBADMSG;
428 			goto done;
429 		}
430 		break;
431 
432 	case DL_ERROR_ACK:
433 		if (size < DL_ERROR_ACK_SIZE) {
434 			errno = EBADMSG;
435 			goto done;
436 		}
437 
438 		dleap = (dl_error_ack_t *)buf;
439 		switch (dleap->dl_errno) {
440 		case DL_SYSERR:
441 			errno = dleap->dl_unix_errno;
442 			break;
443 
444 		default:
445 			errno = EPROTO;
446 			break;
447 		}
448 		goto done;
449 
450 	default:
451 		errno = EBADMSG;
452 		goto done;
453 	}
454 
455 	rc = 0;	/* success */
456 done:
457 	free(buf);
458 	return (rc);
459 }
460 
461 int
462 dlpi_bind(int fd, int timeout, uint_t sap, uint16_t mode,
463     boolean_t conn_mgmt, uint32_t *max_conn_ind,
464     uint32_t *xid_test, uint8_t *addrp, size_t *addrlenp)
465 {
466 	int			rc = -1;
467 	size_t			size;
468 	dl_bind_req_t		dlbr;
469 	dl_bind_ack_t		*dlbap;
470 	dl_error_ack_t		*dleap;
471 	union DL_primitives	*buf;
472 	union DL_primitives	*udlp;
473 
474 	size = 0;
475 	size = MAX(sizeof (dl_bind_ack_t) + MAXADDRLEN + MAXSAPLEN, size);
476 	size = MAX(sizeof (dl_error_ack_t), size);
477 
478 	if ((buf = malloc(size)) == NULL)
479 		return (-1);
480 
481 	dlbr.dl_primitive = DL_BIND_REQ;
482 	dlbr.dl_sap = sap;
483 	dlbr.dl_service_mode = mode;
484 	dlbr.dl_conn_mgmt = (conn_mgmt) ? 1 : 0;
485 	dlbr.dl_max_conind = (max_conn_ind != NULL) ? *max_conn_ind : 0;
486 	dlbr.dl_xidtest_flg = (xid_test != NULL) ? *xid_test : 0;
487 
488 	if (strputmsg(fd, (uint8_t *)&dlbr, DL_BIND_REQ_SIZE, 0) == -1)
489 		goto done;
490 
491 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
492 		goto done;
493 
494 	if (size < sizeof (t_uscalar_t)) {
495 		errno = EBADMSG;
496 		goto done;
497 	}
498 
499 	udlp = (union DL_primitives *)buf;
500 	switch (udlp->dl_primitive) {
501 	case DL_BIND_ACK:
502 		if (size < DL_BIND_ACK_SIZE) {
503 			errno = EBADMSG;
504 			goto done;
505 		}
506 
507 		dlbap = (dl_bind_ack_t *)buf;
508 		if (max_conn_ind != NULL)
509 			*max_conn_ind = dlbap->dl_max_conind;
510 		if (xid_test != NULL)
511 			*xid_test = dlbap->dl_xidtest_flg;
512 
513 		if (dlbap->dl_addr_offset != 0) {
514 			if (dlbap->dl_addr_length == 0) {
515 				errno = EPROTO;
516 				goto done;
517 			}
518 
519 			if (addrlenp != NULL)
520 				*addrlenp = dlbap->dl_addr_length;
521 			if (addrp != NULL)
522 				(void) memcpy(addrp,
523 				    (char *)buf + dlbap->dl_addr_offset,
524 				    dlbap->dl_addr_length);
525 		}
526 
527 		break;
528 
529 	case DL_ERROR_ACK:
530 		if (size < DL_ERROR_ACK_SIZE) {
531 			errno = EBADMSG;
532 			goto done;
533 		}
534 
535 		dleap = (dl_error_ack_t *)buf;
536 		switch (dleap->dl_errno) {
537 		case DL_BADADDR:
538 			errno = EINVAL;
539 			break;
540 
541 		case DL_INITFAILED:
542 		case DL_NOTINIT:
543 			errno = EIO;
544 			break;
545 
546 		case DL_ACCESS:
547 			errno = EACCES;
548 			break;
549 
550 		case DL_NOADDR:
551 			errno = EFAULT;
552 			break;
553 
554 		case DL_UNSUPPORTED:
555 		case DL_NOAUTO:
556 		case DL_NOXIDAUTO:
557 		case DL_NOTESTAUTO:
558 			errno = ENOTSUP;
559 			break;
560 
561 		case DL_SYSERR:
562 			errno = dleap->dl_unix_errno;
563 			break;
564 
565 		default:
566 			errno = EPROTO;
567 			break;
568 		}
569 		goto done;
570 
571 	default:
572 		errno = EBADMSG;
573 		goto done;
574 	}
575 
576 	rc = 0;	/* success */
577 done:
578 	free(buf);
579 	return (rc);
580 }
581 
582 int
583 dlpi_unbind(int fd, int timeout)
584 {
585 	int			rc = -1;
586 	size_t			size;
587 	dl_unbind_req_t		dlubr;
588 	dl_error_ack_t		*dleap;
589 	union DL_primitives	*buf;
590 	union DL_primitives	*udlp;
591 
592 	size = 0;
593 	size = MAX(sizeof (dl_ok_ack_t), size);
594 	size = MAX(sizeof (dl_error_ack_t), size);
595 
596 	if ((buf = malloc(size)) == NULL)
597 		return (-1);
598 
599 	dlubr.dl_primitive = DL_UNBIND_REQ;
600 
601 	if (strputmsg(fd, (uint8_t *)&dlubr, DL_UNBIND_REQ_SIZE, 0) == -1)
602 		goto done;
603 
604 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
605 		goto done;
606 
607 	if (size < sizeof (t_uscalar_t)) {
608 		errno = EBADMSG;
609 		goto done;
610 	}
611 
612 	udlp = (union DL_primitives *)buf;
613 	switch (udlp->dl_primitive) {
614 	case DL_OK_ACK:
615 		if (size < DL_OK_ACK_SIZE) {
616 			errno = EBADMSG;
617 			goto done;
618 		}
619 		break;
620 
621 	case DL_ERROR_ACK:
622 		if (size < DL_ERROR_ACK_SIZE) {
623 			errno = EBADMSG;
624 			goto done;
625 		}
626 
627 		dleap = (dl_error_ack_t *)buf;
628 		switch (dleap->dl_errno) {
629 		case DL_SYSERR:
630 			errno = dleap->dl_unix_errno;
631 			break;
632 
633 		default:
634 			errno = EPROTO;
635 			break;
636 		}
637 		goto done;
638 
639 	default:
640 		errno = EBADMSG;
641 		goto done;
642 	}
643 
644 	rc = 0;	/* success */
645 done:
646 	free(buf);
647 	return (rc);
648 }
649 
650 static int
651 i_dlpi_multi(int fd, int timeout, dlpi_multi_op_t op,
652     uint8_t *addrp, size_t addr_length)
653 {
654 	int			rc = -1;
655 	size_t			opsize;
656 	size_t			size;
657 	dl_enabmulti_req_t	*dlemrp;
658 	dl_disabmulti_req_t	*dldmrp;
659 	dl_error_ack_t		*dleap;
660 	union DL_primitives	*buf;
661 	union DL_primitives	*udlp;
662 
663 	opsize = (op == DLPI_MULTI_ENABLE) ? sizeof (dl_enabmulti_req_t) :
664 	    sizeof (dl_disabmulti_req_t);
665 	opsize += addr_length;
666 
667 	size = 0;
668 	size = MAX(opsize, size);
669 	size = MAX(sizeof (dl_ok_ack_t), size);
670 	size = MAX(sizeof (dl_error_ack_t), size);
671 
672 	if ((buf = malloc(size)) == NULL)
673 		return (-1);
674 
675 	if (op == DLPI_MULTI_ENABLE) {
676 		dlemrp = (dl_enabmulti_req_t *)buf;
677 		dlemrp->dl_primitive = DL_ENABMULTI_REQ;
678 		dlemrp->dl_addr_length = addr_length;
679 		dlemrp->dl_addr_offset = sizeof (dl_enabmulti_req_t);
680 		(void) memcpy(&dlemrp[1], addrp, addr_length);
681 	} else {
682 		dldmrp = (dl_disabmulti_req_t *)buf;
683 		dldmrp->dl_primitive = DL_DISABMULTI_REQ;
684 		dldmrp->dl_addr_length = addr_length;
685 		dldmrp->dl_addr_offset = sizeof (dl_disabmulti_req_t);
686 		(void) memcpy(&dldmrp[1], addrp, addr_length);
687 	}
688 
689 	if (strputmsg(fd, (uint8_t *)buf, opsize, 0) == -1)
690 		goto done;
691 
692 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
693 		goto done;
694 
695 	if (size < sizeof (t_uscalar_t)) {
696 		errno = EBADMSG;
697 		goto done;
698 	}
699 
700 	udlp = (union DL_primitives *)buf;
701 	switch (udlp->dl_primitive) {
702 	case DL_OK_ACK:
703 		if (size < DL_OK_ACK_SIZE) {
704 			errno = EBADMSG;
705 			goto done;
706 		}
707 		break;
708 
709 	case DL_ERROR_ACK:
710 		if (size < DL_ERROR_ACK_SIZE) {
711 			errno = EBADMSG;
712 			goto done;
713 		}
714 
715 		dleap = (dl_error_ack_t *)buf;
716 		switch (dleap->dl_errno) {
717 		case DL_BADADDR:
718 			errno = EINVAL;
719 			break;
720 
721 		case DL_TOOMANY:
722 			errno = ENOSPC;
723 			break;
724 
725 		case DL_NOTSUPPORTED:
726 			errno = ENOTSUP;
727 			break;
728 
729 		case DL_NOTENAB:
730 			errno = EINVAL;
731 			break;
732 
733 		case DL_SYSERR:
734 			errno = dleap->dl_unix_errno;
735 			break;
736 
737 		default:
738 			errno = EPROTO;
739 			break;
740 		}
741 		goto done;
742 
743 	default:
744 		errno = EBADMSG;
745 		goto done;
746 	}
747 
748 	rc = 0;	/* success */
749 done:
750 	free(buf);
751 	return (rc);
752 }
753 
754 int
755 dlpi_enabmulti(int fd, int timeout, uint8_t *addrp,
756     size_t addr_length)
757 {
758 	return (i_dlpi_multi(fd, timeout, DLPI_MULTI_ENABLE, addrp,
759 	    addr_length));
760 }
761 
762 int
763 dlpi_disabmulti(int fd, int timeout, uint8_t *addrp,
764     size_t addr_length)
765 {
766 	return (i_dlpi_multi(fd, timeout, DLPI_MULTI_DISABLE, addrp,
767 	    addr_length));
768 }
769 
770 static int
771 i_dlpi_promisc(int fd, int timeout, dlpi_promisc_op_t op,
772     uint_t level)
773 {
774 	int			rc = -1;
775 	size_t			opsize;
776 	size_t			size;
777 	dl_promiscon_req_t	*dlpnrp;
778 	dl_promiscoff_req_t	*dlpfrp;
779 	dl_error_ack_t		*dleap;
780 	union DL_primitives	*buf;
781 	union DL_primitives	*udlp;
782 
783 	opsize = (op == DLPI_PROMISC_ON) ? sizeof (dl_promiscon_req_t) :
784 	    sizeof (dl_promiscoff_req_t);
785 
786 	size = 0;
787 	size = MAX(opsize, size);
788 	size = MAX(sizeof (dl_ok_ack_t), size);
789 	size = MAX(sizeof (dl_error_ack_t), size);
790 
791 	if ((buf = malloc(size)) == NULL)
792 		return (-1);
793 
794 	if (op == DLPI_PROMISC_ON) {
795 		dlpnrp = (dl_promiscon_req_t *)buf;
796 		dlpnrp->dl_primitive = DL_PROMISCON_REQ;
797 		dlpnrp->dl_level = level;
798 
799 		if (strputmsg(fd, (uint8_t *)dlpnrp, opsize, 0) == -1)
800 			goto done;
801 	} else {
802 		dlpfrp = (dl_promiscoff_req_t *)buf;
803 		dlpfrp->dl_primitive = DL_PROMISCOFF_REQ;
804 		dlpfrp->dl_level = level;
805 
806 		if (strputmsg(fd, (uint8_t *)dlpfrp, opsize, 0) == -1)
807 			goto done;
808 	}
809 
810 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
811 		goto done;
812 
813 	if (size < sizeof (t_uscalar_t)) {
814 		errno = EBADMSG;
815 		goto done;
816 	}
817 
818 	udlp = (union DL_primitives *)buf;
819 	switch (udlp->dl_primitive) {
820 	case DL_OK_ACK:
821 		if (size < DL_OK_ACK_SIZE) {
822 			errno = EBADMSG;
823 			goto done;
824 		}
825 		break;
826 
827 	case DL_ERROR_ACK:
828 		if (size < DL_ERROR_ACK_SIZE) {
829 			errno = EBADMSG;
830 			goto done;
831 		}
832 
833 		dleap = (dl_error_ack_t *)buf;
834 		switch (dleap->dl_errno) {
835 		case DL_NOTSUPPORTED:
836 		case DL_UNSUPPORTED:
837 			errno = ENOTSUP;
838 			break;
839 
840 		case DL_NOTENAB:
841 			errno = EINVAL;
842 			break;
843 
844 		case DL_SYSERR:
845 			errno = dleap->dl_unix_errno;
846 			break;
847 
848 		default:
849 			errno = EPROTO;
850 			break;
851 		}
852 		goto done;
853 
854 	default:
855 		errno = EBADMSG;
856 		goto done;
857 	}
858 
859 	rc = 0;	/* success */
860 done:
861 	free(buf);
862 	return (rc);
863 }
864 
865 int
866 dlpi_promiscon(int fd, int timeout, uint_t level)
867 {
868 	return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_ON, level));
869 }
870 
871 int
872 dlpi_promiscoff(int fd, int timeout, uint_t level)
873 {
874 	return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_OFF, level));
875 }
876 
877 int
878 dlpi_phys_addr(int fd, int timeout, uint_t type, uint8_t *addrp,
879     size_t *addrlenp)
880 {
881 	int			rc = -1;
882 	size_t			size;
883 	dl_phys_addr_req_t	dlpar;
884 	dl_phys_addr_ack_t	*dlpaap;
885 	dl_error_ack_t		*dleap;
886 	union DL_primitives	*buf;
887 	union DL_primitives	*udlp;
888 
889 	size = 0;
890 	size = MAX(sizeof (dl_phys_addr_ack_t) + MAXADDRLEN, size);
891 	size = MAX(sizeof (dl_error_ack_t), size);
892 
893 	if ((buf = malloc(size)) == NULL)
894 		return (-1);
895 
896 	dlpar.dl_primitive = DL_PHYS_ADDR_REQ;
897 	dlpar.dl_addr_type = type;
898 
899 	if (strputmsg(fd, (uint8_t *)&dlpar, DL_PHYS_ADDR_REQ_SIZE, 0) == -1)
900 		goto done;
901 
902 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
903 		goto done;
904 
905 	if (size < sizeof (t_uscalar_t)) {
906 		errno = EBADMSG;
907 		goto done;
908 	}
909 
910 	udlp = (union DL_primitives *)buf;
911 	switch (udlp->dl_primitive) {
912 	case DL_PHYS_ADDR_ACK:
913 		if (size < DL_PHYS_ADDR_ACK_SIZE) {
914 			errno = EBADMSG;
915 			goto done;
916 		}
917 
918 		dlpaap = (dl_phys_addr_ack_t *)buf;
919 		if (dlpaap->dl_addr_offset != 0) {
920 			if (dlpaap->dl_addr_length == 0) {
921 				errno = EPROTO;
922 				goto done;
923 			}
924 
925 			if (addrlenp != NULL)
926 				*addrlenp = dlpaap->dl_addr_length;
927 
928 			if (addrp != NULL)
929 				(void) memcpy(addrp,
930 				    (char *)buf + dlpaap->dl_addr_offset,
931 				    dlpaap->dl_addr_length);
932 		}
933 		break;
934 
935 	case DL_ERROR_ACK:
936 		if (size < DL_ERROR_ACK_SIZE) {
937 			errno = EBADMSG;
938 			goto done;
939 		}
940 
941 		dleap = (dl_error_ack_t *)buf;
942 		switch (dleap->dl_errno) {
943 		case DL_SYSERR:
944 			errno = dleap->dl_unix_errno;
945 			break;
946 
947 		default:
948 			errno = EPROTO;
949 			break;
950 		}
951 		goto done;
952 
953 	default:
954 		errno = EBADMSG;
955 		goto done;
956 	}
957 
958 	rc = 0;	/* success */
959 done:
960 	free(buf);
961 	return (rc);
962 }
963 
964 int
965 dlpi_set_phys_addr(int fd, int timeout, uint8_t *addrp,
966     size_t addr_length)
967 {
968 	int			rc = -1;
969 	size_t			opsize;
970 	size_t			size;
971 	dl_set_phys_addr_req_t	*dlspap;
972 	dl_error_ack_t		*dleap;
973 	union DL_primitives	*buf;
974 	union DL_primitives	*udlp;
975 
976 	opsize = sizeof (dl_set_phys_addr_req_t) + addr_length;
977 
978 	size = 0;
979 	size = MAX(opsize, size);
980 	size = MAX(sizeof (dl_ok_ack_t), size);
981 	size = MAX(sizeof (dl_error_ack_t), size);
982 
983 	if ((buf = malloc(size)) == NULL)
984 		return (-1);
985 
986 	dlspap = (dl_set_phys_addr_req_t *)buf;
987 	dlspap->dl_primitive = DL_SET_PHYS_ADDR_REQ;
988 	dlspap->dl_addr_length = addr_length;
989 	dlspap->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
990 	(void) memcpy(&dlspap[1], addrp, addr_length);
991 
992 	if (strputmsg(fd, (uint8_t *)dlspap, opsize, 0) == -1)
993 		goto done;
994 
995 	if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1)
996 		goto done;
997 
998 	if (size < sizeof (t_uscalar_t)) {
999 		errno = EBADMSG;
1000 		goto done;
1001 	}
1002 
1003 	udlp = (union DL_primitives *)buf;
1004 	switch (udlp->dl_primitive) {
1005 	case DL_OK_ACK:
1006 		if (size < DL_OK_ACK_SIZE) {
1007 			errno = EBADMSG;
1008 			goto done;
1009 		}
1010 		break;
1011 
1012 	case DL_ERROR_ACK:
1013 		if (size < DL_ERROR_ACK_SIZE) {
1014 			errno = EBADMSG;
1015 			goto done;
1016 		}
1017 
1018 		dleap = (dl_error_ack_t *)buf;
1019 		switch (dleap->dl_errno) {
1020 		case DL_BADADDR:
1021 			errno = EINVAL;
1022 			break;
1023 
1024 		case DL_NOTSUPPORTED:
1025 			errno = ENOTSUP;
1026 			break;
1027 
1028 		case DL_SYSERR:
1029 			errno = dleap->dl_unix_errno;
1030 			break;
1031 
1032 		default:
1033 			errno = EPROTO;
1034 			break;
1035 		}
1036 		goto done;
1037 
1038 	default:
1039 		errno = EBADMSG;
1040 		goto done;
1041 	}
1042 
1043 	rc = 0;	/* success */
1044 done:
1045 	free(buf);
1046 	return (rc);
1047 }
1048 
1049 void
1050 dlpi_passive(int fd, int timeout)
1051 {
1052 	size_t			size;
1053 	dl_passive_req_t	dlpr;
1054 	union DL_primitives	*buf;
1055 
1056 	size = MAX(sizeof (dl_ok_ack_t), sizeof (dl_error_ack_t));
1057 
1058 	if ((buf = malloc(size)) == NULL)
1059 		return;
1060 
1061 	dlpr.dl_primitive = DL_PASSIVE_REQ;
1062 
1063 	/*
1064 	 * We don't care about the outcome of this operation.  We at least
1065 	 * don't want to return until the operation completes or the
1066 	 * timeout expires.
1067 	 */
1068 	if (strputmsg(fd, (uint8_t *)&dlpr, DL_PASSIVE_REQ_SIZE, 0) == 0)
1069 		(void) strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL);
1070 	free(buf);
1071 }
1072 
1073 static int
1074 i_dlpi_style1_open(dlpi_if_attr_t *diap)
1075 {
1076 	int		fd;
1077 	int		cnt;
1078 	dl_info_ack_t	dlia;
1079 
1080 	/* Open device */
1081 	if ((fd = dlpi_open(diap->devname)) == -1) {
1082 		diap->style1_failed = B_TRUE;
1083 		diap->mod_pushed = 0;
1084 		return (-1);
1085 	} else {
1086 		diap->style1_fd = fd;
1087 	}
1088 
1089 	/*
1090 	 * Try to push modules (if any) onto the device stream
1091 	 */
1092 	for (cnt = 0; cnt < diap->mod_cnt; cnt++) {
1093 		if (ioctl(fd, I_PUSH, diap->modlist[cnt]) == -1) {
1094 			diap->mod_pushed = cnt+1;
1095 			return (-1);
1096 		}
1097 	}
1098 
1099 	if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) == -1)
1100 		goto failed;
1101 
1102 	if (dlia.dl_provider_style != DL_STYLE1)
1103 		goto failed;
1104 
1105 	diap->style = DL_STYLE1;
1106 	diap->style1_failed = B_FALSE;
1107 
1108 	return (fd);
1109 failed:
1110 	diap->style1_failed = B_TRUE;
1111 	(void) dlpi_close(fd);
1112 	return (-1);
1113 }
1114 
1115 static int
1116 i_dlpi_style2_open(dlpi_if_attr_t *diap)
1117 {
1118 	int	fd;
1119 	uint_t	ppa;
1120 	dl_info_ack_t	dlia;
1121 
1122 	/*
1123 	 * If style 1 open failed, we need to determine how far it got and
1124 	 * finish up the open() call as a style 2 open
1125 	 *
1126 	 * If no modules were pushed (mod_pushed == 0), then we need to
1127 	 * strip off the ppa off the device name and open it as a style 2
1128 	 * device
1129 	 *
1130 	 * If the pushing of the last module failed, we need to strip off the
1131 	 * ppa from that module and try pushing it as a style 2 module
1132 	 *
1133 	 * Otherwise we failed during the push of an intermediate module and
1134 	 * must fail out and close the device.
1135 	 *
1136 	 * And if style1 did not fail (i.e. we called style2 open directly),
1137 	 * just open the device
1138 	 */
1139 	if (diap->style1_failed) {
1140 		if (!diap->mod_pushed) {
1141 			if (i_dlpi_ifrm_num(diap->devname, &ppa) < 0)
1142 				return (-1);
1143 			if ((fd = dlpi_open(diap->devname)) == -1)
1144 				return (-1);
1145 		} else if (diap->mod_pushed == diap->mod_cnt) {
1146 			if (i_dlpi_ifrm_num(
1147 				    diap->modlist[diap->mod_cnt - 1], &ppa) < 0)
1148 				return (-1);
1149 			diap->mod_pushed--;
1150 			fd = diap->style1_fd;
1151 		} else {
1152 			return (-1);
1153 		}
1154 	} else {
1155 		if ((fd = dlpi_open(diap->devname)) == -1)
1156 			return (-1);
1157 	}
1158 
1159 	/*
1160 	 * Try and push modules (if any) onto the device stream
1161 	 */
1162 	for (; diap->mod_pushed < diap->mod_cnt; diap->mod_pushed++) {
1163 		if (ioctl(fd, I_PUSH,
1164 		    diap->modlist[diap->mod_pushed]) == -1)
1165 			goto failed;
1166 	}
1167 
1168 	if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL,
1169 	    NULL) == -1)
1170 		goto failed;
1171 
1172 	if (dlia.dl_provider_style != DL_STYLE2)
1173 		goto failed;
1174 
1175 	diap->style = DL_STYLE2;
1176 
1177 	if (dlpi_attach(fd, -1, diap->ppa) < 0)
1178 		goto failed;
1179 
1180 	return (fd);
1181 failed:
1182 	(void) dlpi_close(fd);
1183 	return (-1);
1184 }
1185 
1186 static int
1187 i_dlpi_ifname_parse(const char *ifname, dlpi_if_attr_t *diap)
1188 {
1189 	char		*modlist = NULL; /* list of modules to push */
1190 	int		cnt = 0; /* number of modules to push */
1191 	char		modbuf[LIFNAMSIZ + 32];
1192 	char		*nxtmod;
1193 	char		*p;
1194 	int		len;
1195 
1196 	/* if lun is specified fail (backwards compat) */
1197 	if (strchr(ifname, ':') != NULL)
1198 		return (-1);
1199 
1200 	/* save copy of original device name */
1201 	if (strlcpy(diap->ifname, ifname, sizeof (diap->ifname)) >=
1202 	    sizeof (diap->ifname))
1203 		return (-1);
1204 
1205 	/* initialize ppa */
1206 	diap->ppa = -1;
1207 
1208 	/* get provider name and ppa from ifname */
1209 	len = strlen(ifname);
1210 	for (p = (char *)ifname + len; --p != ifname; len--) {
1211 		if (!isdigit(*p)) {
1212 			(void) strlcpy(diap->provider, ifname, len + 1);
1213 			diap->ppa = atoi(p + 1);
1214 			break;
1215 		}
1216 	}
1217 
1218 	if (strlcpy(modbuf, diap->ifname, sizeof (modbuf)) >=
1219 	    sizeof (modbuf))
1220 		return (-1);
1221 
1222 	/* parse '.' delimited module list */
1223 	modlist = strchr(modbuf, '.');
1224 	if (modlist != NULL) {
1225 		/* null-terminate interface name (device) */
1226 		*modlist = '\0';
1227 		modlist++;
1228 		while (modlist && cnt < MAX_MODS) {
1229 			if (*modlist == '\0')
1230 				return (-1);
1231 
1232 			nxtmod = strchr(modlist, '.');
1233 			if (nxtmod) {
1234 				*nxtmod = '\0';
1235 				nxtmod++;
1236 			}
1237 			if (strlcpy(diap->modlist[cnt], modlist,
1238 			    sizeof (diap->modlist[cnt])) >=
1239 			    sizeof (diap->modlist[cnt]))
1240 				return (-1);
1241 			cnt++;
1242 			modlist = nxtmod;
1243 		}
1244 	}
1245 	diap->mod_cnt = cnt;
1246 
1247 	if (strlcpy(diap->devname, modbuf, sizeof (diap->devname)) >=
1248 	    sizeof (diap->devname))
1249 		return (-1);
1250 
1251 	return (0);
1252 }
1253 
1254 int
1255 dlpi_if_open(const char *ifname, dlpi_if_attr_t *diap,
1256     boolean_t force_style2)
1257 {
1258 	int	fd;
1259 
1260 	if (i_dlpi_ifname_parse(ifname, diap) == -1) {
1261 		errno = EINVAL;
1262 		return (-1);
1263 	}
1264 
1265 	diap->style1_failed = B_TRUE;
1266 
1267 	if (!force_style2) {
1268 		if ((fd = i_dlpi_style1_open(diap)) != -1)
1269 			return (fd);
1270 	}
1271 
1272 	if ((fd = i_dlpi_style2_open(diap)) == -1)
1273 		return (-1);
1274 
1275 	return (fd);
1276 }
1277 
1278 int
1279 dlpi_if_parse(const char *ifname, char *provider, int *ppap)
1280 {
1281 	dlpi_if_attr_t	diap;
1282 
1283 	if (i_dlpi_ifname_parse(ifname, &diap) == -1) {
1284 		errno = EINVAL;
1285 		return (-1);
1286 	}
1287 
1288 	if (strlcpy(provider, diap.provider, LIFNAMSIZ) > LIFNAMSIZ)
1289 		return (-1);
1290 
1291 	if (ppap != NULL)
1292 		*ppap = diap.ppa;
1293 
1294 	return (0);
1295 }
1296 
1297 /*
1298  * attempt to remove ppa from end of file name
1299  * return -1 if none found
1300  * return ppa if found and remove the ppa from the filename
1301  */
1302 static int
1303 i_dlpi_ifrm_num(char *fname, unsigned int *ppa)
1304 {
1305 	int	i;
1306 	uint_t	p = 0;
1307 	unsigned int	m = 1;
1308 
1309 	i = strlen(fname) - 1;
1310 
1311 	while (i >= 0 && isdigit(fname[i])) {
1312 		p += (fname[i] - '0')*m;
1313 		m *= 10;
1314 		i--;
1315 	}
1316 
1317 	if (m == 1) {
1318 		return (-1);
1319 	}
1320 
1321 	fname[i + 1] = '\0';
1322 	*ppa = p;
1323 	return (0);
1324 }
1325