xref: /illumos-gate/usr/src/uts/common/io/sundlpi.c (revision b6c3f786)
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