| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * Common Sun DLPI routines. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/sysmacros.h> |
| #include <sys/byteorder.h> |
| #include <sys/stream.h> |
| #include <sys/strsun.h> |
| #include <sys/dlpi.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/sunldi.h> |
| #include <sys/cmn_err.h> |
| |
| void |
| dlbindack( |
| queue_t *wq, |
| mblk_t *mp, |
| t_scalar_t sap, |
| const void *addrp, |
| t_uscalar_t addrlen, |
| t_uscalar_t maxconind, |
| t_uscalar_t xidtest) |
| { |
| union DL_primitives *dlp; |
| size_t size; |
| |
| size = sizeof (dl_bind_ack_t) + addrlen; |
| if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_BIND_ACK)) == NULL) |
| return; |
| |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->bind_ack.dl_sap = sap; |
| dlp->bind_ack.dl_addr_length = addrlen; |
| dlp->bind_ack.dl_addr_offset = sizeof (dl_bind_ack_t); |
| dlp->bind_ack.dl_max_conind = maxconind; |
| dlp->bind_ack.dl_xidtest_flg = xidtest; |
| if (addrlen != 0) |
| bcopy(addrp, mp->b_rptr + sizeof (dl_bind_ack_t), addrlen); |
| qreply(wq, mp); |
| } |
| |
| void |
| dlokack( |
| queue_t *wq, |
| mblk_t *mp, |
| t_uscalar_t correct_primitive) |
| { |
| union DL_primitives *dlp; |
| |
| if ((mp = mexchange(wq, mp, sizeof (dl_ok_ack_t), M_PCPROTO, |
| DL_OK_ACK)) == NULL) |
| return; |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->ok_ack.dl_correct_primitive = correct_primitive; |
| qreply(wq, mp); |
| } |
| |
| void |
| dlerrorack( |
| queue_t *wq, |
| mblk_t *mp, |
| t_uscalar_t error_primitive, |
| t_uscalar_t error, |
| t_uscalar_t unix_errno) |
| { |
| union DL_primitives *dlp; |
| |
| if ((mp = mexchange(wq, mp, sizeof (dl_error_ack_t), M_PCPROTO, |
| DL_ERROR_ACK)) == NULL) |
| return; |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->error_ack.dl_error_primitive = error_primitive; |
| dlp->error_ack.dl_errno = error; |
| dlp->error_ack.dl_unix_errno = unix_errno; |
| qreply(wq, mp); |
| } |
| |
| void |
| dluderrorind( |
| queue_t *wq, |
| mblk_t *mp, |
| const void *addrp, |
| t_uscalar_t addrlen, |
| t_uscalar_t error, |
| t_uscalar_t unix_errno) |
| { |
| union DL_primitives *dlp; |
| size_t size; |
| |
| size = sizeof (dl_uderror_ind_t) + addrlen; |
| if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_UDERROR_IND)) == NULL) |
| return; |
| |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->uderror_ind.dl_dest_addr_length = addrlen; |
| dlp->uderror_ind.dl_dest_addr_offset = sizeof (dl_uderror_ind_t); |
| dlp->uderror_ind.dl_unix_errno = unix_errno; |
| dlp->uderror_ind.dl_errno = error; |
| if (addrlen != 0) |
| bcopy(addrp, mp->b_rptr + sizeof (dl_uderror_ind_t), addrlen); |
| qreply(wq, mp); |
| } |
| |
| void |
| dlphysaddrack( |
| queue_t *wq, |
| mblk_t *mp, |
| const void *addrp, |
| t_uscalar_t len) |
| { |
| union DL_primitives *dlp; |
| size_t size; |
| |
| size = sizeof (dl_phys_addr_ack_t) + len; |
| if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_PHYS_ADDR_ACK)) == NULL) |
| return; |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->physaddr_ack.dl_addr_length = len; |
| dlp->physaddr_ack.dl_addr_offset = sizeof (dl_phys_addr_ack_t); |
| if (len != 0) |
| bcopy(addrp, mp->b_rptr + sizeof (dl_phys_addr_ack_t), len); |
| qreply(wq, mp); |
| } |
| |
| void |
| dlcapabsetqid(dl_mid_t *idp, const queue_t *q) |
| { |
| #ifndef _LP64 |
| idp->mid[0] = (t_uscalar_t)q; |
| #else |
| idp->mid[0] = (t_uscalar_t)BMASK_32((uint64_t)q); |
| idp->mid[1] = (t_uscalar_t)BMASK_32(((uint64_t)q) >> 32); |
| #endif |
| } |
| |
| boolean_t |
| dlcapabcheckqid(const dl_mid_t *idp, const queue_t *q) |
| { |
| #ifndef _LP64 |
| return ((queue_t *)(idp->mid[0]) == q); |
| #else |
| return ((queue_t *) |
| ((uint64_t)idp->mid[0] | ((uint64_t)idp->mid[1] << 32)) == q); |
| #endif |
| } |
| |
| void |
| dlnotifyack( |
| queue_t *wq, |
| mblk_t *mp, |
| uint32_t notifications) |
| { |
| union DL_primitives *dlp; |
| |
| if ((mp = mexchange(wq, mp, sizeof (dl_notify_ack_t), M_PROTO, |
| DL_NOTIFY_ACK)) == NULL) |
| return; |
| dlp = (union DL_primitives *)mp->b_rptr; |
| dlp->notify_ack.dl_notifications = notifications; |
| qreply(wq, mp); |
| } |
| |
| static int |
| dl_op(ldi_handle_t lh, mblk_t **mpp, t_uscalar_t expprim, size_t minlen, |
| dl_error_ack_t *dleap, timestruc_t *tvp) |
| { |
| int err; |
| size_t len; |
| mblk_t *mp = *mpp; |
| t_uscalar_t reqprim, ackprim, ackreqprim; |
| union DL_primitives *dlp; |
| |
| reqprim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; |
| |
| (void) ldi_putmsg(lh, mp); |
| |
| switch (err = ldi_getmsg(lh, &mp, tvp)) { |
| case 0: |
| break; |
| case ETIME: |
| cmn_err(CE_NOTE, "!dl_op: timed out waiting for %s to %s", |
| dl_primstr(reqprim), dl_primstr(expprim)); |
| return (ETIME); |
| default: |
| cmn_err(CE_NOTE, "!dl_op: ldi_getmsg() for %s failed: %d", |
| dl_primstr(expprim), err); |
| return (err); |
| } |
| |
| len = MBLKL(mp); |
| if (len < sizeof (t_uscalar_t)) { |
| cmn_err(CE_NOTE, "!dl_op: received runt DLPI message"); |
| freemsg(mp); |
| return (EBADMSG); |
| } |
| |
| dlp = (union DL_primitives *)mp->b_rptr; |
| ackprim = dlp->dl_primitive; |
| |
| if (ackprim == expprim) { |
| if (len < minlen) |
| goto runt; |
| |
| if (ackprim == DL_OK_ACK) { |
| if (dlp->ok_ack.dl_correct_primitive != reqprim) { |
| ackreqprim = dlp->ok_ack.dl_correct_primitive; |
| goto mixup; |
| } |
| } |
| *mpp = mp; |
| return (0); |
| } |
| |
| if (ackprim == DL_ERROR_ACK) { |
| if (len < DL_ERROR_ACK_SIZE) |
| goto runt; |
| |
| if (dlp->error_ack.dl_error_primitive != reqprim) { |
| ackreqprim = dlp->error_ack.dl_error_primitive; |
| goto mixup; |
| } |
| |
| /* |
| * Return a special error code (ENOTSUP) indicating that the |
| * caller has returned DL_ERROR_ACK. Callers that want more |
| * details an pass a non-NULL dleap. |
| */ |
| if (dleap != NULL) |
| *dleap = dlp->error_ack; |
| |
| freemsg(mp); |
| return (ENOTSUP); |
| } |
| |
| cmn_err(CE_NOTE, "!dl_op: expected %s but received %s", |
| dl_primstr(expprim), dl_primstr(ackprim)); |
| freemsg(mp); |
| return (EBADMSG); |
| runt: |
| cmn_err(CE_NOTE, "!dl_op: received runt %s", dl_primstr(ackprim)); |
| freemsg(mp); |
| return (EBADMSG); |
| mixup: |
| cmn_err(CE_NOTE, "!dl_op: received %s for %s instead of %s", |
| dl_primstr(ackprim), dl_primstr(ackreqprim), dl_primstr(reqprim)); |
| freemsg(mp); |
| return (EBADMSG); |
| } |
| |
| /* |
| * Send a DL_ATTACH_REQ for `ppa' over `lh' and wait for the response. |
| * |
| * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the |
| * caller can get the contents by passing a non-NULL `dleap'). |
| */ |
| int |
| dl_attach(ldi_handle_t lh, int ppa, dl_error_ack_t *dleap) |
| { |
| mblk_t *mp; |
| int err; |
| |
| mp = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ); |
| if (mp == NULL) |
| return (ENOMEM); |
| |
| ((dl_attach_req_t *)mp->b_rptr)->dl_ppa = ppa; |
| |
| err = dl_op(lh, &mp, DL_OK_ACK, DL_OK_ACK_SIZE, dleap, NULL); |
| if (err == 0) |
| freemsg(mp); |
| return (err); |
| } |
| |
| /* |
| * Send a DL_BIND_REQ for `sap' over `lh' and wait for the response. |
| * |
| * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the |
| * caller can get the contents by passing a non-NULL `dleap'). |
| */ |
| int |
| dl_bind(ldi_handle_t lh, uint_t sap, dl_error_ack_t *dleap) |
| { |
| dl_bind_req_t *dlbrp; |
| dl_bind_ack_t *dlbap; |
| mblk_t *mp; |
| int err; |
| |
| mp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); |
| if (mp == NULL) |
| return (ENOMEM); |
| |
| dlbrp = (dl_bind_req_t *)mp->b_rptr; |
| dlbrp->dl_sap = sap; |
| dlbrp->dl_conn_mgmt = 0; |
| dlbrp->dl_max_conind = 0; |
| dlbrp->dl_xidtest_flg = 0; |
| dlbrp->dl_service_mode = DL_CLDLS; |
| |
| err = dl_op(lh, &mp, DL_BIND_ACK, DL_BIND_ACK_SIZE, dleap, NULL); |
| if (err == 0) { |
| dlbap = (dl_bind_ack_t *)mp->b_rptr; |
| if (dlbap->dl_sap != sap) { |
| cmn_err(CE_NOTE, "!dl_bind: DL_BIND_ACK: bad sap %u", |
| dlbap->dl_sap); |
| err = EPROTO; |
| } |
| freemsg(mp); |
| } |
| return (err); |
| } |
| |
| /* |
| * Send a DL_PHYS_ADDR_REQ over `lh' and wait for the response. The caller |
| * must set `*physlenp' to the size of `physaddr' (both of which must be |
| * non-NULL); upon success they will be updated to contain the actual physical |
| * address and length. |
| * |
| * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the |
| * caller can get the contents by passing a non-NULL `dleap'). |
| */ |
| int |
| dl_phys_addr(ldi_handle_t lh, uchar_t *physaddr, size_t *physlenp, |
| dl_error_ack_t *dleap) |
| { |
| dl_phys_addr_ack_t *dlpap; |
| mblk_t *mp; |
| int err; |
| t_uscalar_t paddrlen, paddroff; |
| timestruc_t tv; |
| |
| mp = mexchange(NULL, NULL, DL_PHYS_ADDR_REQ_SIZE, M_PROTO, |
| DL_PHYS_ADDR_REQ); |
| if (mp == NULL) |
| return (ENOMEM); |
| |
| ((dl_phys_addr_req_t *)mp->b_rptr)->dl_addr_type = DL_CURR_PHYS_ADDR; |
| |
| /* |
| * In case some provider doesn't implement or NAK the |
| * request, just wait for 15 seconds. |
| */ |
| tv.tv_sec = 15; |
| tv.tv_nsec = 0; |
| |
| err = dl_op(lh, &mp, DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE, dleap, |
| &tv); |
| if (err == 0) { |
| dlpap = (dl_phys_addr_ack_t *)mp->b_rptr; |
| paddrlen = dlpap->dl_addr_length; |
| paddroff = dlpap->dl_addr_offset; |
| if (paddroff == 0 || paddrlen == 0 || paddrlen > *physlenp || |
| !MBLKIN(mp, paddroff, paddrlen)) { |
| cmn_err(CE_NOTE, "!dl_phys_addr: DL_PHYS_ADDR_ACK: " |
| "bad length/offset %d/%d", paddrlen, paddroff); |
| err = EBADMSG; |
| } else { |
| bcopy(mp->b_rptr + paddroff, physaddr, paddrlen); |
| *physlenp = paddrlen; |
| } |
| freemsg(mp); |
| } |
| return (err); |
| } |
| |
| /* |
| * Send a DL_INFO_REQ over `lh' and wait for the response. The caller must |
| * pass a non-NULL `dliap', which upon success will contain the dl_info_ack_t |
| * from the provider. The caller may optionally get the provider's physical |
| * address by passing a non-NULL `physaddr' and setting `*physlenp' to its |
| * size; upon success they will be updated to contain the actual physical |
| * address and its length. |
| * |
| * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the |
| * caller can get the contents by passing a non-NULL `dleap'). |
| */ |
| int |
| dl_info(ldi_handle_t lh, dl_info_ack_t *dliap, uchar_t *physaddr, |
| size_t *physlenp, dl_error_ack_t *dleap) |
| { |
| mblk_t *mp; |
| int err; |
| int addrlen, addroff; |
| |
| mp = mexchange(NULL, NULL, DL_INFO_REQ_SIZE, M_PCPROTO, DL_INFO_REQ); |
| if (mp == NULL) |
| return (ENOMEM); |
| |
| err = dl_op(lh, &mp, DL_INFO_ACK, DL_INFO_ACK_SIZE, dleap, NULL); |
| if (err != 0) |
| return (err); |
| |
| *dliap = *(dl_info_ack_t *)mp->b_rptr; |
| if (physaddr != NULL) { |
| addrlen = dliap->dl_addr_length - ABS(dliap->dl_sap_length); |
| addroff = dliap->dl_addr_offset; |
| if (addroff == 0 || addrlen <= 0 || addrlen > *physlenp || |
| !MBLKIN(mp, addroff, dliap->dl_addr_length)) { |
| cmn_err(CE_NOTE, "!dl_info: DL_INFO_ACK: " |
| "bad length/offset %d/%d", addrlen, addroff); |
| freemsg(mp); |
| return (EBADMSG); |
| } |
| |
| if (dliap->dl_sap_length > 0) |
| addroff += dliap->dl_sap_length; |
| bcopy(mp->b_rptr + addroff, physaddr, addrlen); |
| *physlenp = addrlen; |
| } |
| freemsg(mp); |
| return (err); |
| } |
| |
| /* |
| * Send a DL_NOTIFY_REQ over `lh' and wait for the response. The caller |
| * should set `notesp' to the set of notifications they wish to enable; |
| * upon success it will contain the notifications enabled by the provider. |
| * |
| * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the |
| * caller can get the contents by passing a non-NULL `dleap'). |
| */ |
| int |
| dl_notify(ldi_handle_t lh, uint32_t *notesp, dl_error_ack_t *dleap) |
| { |
| mblk_t *mp; |
| int err; |
| |
| mp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ); |
| if (mp == NULL) |
| return (ENOMEM); |
| |
| ((dl_notify_req_t *)mp->b_rptr)->dl_notifications = *notesp; |
| |
| err = dl_op(lh, &mp, DL_NOTIFY_ACK, DL_NOTIFY_ACK_SIZE, dleap, NULL); |
| if (err == 0) { |
| *notesp = ((dl_notify_ack_t *)mp->b_rptr)->dl_notifications; |
| freemsg(mp); |
| } |
| return (err); |
| } |
| |
| const char * |
| dl_primstr(t_uscalar_t prim) |
| { |
| switch (prim) { |
| case DL_INFO_REQ: return ("DL_INFO_REQ"); |
| case DL_INFO_ACK: return ("DL_INFO_ACK"); |
| case DL_ATTACH_REQ: return ("DL_ATTACH_REQ"); |
| case DL_DETACH_REQ: return ("DL_DETACH_REQ"); |
| case DL_BIND_REQ: return ("DL_BIND_REQ"); |
| case DL_BIND_ACK: return ("DL_BIND_ACK"); |
| case DL_UNBIND_REQ: return ("DL_UNBIND_REQ"); |
| case DL_OK_ACK: return ("DL_OK_ACK"); |
| case DL_ERROR_ACK: return ("DL_ERROR_ACK"); |
| case DL_ENABMULTI_REQ: return ("DL_ENABMULTI_REQ"); |
| case DL_DISABMULTI_REQ: return ("DL_DISABMULTI_REQ"); |
| case DL_PROMISCON_REQ: return ("DL_PROMISCON_REQ"); |
| case DL_PROMISCOFF_REQ: return ("DL_PROMISCOFF_REQ"); |
| case DL_UNITDATA_REQ: return ("DL_UNITDATA_REQ"); |
| case DL_UNITDATA_IND: return ("DL_UNITDATA_IND"); |
| case DL_UDERROR_IND: return ("DL_UDERROR_IND"); |
| case DL_PHYS_ADDR_REQ: return ("DL_PHYS_ADDR_REQ"); |
| case DL_PHYS_ADDR_ACK: return ("DL_PHYS_ADDR_ACK"); |
| case DL_SET_PHYS_ADDR_REQ: return ("DL_SET_PHYS_ADDR_REQ"); |
| case DL_NOTIFY_REQ: return ("DL_NOTIFY_REQ"); |
| case DL_NOTIFY_ACK: return ("DL_NOTIFY_ACK"); |
| case DL_NOTIFY_IND: return ("DL_NOTIFY_IND"); |
| case DL_NOTIFY_CONF: return ("DL_NOTIFY_CONF"); |
| case DL_CAPABILITY_REQ: return ("DL_CAPABILITY_REQ"); |
| case DL_CAPABILITY_ACK: return ("DL_CAPABILITY_ACK"); |
| case DL_CONTROL_REQ: return ("DL_CONTROL_REQ"); |
| case DL_CONTROL_ACK: return ("DL_CONTROL_ACK"); |
| case DL_PASSIVE_REQ: return ("DL_PASSIVE_REQ"); |
| case DL_INTR_MODE_REQ: return ("DL_INTR_MODE_REQ"); |
| case DL_UDQOS_REQ: return ("DL_UDQOS_REQ"); |
| default: return ("<unknown primitive>"); |
| } |
| } |
| |
| const char * |
| dl_errstr(t_uscalar_t err) |
| { |
| switch (err) { |
| case DL_ACCESS: return ("DL_ACCESS"); |
| case DL_BADADDR: return ("DL_BADADDR"); |
| case DL_BADCORR: return ("DL_BADCORR"); |
| case DL_BADDATA: return ("DL_BADDATA"); |
| case DL_BADPPA: return ("DL_BADPPA"); |
| case DL_BADPRIM: return ("DL_BADPRIM"); |
| case DL_BADQOSPARAM: return ("DL_BADQOSPARAM"); |
| case DL_BADQOSTYPE: return ("DL_BADQOSTYPE"); |
| case DL_BADSAP: return ("DL_BADSAP"); |
| case DL_BADTOKEN: return ("DL_BADTOKEN"); |
| case DL_BOUND: return ("DL_BOUND"); |
| case DL_INITFAILED: return ("DL_INITFAILED"); |
| case DL_NOADDR: return ("DL_NOADDR"); |
| case DL_NOTINIT: return ("DL_NOTINIT"); |
| case DL_OUTSTATE: return ("DL_OUTSTATE"); |
| case DL_SYSERR: return ("DL_SYSERR"); |
| case DL_UNSUPPORTED: return ("DL_UNSUPPORTED"); |
| case DL_UNDELIVERABLE: return ("DL_UNDELIVERABLE"); |
| case DL_NOTSUPPORTED: return ("DL_NOTSUPPORTED "); |
| case DL_TOOMANY: return ("DL_TOOMANY"); |
| case DL_NOTENAB: return ("DL_NOTENAB"); |
| case DL_BUSY: return ("DL_BUSY"); |
| case DL_NOAUTO: return ("DL_NOAUTO"); |
| case DL_NOXIDAUTO: return ("DL_NOXIDAUTO"); |
| case DL_NOTESTAUTO: return ("DL_NOTESTAUTO"); |
| case DL_XIDAUTO: return ("DL_XIDAUTO"); |
| case DL_TESTAUTO: return ("DL_TESTAUTO"); |
| case DL_PENDING: return ("DL_PENDING"); |
| default: return ("<unknown error>"); |
| } |
| } |
| |
| const char * |
| dl_mactypestr(t_uscalar_t mactype) |
| { |
| switch (mactype) { |
| case DL_CSMACD: return ("CSMA/CD"); |
| case DL_TPB: return ("Token Bus"); |
| case DL_TPR: return ("Token Ring"); |
| case DL_METRO: return ("Metro Net"); |
| case DL_ETHER: return ("Ethernet"); |
| case DL_HDLC: return ("HDLC"); |
| case DL_CHAR: return ("Sync Character"); |
| case DL_CTCA: return ("CTCA"); |
| case DL_FDDI: return ("FDDI"); |
| case DL_FRAME: return ("Frame Relay (LAPF)"); |
| case DL_MPFRAME: return ("MP Frame Relay"); |
| case DL_ASYNC: return ("Async Character"); |
| case DL_IPX25: return ("X.25 (Classic IP)"); |
| case DL_LOOP: return ("Software Loopback"); |
| case DL_FC: return ("Fiber Channel"); |
| case DL_ATM: return ("ATM"); |
| case DL_IPATM: return ("ATM (Classic IP)"); |
| case DL_X25: return ("X.25 (LAPB)"); |
| case DL_ISDN: return ("ISDN"); |
| case DL_HIPPI: return ("HIPPI"); |
| case DL_100VG: return ("100BaseVG Ethernet"); |
| case DL_100VGTPR: return ("100BaseVG Token Ring"); |
| case DL_ETH_CSMA: return ("Ethernet/IEEE 802.3"); |
| case DL_100BT: return ("100BaseT"); |
| case DL_IB: return ("Infiniband"); |
| case DL_IPV4: return ("IPv4 Tunnel"); |
| case DL_IPV6: return ("IPv6 Tunnel"); |
| case DL_WIFI: return ("IEEE 802.11"); |
| case DL_IPNET: return ("IPNET"); |
| default: return ("<unknown mactype>"); |
| } |
| } |