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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Common Sun DLPI routines. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/sysmacros.h> 32 #include <sys/byteorder.h> 33 #include <sys/systm.h> 34 #include <sys/stream.h> 35 #include <sys/strsun.h> 36 #include <sys/dlpi.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunldi.h> 40 #include <sys/cmn_err.h> 41 42 #define DLADDRL (80) 43 44 void 45 dlbindack( 46 queue_t *wq, 47 mblk_t *mp, 48 t_scalar_t sap, 49 void *addrp, 50 t_uscalar_t addrlen, 51 t_uscalar_t maxconind, 52 t_uscalar_t xidtest) 53 { 54 union DL_primitives *dlp; 55 size_t size; 56 57 size = sizeof (dl_bind_ack_t) + addrlen; 58 if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_BIND_ACK)) == NULL) 59 return; 60 61 dlp = (union DL_primitives *)mp->b_rptr; 62 dlp->bind_ack.dl_sap = sap; 63 dlp->bind_ack.dl_addr_length = addrlen; 64 dlp->bind_ack.dl_addr_offset = sizeof (dl_bind_ack_t); 65 dlp->bind_ack.dl_max_conind = maxconind; 66 dlp->bind_ack.dl_xidtest_flg = xidtest; 67 if (addrlen != 0) 68 bcopy(addrp, mp->b_rptr + sizeof (dl_bind_ack_t), addrlen); 69 70 qreply(wq, mp); 71 } 72 73 void 74 dlokack( 75 queue_t *wq, 76 mblk_t *mp, 77 t_uscalar_t correct_primitive) 78 { 79 union DL_primitives *dlp; 80 81 if ((mp = mexchange(wq, mp, sizeof (dl_ok_ack_t), M_PCPROTO, 82 DL_OK_ACK)) == NULL) 83 return; 84 dlp = (union DL_primitives *)mp->b_rptr; 85 dlp->ok_ack.dl_correct_primitive = correct_primitive; 86 qreply(wq, mp); 87 } 88 89 void 90 dlerrorack( 91 queue_t *wq, 92 mblk_t *mp, 93 t_uscalar_t error_primitive, 94 t_uscalar_t error, 95 t_uscalar_t unix_errno) 96 { 97 union DL_primitives *dlp; 98 99 if ((mp = mexchange(wq, mp, sizeof (dl_error_ack_t), M_PCPROTO, 100 DL_ERROR_ACK)) == NULL) 101 return; 102 dlp = (union DL_primitives *)mp->b_rptr; 103 dlp->error_ack.dl_error_primitive = error_primitive; 104 dlp->error_ack.dl_errno = error; 105 dlp->error_ack.dl_unix_errno = unix_errno; 106 qreply(wq, mp); 107 } 108 109 void 110 dluderrorind( 111 queue_t *wq, 112 mblk_t *mp, 113 void *addrp, 114 t_uscalar_t addrlen, 115 t_uscalar_t error, 116 t_uscalar_t unix_errno) 117 { 118 union DL_primitives *dlp; 119 char buf[DLADDRL]; 120 size_t size; 121 122 if (addrlen > DLADDRL) 123 addrlen = DLADDRL; 124 125 bcopy(addrp, buf, addrlen); 126 127 size = sizeof (dl_uderror_ind_t) + addrlen; 128 129 if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_UDERROR_IND)) == NULL) 130 return; 131 132 dlp = (union DL_primitives *)mp->b_rptr; 133 dlp->uderror_ind.dl_dest_addr_length = addrlen; 134 dlp->uderror_ind.dl_dest_addr_offset = sizeof (dl_uderror_ind_t); 135 dlp->uderror_ind.dl_unix_errno = unix_errno; 136 dlp->uderror_ind.dl_errno = error; 137 bcopy((caddr_t)buf, 138 (caddr_t)(mp->b_rptr + sizeof (dl_uderror_ind_t)), addrlen); 139 qreply(wq, mp); 140 } 141 142 void 143 dlphysaddrack( 144 queue_t *wq, 145 mblk_t *mp, 146 void *addrp, 147 t_uscalar_t len) 148 { 149 union DL_primitives *dlp; 150 size_t size; 151 152 size = sizeof (dl_phys_addr_ack_t) + len; 153 if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_PHYS_ADDR_ACK)) == NULL) 154 return; 155 dlp = (union DL_primitives *)mp->b_rptr; 156 dlp->physaddr_ack.dl_addr_length = len; 157 dlp->physaddr_ack.dl_addr_offset = sizeof (dl_phys_addr_ack_t); 158 if (len != 0) 159 bcopy(addrp, mp->b_rptr + sizeof (dl_phys_addr_ack_t), len); 160 qreply(wq, mp); 161 } 162 163 void 164 dlcapabsetqid(dl_mid_t *idp, const queue_t *q) 165 { 166 #ifndef _LP64 167 idp->mid[0] = (t_uscalar_t)q; 168 #else 169 idp->mid[0] = (t_uscalar_t)BMASK_32((uint64_t)q); 170 idp->mid[1] = (t_uscalar_t)BMASK_32(((uint64_t)q) >> 32); 171 #endif 172 } 173 174 boolean_t 175 dlcapabcheckqid(const dl_mid_t *idp, const queue_t *q) 176 { 177 #ifndef _LP64 178 return ((queue_t *)(idp->mid[0]) == q); 179 #else 180 return ((queue_t *) 181 ((uint64_t)idp->mid[0] | ((uint64_t)idp->mid[1] << 32)) == q); 182 #endif 183 } 184 185 void 186 dlnotifyack( 187 queue_t *wq, 188 mblk_t *mp, 189 uint32_t notifications) 190 { 191 union DL_primitives *dlp; 192 193 if ((mp = mexchange(wq, mp, sizeof (dl_notify_ack_t), M_PROTO, 194 DL_NOTIFY_ACK)) == NULL) 195 return; 196 dlp = (union DL_primitives *)mp->b_rptr; 197 dlp->notify_ack.dl_notifications = notifications; 198 qreply(wq, mp); 199 } 200 201 static int 202 dl_op(ldi_handle_t lh, mblk_t **mpp, t_uscalar_t expprim, size_t minlen, 203 dl_error_ack_t *dleap, timestruc_t *tvp) 204 { 205 int err; 206 size_t len; 207 mblk_t *mp = *mpp; 208 t_uscalar_t reqprim, ackprim, ackreqprim; 209 union DL_primitives *dlp; 210 211 reqprim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 212 213 (void) ldi_putmsg(lh, mp); 214 215 switch (err = ldi_getmsg(lh, &mp, tvp)) { 216 case 0: 217 break; 218 case ETIME: 219 cmn_err(CE_NOTE, "!dl_op: timed out waiting for %s to %s", 220 dl_primstr(reqprim), dl_primstr(expprim)); 221 return (ETIME); 222 default: 223 cmn_err(CE_NOTE, "!dl_op: ldi_getmsg() for %s failed: %d", 224 dl_primstr(expprim), err); 225 return (err); 226 } 227 228 len = MBLKL(mp); 229 if (len < sizeof (t_uscalar_t)) { 230 cmn_err(CE_NOTE, "!dl_op: received runt DLPI message"); 231 freemsg(mp); 232 return (EBADMSG); 233 } 234 235 dlp = (union DL_primitives *)mp->b_rptr; 236 ackprim = dlp->dl_primitive; 237 238 if (ackprim == expprim) { 239 if (len < minlen) 240 goto runt; 241 242 if (ackprim == DL_OK_ACK) { 243 if (dlp->ok_ack.dl_correct_primitive != reqprim) { 244 ackreqprim = dlp->ok_ack.dl_correct_primitive; 245 goto mixup; 246 } 247 } 248 *mpp = mp; 249 return (0); 250 } 251 252 if (ackprim == DL_ERROR_ACK) { 253 if (len < DL_ERROR_ACK_SIZE) 254 goto runt; 255 256 if (dlp->error_ack.dl_error_primitive != reqprim) { 257 ackreqprim = dlp->error_ack.dl_error_primitive; 258 goto mixup; 259 } 260 261 /* 262 * Return a special error code (ENOTSUP) indicating that the 263 * caller has returned DL_ERROR_ACK. Callers that want more 264 * details an pass a non-NULL dleap. 265 */ 266 if (dleap != NULL) 267 *dleap = dlp->error_ack; 268 269 freemsg(mp); 270 return (ENOTSUP); 271 } 272 273 cmn_err(CE_NOTE, "!dl_op: expected %s but received %s", 274 dl_primstr(expprim), dl_primstr(ackprim)); 275 freemsg(mp); 276 return (EBADMSG); 277 runt: 278 cmn_err(CE_NOTE, "!dl_op: received runt %s", dl_primstr(ackprim)); 279 freemsg(mp); 280 return (EBADMSG); 281 mixup: 282 cmn_err(CE_NOTE, "!dl_op: received %s for %s instead of %s", 283 dl_primstr(ackprim), dl_primstr(ackreqprim), dl_primstr(reqprim)); 284 freemsg(mp); 285 return (EBADMSG); 286 } 287 288 /* 289 * Send a DL_ATTACH_REQ for `ppa' over `lh' and wait for the response. 290 * 291 * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the 292 * caller can get the contents by passing a non-NULL `dleap'). 293 */ 294 int 295 dl_attach(ldi_handle_t lh, int ppa, dl_error_ack_t *dleap) 296 { 297 mblk_t *mp; 298 int err; 299 300 mp = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ); 301 if (mp == NULL) 302 return (ENOMEM); 303 304 ((dl_attach_req_t *)mp->b_rptr)->dl_ppa = ppa; 305 306 err = dl_op(lh, &mp, DL_OK_ACK, DL_OK_ACK_SIZE, dleap, NULL); 307 if (err == 0) 308 freemsg(mp); 309 return (err); 310 } 311 312 /* 313 * Send a DL_BIND_REQ for `sap' over `lh' and wait for the response. 314 * 315 * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the 316 * caller can get the contents by passing a non-NULL `dleap'). 317 */ 318 int 319 dl_bind(ldi_handle_t lh, uint_t sap, dl_error_ack_t *dleap) 320 { 321 dl_bind_req_t *dlbrp; 322 dl_bind_ack_t *dlbap; 323 mblk_t *mp; 324 int err; 325 326 mp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); 327 if (mp == NULL) 328 return (ENOMEM); 329 330 dlbrp = (dl_bind_req_t *)mp->b_rptr; 331 dlbrp->dl_sap = sap; 332 dlbrp->dl_conn_mgmt = 0; 333 dlbrp->dl_max_conind = 0; 334 dlbrp->dl_xidtest_flg = 0; 335 dlbrp->dl_service_mode = DL_CLDLS; 336 337 err = dl_op(lh, &mp, DL_BIND_ACK, DL_BIND_ACK_SIZE, dleap, NULL); 338 if (err == 0) { 339 dlbap = (dl_bind_ack_t *)mp->b_rptr; 340 if (dlbap->dl_sap != sap) { 341 cmn_err(CE_NOTE, "!dl_bind: DL_BIND_ACK: bad sap %u", 342 dlbap->dl_sap); 343 err = EPROTO; 344 } 345 freemsg(mp); 346 } 347 return (err); 348 } 349 350 /* 351 * Send a DL_PHYS_ADDR_REQ over `lh' and wait for the response. The caller 352 * must set `*physlenp' to the size of `physaddr' (both of which must be 353 * non-NULL); upon success they will be updated to contain the actual physical 354 * address and length. 355 * 356 * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the 357 * caller can get the contents by passing a non-NULL `dleap'). 358 */ 359 int 360 dl_phys_addr(ldi_handle_t lh, uchar_t *physaddr, size_t *physlenp, 361 dl_error_ack_t *dleap) 362 { 363 dl_phys_addr_ack_t *dlpap; 364 mblk_t *mp; 365 int err; 366 t_uscalar_t paddrlen, paddroff; 367 timestruc_t tv; 368 369 mp = mexchange(NULL, NULL, DL_PHYS_ADDR_REQ_SIZE, M_PROTO, 370 DL_PHYS_ADDR_REQ); 371 if (mp == NULL) 372 return (ENOMEM); 373 374 ((dl_phys_addr_req_t *)mp->b_rptr)->dl_addr_type = DL_CURR_PHYS_ADDR; 375 376 /* 377 * In case some provider doesn't implement or NAK the 378 * request, just wait for 15 seconds. 379 */ 380 tv.tv_sec = 15; 381 tv.tv_nsec = 0; 382 383 err = dl_op(lh, &mp, DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE, dleap, 384 &tv); 385 if (err == 0) { 386 dlpap = (dl_phys_addr_ack_t *)mp->b_rptr; 387 paddrlen = dlpap->dl_addr_length; 388 paddroff = dlpap->dl_addr_offset; 389 if (paddroff == 0 || paddrlen == 0 || paddrlen > *physlenp || 390 !MBLKIN(mp, paddroff, paddrlen)) { 391 cmn_err(CE_NOTE, "!dl_phys_addr: DL_PHYS_ADDR_ACK: " 392 "bad length/offset %d/%d", paddrlen, paddroff); 393 err = EBADMSG; 394 } else { 395 bcopy(mp->b_rptr + paddroff, physaddr, paddrlen); 396 *physlenp = paddrlen; 397 } 398 freemsg(mp); 399 } 400 return (err); 401 } 402 403 /* 404 * Send a DL_INFO_REQ over `lh' and wait for the response. The caller must 405 * pass a non-NULL `dliap', which upon success will contain the dl_info_ack_t 406 * from the provider. The caller may optionally get the provider's physical 407 * address by passing a non-NULL `physaddr' and setting `*physlenp' to its 408 * size; upon success they will be updated to contain the actual physical 409 * address and its length. 410 * 411 * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the 412 * caller can get the contents by passing a non-NULL `dleap'). 413 */ 414 int 415 dl_info(ldi_handle_t lh, dl_info_ack_t *dliap, uchar_t *physaddr, 416 size_t *physlenp, dl_error_ack_t *dleap) 417 { 418 mblk_t *mp; 419 int err; 420 int addrlen, addroff; 421 422 mp = mexchange(NULL, NULL, DL_INFO_REQ_SIZE, M_PCPROTO, DL_INFO_REQ); 423 if (mp == NULL) 424 return (ENOMEM); 425 426 err = dl_op(lh, &mp, DL_INFO_ACK, DL_INFO_ACK_SIZE, dleap, NULL); 427 if (err != 0) 428 return (err); 429 430 *dliap = *(dl_info_ack_t *)mp->b_rptr; 431 if (physaddr != NULL) { 432 addrlen = dliap->dl_addr_length - ABS(dliap->dl_sap_length); 433 addroff = dliap->dl_addr_offset; 434 if (addroff == 0 || addrlen <= 0 || addrlen > *physlenp || 435 !MBLKIN(mp, addroff, dliap->dl_addr_length)) { 436 cmn_err(CE_NOTE, "!dl_info: DL_INFO_ACK: " 437 "bad length/offset %d/%d", addrlen, addroff); 438 freemsg(mp); 439 return (EBADMSG); 440 } 441 442 if (dliap->dl_sap_length > 0) 443 addroff += dliap->dl_sap_length; 444 bcopy(mp->b_rptr + addroff, physaddr, addrlen); 445 *physlenp = addrlen; 446 } 447 freemsg(mp); 448 return (err); 449 } 450 451 /* 452 * Send a DL_NOTIFY_REQ over `lh' and wait for the response. The caller 453 * should set `notesp' to the set of notifications they wish to enable; 454 * upon success it will contain the notifications enabled by the provider. 455 * 456 * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the 457 * caller can get the contents by passing a non-NULL `dleap'). 458 */ 459 int 460 dl_notify(ldi_handle_t lh, uint32_t *notesp, dl_error_ack_t *dleap) 461 { 462 mblk_t *mp; 463 int err; 464 465 mp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ); 466 if (mp == NULL) 467 return (ENOMEM); 468 469 ((dl_notify_req_t *)mp->b_rptr)->dl_notifications = *notesp; 470 471 err = dl_op(lh, &mp, DL_NOTIFY_ACK, DL_NOTIFY_ACK_SIZE, dleap, NULL); 472 if (err == 0) { 473 *notesp = ((dl_notify_ack_t *)mp->b_rptr)->dl_notifications; 474 freemsg(mp); 475 } 476 return (err); 477 } 478 479 const char * 480 dl_primstr(t_uscalar_t prim) 481 { 482 switch (prim) { 483 case DL_INFO_REQ: return ("DL_INFO_REQ"); 484 case DL_INFO_ACK: return ("DL_INFO_ACK"); 485 case DL_ATTACH_REQ: return ("DL_ATTACH_REQ"); 486 case DL_DETACH_REQ: return ("DL_DETACH_REQ"); 487 case DL_BIND_REQ: return ("DL_BIND_REQ"); 488 case DL_BIND_ACK: return ("DL_BIND_ACK"); 489 case DL_UNBIND_REQ: return ("DL_UNBIND_REQ"); 490 case DL_OK_ACK: return ("DL_OK_ACK"); 491 case DL_ERROR_ACK: return ("DL_ERROR_ACK"); 492 case DL_ENABMULTI_REQ: return ("DL_ENABMULTI_REQ"); 493 case DL_DISABMULTI_REQ: return ("DL_DISABMULTI_REQ"); 494 case DL_PROMISCON_REQ: return ("DL_PROMISCON_REQ"); 495 case DL_PROMISCOFF_REQ: return ("DL_PROMISCOFF_REQ"); 496 case DL_UNITDATA_REQ: return ("DL_UNITDATA_REQ"); 497 case DL_UNITDATA_IND: return ("DL_UNITDATA_IND"); 498 case DL_UDERROR_IND: return ("DL_UDERROR_IND"); 499 case DL_PHYS_ADDR_REQ: return ("DL_PHYS_ADDR_REQ"); 500 case DL_PHYS_ADDR_ACK: return ("DL_PHYS_ADDR_ACK"); 501 case DL_SET_PHYS_ADDR_REQ: return ("DL_SET_PHYS_ADDR_REQ"); 502 case DL_NOTIFY_REQ: return ("DL_NOTIFY_REQ"); 503 case DL_NOTIFY_ACK: return ("DL_NOTIFY_ACK"); 504 case DL_NOTIFY_IND: return ("DL_NOTIFY_IND"); 505 case DL_CAPABILITY_REQ: return ("DL_CAPABILITY_REQ"); 506 case DL_CAPABILITY_ACK: return ("DL_CAPABILITY_ACK"); 507 case DL_CONTROL_REQ: return ("DL_CONTROL_REQ"); 508 case DL_CONTROL_ACK: return ("DL_CONTROL_ACK"); 509 case DL_PASSIVE_REQ: return ("DL_PASSIVE_REQ"); 510 case DL_INTR_MODE_REQ: return ("DL_INTR_MODE_REQ"); 511 case DL_UDQOS_REQ: return ("DL_UDQOS_REQ"); 512 default: return ("<unknown primitive>"); 513 } 514 } 515 516 const char * 517 dl_errstr(t_uscalar_t err) 518 { 519 switch (err) { 520 case DL_ACCESS: return ("DL_ACCESS"); 521 case DL_BADADDR: return ("DL_BADADDR"); 522 case DL_BADCORR: return ("DL_BADCORR"); 523 case DL_BADDATA: return ("DL_BADDATA"); 524 case DL_BADPPA: return ("DL_BADPPA"); 525 case DL_BADPRIM: return ("DL_BADPRIM"); 526 case DL_BADQOSPARAM: return ("DL_BADQOSPARAM"); 527 case DL_BADQOSTYPE: return ("DL_BADQOSTYPE"); 528 case DL_BADSAP: return ("DL_BADSAP"); 529 case DL_BADTOKEN: return ("DL_BADTOKEN"); 530 case DL_BOUND: return ("DL_BOUND"); 531 case DL_INITFAILED: return ("DL_INITFAILED"); 532 case DL_NOADDR: return ("DL_NOADDR"); 533 case DL_NOTINIT: return ("DL_NOTINIT"); 534 case DL_OUTSTATE: return ("DL_OUTSTATE"); 535 case DL_SYSERR: return ("DL_SYSERR"); 536 case DL_UNSUPPORTED: return ("DL_UNSUPPORTED"); 537 case DL_UNDELIVERABLE: return ("DL_UNDELIVERABLE"); 538 case DL_NOTSUPPORTED: return ("DL_NOTSUPPORTED "); 539 case DL_TOOMANY: return ("DL_TOOMANY"); 540 case DL_NOTENAB: return ("DL_NOTENAB"); 541 case DL_BUSY: return ("DL_BUSY"); 542 case DL_NOAUTO: return ("DL_NOAUTO"); 543 case DL_NOXIDAUTO: return ("DL_NOXIDAUTO"); 544 case DL_NOTESTAUTO: return ("DL_NOTESTAUTO"); 545 case DL_XIDAUTO: return ("DL_XIDAUTO"); 546 case DL_TESTAUTO: return ("DL_TESTAUTO"); 547 case DL_PENDING: return ("DL_PENDING"); 548 default: return ("<unknown error>"); 549 } 550 } 551 552 const char * 553 dl_mactypestr(t_uscalar_t mactype) 554 { 555 switch (mactype) { 556 case DL_CSMACD: return ("CSMA/CD"); 557 case DL_TPB: return ("Token Bus"); 558 case DL_TPR: return ("Token Ring"); 559 case DL_METRO: return ("Metro Net"); 560 case DL_ETHER: return ("Ethernet"); 561 case DL_HDLC: return ("HDLC"); 562 case DL_CHAR: return ("Sync Character"); 563 case DL_CTCA: return ("CTCA"); 564 case DL_FDDI: return ("FDDI"); 565 case DL_FRAME: return ("Frame Relay (LAPF)"); 566 case DL_MPFRAME: return ("MP Frame Relay"); 567 case DL_ASYNC: return ("Async Character"); 568 case DL_IPX25: return ("X.25 (Classic IP)"); 569 case DL_LOOP: return ("Software Loopback"); 570 case DL_FC: return ("Fiber Channel"); 571 case DL_ATM: return ("ATM"); 572 case DL_IPATM: return ("ATM (Classic IP)"); 573 case DL_X25: return ("X.25 (LAPB)"); 574 case DL_ISDN: return ("ISDN"); 575 case DL_HIPPI: return ("HIPPI"); 576 case DL_100VG: return ("100BaseVG Ethernet"); 577 case DL_100VGTPR: return ("100BaseVG Token Ring"); 578 case DL_ETH_CSMA: return ("Ethernet/IEEE 802.3"); 579 case DL_100BT: return ("100BaseT"); 580 case DL_IB: return ("Infiniband"); 581 case DL_IPV4: return ("IPv4 Tunnel"); 582 case DL_IPV6: return ("IPv6 Tunnel"); 583 case DL_WIFI: return ("IEEE 802.11"); 584 case DL_IPNET: return ("IPNET"); 585 default: return ("<unknown mactype>"); 586 } 587 } 588