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