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