xref: /illumos-gate/usr/src/cmd/mdb/common/modules/arp/arp.c (revision 68ac2337c38c8af06edcf32a72e42de36ec72a9d)
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 2006 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 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stropts.h>
31 #include <sys/stream.h>
32 #include <sys/dlpi.h>
33 #include <sys/hook.h>
34 #include <sys/hook_event.h>
35 #include <inet/led.h>
36 #include <inet/common.h>
37 #include <inet/mi.h>
38 #include <inet/arp.h>
39 #include <inet/arp_impl.h>
40 #include <inet/ip.h>
41 #include <netinet/arp.h>
42 
43 #include <mdb/mdb_modapi.h>
44 #include <mdb/mdb_ks.h>
45 
46 typedef struct {
47 	uint32_t	act_cmd;
48 	char		*act_name;
49 	char		*act_type;
50 } arp_cmd_tbl;
51 
52 /*
53  * Table of ARP commands and structure types used for messages between ARP and
54  * IP.
55  */
56 static const arp_cmd_tbl act_list[] = {
57 	{ AR_ENTRY_ADD,		"AR_ENTRY_ADD",		"arp`area_t" },
58 	{ AR_ENTRY_DELETE,	"AR_ENTRY_DELETE",	"arp`ared_t" },
59 	{ AR_ENTRY_QUERY,	"AR_ENTRY_QUERY",	"arp`areq_t" },
60 	{ AR_ENTRY_SQUERY,	"AR_ENTRY_SQUERY",	"arp`area_t" },
61 	{ AR_MAPPING_ADD,	"AR_MAPPING_ADD",	"arp`arma_t" },
62 	{ AR_CLIENT_NOTIFY,	"AR_CLIENT_NOTIFY",	"arp`arcn_t" },
63 	{ AR_INTERFACE_UP,	"AR_INTERFACE_UP",	"arp`arc_t" },
64 	{ AR_INTERFACE_DOWN,	"AR_INTERFACE_DOWN",	"arp`arc_t" },
65 	{ AR_INTERFACE_ON,	"AR_INTERFACE_ON",	"arp`arc_t" },
66 	{ AR_INTERFACE_OFF,	"AR_INTERFACE_OFF",	"arp`arc_t" },
67 	{ AR_DLPIOP_DONE,	"AR_DLPIOP_DONE",	"arp`arc_t" },
68 	{ AR_ARP_CLOSING,	"AR_ARP_CLOSING",	"arp`arc_t" },
69 	{ AR_ARP_EXTEND,	"AR_ARP_EXTEND",	"arp`arc_t" },
70 	{ 0,			"unknown command",	"arp`arc_t" }
71 };
72 
73 /*
74  * State information kept during walk over ACE hash table and unhashed mask
75  * list.
76  */
77 typedef struct ace_walk_data {
78 	ace_t *awd_hash_tbl[ARP_HASH_SIZE];
79 	ace_t *awd_masks;
80 	int awd_idx;
81 } ace_walk_data_t;
82 
83 static int
84 arl_walk_init(mdb_walk_state_t *wsp)
85 {
86 	if (wsp->walk_addr == NULL &&
87 	    mdb_readvar(&wsp->walk_addr, "arl_g_head") == -1) {
88 		mdb_warn("failed to read 'arl_g_head'");
89 		return (WALK_ERR);
90 	}
91 	return (WALK_NEXT);
92 }
93 
94 static int
95 arl_walk_step(mdb_walk_state_t *wsp)
96 {
97 	uintptr_t addr = wsp->walk_addr;
98 	arl_t arl;
99 
100 	if (wsp->walk_addr == NULL)
101 		return (WALK_DONE);
102 
103 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
104 		mdb_warn("failed to read arl_t at %p", addr);
105 		return (WALK_ERR);
106 	}
107 
108 	wsp->walk_addr = (uintptr_t)arl.arl_next;
109 
110 	return ((*wsp->walk_callback)(addr, &arl, wsp->walk_cbdata));
111 }
112 
113 static int
114 ace_walk_init(mdb_walk_state_t *wsp)
115 {
116 	ace_walk_data_t *aw;
117 
118 	if (wsp->walk_addr != NULL) {
119 		mdb_warn("ace supports only global walks\n");
120 		return (WALK_ERR);
121 	}
122 
123 	aw = mdb_alloc(sizeof (ace_walk_data_t), UM_SLEEP);
124 
125 	if (mdb_readsym(aw->awd_hash_tbl, sizeof (aw->awd_hash_tbl),
126 	    "ar_ce_hash_tbl") == -1) {
127 		mdb_warn("failed to read 'ar_ce_hash_tbl'");
128 		mdb_free(aw, sizeof (ace_walk_data_t));
129 		return (WALK_ERR);
130 	}
131 
132 	if (mdb_readvar(&aw->awd_masks, "ar_ce_mask_entries") == -1) {
133 		mdb_warn("failed to read 'ar_ce_mask_entries'");
134 		mdb_free(aw, sizeof (ace_walk_data_t));
135 		return (WALK_ERR);
136 	}
137 
138 	/* The step routine will start off by incrementing to index 0 */
139 	aw->awd_idx = -1;
140 	wsp->walk_addr = 0;
141 	wsp->walk_data = aw;
142 
143 	return (WALK_NEXT);
144 }
145 
146 static int
147 ace_walk_step(mdb_walk_state_t *wsp)
148 {
149 	uintptr_t addr;
150 	ace_walk_data_t *aw = wsp->walk_data;
151 	ace_t ace;
152 
153 	/*
154 	 * If we're at the end of the previous list, then find the start of the
155 	 * next list to process.
156 	 */
157 	while (wsp->walk_addr == NULL) {
158 		if (aw->awd_idx == ARP_HASH_SIZE)
159 			return (WALK_DONE);
160 		if (++aw->awd_idx == ARP_HASH_SIZE) {
161 			wsp->walk_addr = (uintptr_t)aw->awd_masks;
162 		} else {
163 			wsp->walk_addr =
164 			    (uintptr_t)aw->awd_hash_tbl[aw->awd_idx];
165 		}
166 	}
167 
168 	addr = wsp->walk_addr;
169 	if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
170 		mdb_warn("failed to read ace_t at %p", addr);
171 		return (WALK_ERR);
172 	}
173 
174 	wsp->walk_addr = (uintptr_t)ace.ace_next;
175 
176 	return (wsp->walk_callback(addr, &ace, wsp->walk_cbdata));
177 }
178 
179 static void
180 ace_walk_fini(mdb_walk_state_t *wsp)
181 {
182 	mdb_free(wsp->walk_data, sizeof (ace_walk_data_t));
183 }
184 
185 /* Common routine to produce an 'ar' text description */
186 static void
187 ar_describe(const ar_t *ar, char *buf, size_t nbytes, boolean_t addmac)
188 {
189 	if (ar->ar_arl == NULL) {
190 		queue_t wq, ipq;
191 		ill_t ill;
192 		char name[LIFNAMSIZ];
193 		GElf_Sym sym;
194 		boolean_t nextip;
195 
196 		if (mdb_vread(&wq, sizeof (wq), (uintptr_t)ar->ar_wq) == -1 ||
197 		    mdb_vread(&ipq, sizeof (ipq), (uintptr_t)wq.q_next) == -1)
198 			return;
199 
200 		nextip =
201 		    (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0 &&
202 		    (uintptr_t)sym.st_value == (uintptr_t)ipq.q_qinfo);
203 
204 		if (!ar->ar_on_ill_stream) {
205 			(void) strcpy(buf, nextip ? "Client" : "Unknown");
206 			return;
207 		}
208 
209 		if (!nextip ||
210 		    mdb_vread(&ill, sizeof (ill), (uintptr_t)ipq.q_ptr) == -1 ||
211 		    mdb_readstr(name, sizeof (name),
212 		    (uintptr_t)ill.ill_name) == -1) {
213 			return;
214 		}
215 		(void) mdb_snprintf(buf, nbytes, "IP %s", name);
216 	} else {
217 		arl_t arl;
218 		arlphy_t ap;
219 		ssize_t retv;
220 		uint32_t alen;
221 		uchar_t macaddr[ARP_MAX_ADDR_LEN];
222 
223 		if (mdb_vread(&arl, sizeof (arl), (uintptr_t)ar->ar_arl) == -1)
224 			return;
225 		retv = mdb_snprintf(buf, nbytes, "ARP %s ", arl.arl_name);
226 		if (retv >= nbytes || !addmac)
227 			return;
228 		if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl.arl_phy) == -1)
229 			return;
230 		alen = ap.ap_hw_addrlen;
231 		if (ap.ap_hw_addr == NULL || alen == 0 ||
232 		    alen > sizeof (macaddr))
233 			return;
234 		if (mdb_vread(macaddr, alen, (uintptr_t)ap.ap_hw_addr) == -1)
235 			return;
236 		mdb_mac_addr(macaddr, alen, buf + retv, nbytes - retv);
237 	}
238 }
239 
240 /* ARGSUSED2 */
241 static int
242 ar_cb(uintptr_t addr, const void *arptr, void *dummy)
243 {
244 	const ar_t *ar = arptr;
245 	char ardesc[sizeof ("ARP  ") + LIFNAMSIZ];
246 
247 	ar_describe(ar, ardesc, sizeof (ardesc), B_FALSE);
248 	mdb_printf("%?p %?p %?p %s\n", addr, ar->ar_wq, ar->ar_arl, ardesc);
249 	return (WALK_NEXT);
250 }
251 
252 /*
253  * Print out ARP client structures.
254  */
255 /* ARGSUSED2 */
256 static int
257 ar_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
258 {
259 	ar_t ar;
260 
261 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
262 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
263 		    "AR", "WQ", "ARL", "TYPE");
264 	}
265 
266 	if (flags & DCMD_ADDRSPEC) {
267 		if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
268 			mdb_warn("failed to read ar_t at %p", addr);
269 			return (DCMD_ERR);
270 		}
271 		(void) ar_cb(addr, &ar, NULL);
272 	} else {
273 		if (mdb_walk("ar", ar_cb, NULL) == -1) {
274 			mdb_warn("cannot walk ar_t structures");
275 			return (DCMD_ERR);
276 		}
277 	}
278 	return (DCMD_OK);
279 }
280 
281 /* ARGSUSED2 */
282 static int
283 arl_cb(uintptr_t addr, const void *arlptr, void *dummy)
284 {
285 	const arl_t *arl = arlptr;
286 	arlphy_t ap;
287 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
288 	char macstr[ARP_MAX_ADDR_LEN*3];
289 	char flags[4];
290 	const char *primstr;
291 
292 	mdb_printf("%?p  ", addr);
293 	if (arl->arl_dlpi_pending == DL_PRIM_INVAL)
294 		mdb_printf("%16s", "--");
295 	else if ((primstr = mdb_dlpi_prim(arl->arl_dlpi_pending)) != NULL)
296 		mdb_printf("%16s", primstr);
297 	else
298 		mdb_printf("%16x", arl->arl_dlpi_pending);
299 
300 	if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl->arl_phy) == -1 ||
301 	    ap.ap_hw_addrlen == 0 || ap.ap_hw_addrlen > sizeof (macaddr)) {
302 		(void) strcpy(macstr, "--");
303 	} else if (mdb_vread(macaddr, ap.ap_hw_addrlen,
304 	    (uintptr_t)ap.ap_hw_addr) == -1) {
305 		(void) strcpy(macstr, "?");
306 	} else {
307 		mdb_mac_addr(macaddr, ap.ap_hw_addrlen, macstr,
308 		    sizeof (macstr));
309 	}
310 
311 	/* Print both the link-layer state and the NOARP flag */
312 	flags[0] = '\0';
313 	if (arl->arl_flags & ARL_F_NOARP)
314 		(void) strcat(flags, "N");
315 	switch (arl->arl_state) {
316 	case ARL_S_DOWN:
317 		(void) strcat(flags, "d");
318 		break;
319 	case ARL_S_PENDING:
320 		(void) strcat(flags, "P");
321 		break;
322 	case ARL_S_UP:
323 		(void) strcat(flags, "U");
324 		break;
325 	default:
326 		(void) strcat(flags, "?");
327 		break;
328 	}
329 	mdb_printf("  %8d  %-3s  %-9s  %s\n",
330 	    mdb_mblk_count(arl->arl_dlpi_deferred), flags, arl->arl_name,
331 	    macstr);
332 	return (WALK_NEXT);
333 }
334 
335 /*
336  * Print out ARP link-layer elements.
337  */
338 /* ARGSUSED2 */
339 static int
340 arl_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
341 {
342 	arl_t arl;
343 
344 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
345 		mdb_printf("%<u>%?s  %16s  %8s  %3s  %9s  %s%</u>\n",
346 		    "ARL", "DLPI REQ", "DLPI CNT", "FLG", "INTERFACE",
347 		    "HWADDR");
348 	}
349 
350 	if (flags & DCMD_ADDRSPEC) {
351 		if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
352 			mdb_warn("failed to read arl_t at %p", addr);
353 			return (DCMD_ERR);
354 		}
355 		(void) arl_cb(addr, &arl, NULL);
356 	} else {
357 		if (mdb_walk("arl", arl_cb, NULL) == -1) {
358 			mdb_warn("cannot walk arl_t structures");
359 			return (DCMD_ERR);
360 		}
361 	}
362 	return (DCMD_OK);
363 }
364 
365 /* ARGSUSED2 */
366 static int
367 ace_cb(uintptr_t addr, const void *aceptr, void *dummy)
368 {
369 	const ace_t *ace = aceptr;
370 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
371 	char macstr[ARP_MAX_ADDR_LEN*3];
372 	/* The %b format isn't compact enough for long listings */
373 	static const char ace_flags[] = "SPDRMLdA ofya";
374 	const char *cp;
375 	char flags[sizeof (ace_flags)], *fp;
376 	int flg;
377 	in_addr_t inaddr, mask;
378 	char addrstr[sizeof ("255.255.255.255/32")];
379 
380 	/* Walk the list of flags and produce a string */
381 	cp = ace_flags;
382 	fp = flags;
383 	for (flg = 1; *cp != '\0'; flg <<= 1, cp++) {
384 		if ((flg & ace->ace_flags) && *cp != ' ')
385 			*fp++ = *cp;
386 	}
387 	*fp = '\0';
388 
389 	/* If it's not resolved, then it has no hardware address */
390 	if (!(ace->ace_flags & ACE_F_RESOLVED) ||
391 	    ace->ace_hw_addr_length == 0 ||
392 	    ace->ace_hw_addr_length > sizeof (macaddr)) {
393 		(void) strcpy(macstr, "--");
394 	} else if (mdb_vread(macaddr, ace->ace_hw_addr_length,
395 	    (uintptr_t)ace->ace_hw_addr) == -1) {
396 		(void) strcpy(macstr, "?");
397 	} else {
398 		mdb_mac_addr(macaddr, ace->ace_hw_addr_length, macstr,
399 		    sizeof (macstr));
400 	}
401 
402 	/*
403 	 * Nothing other than IP uses ARP these days, so we don't try very hard
404 	 * here to switch out on ARP protocol type.  (Note that ARP protocol
405 	 * types are roughly Ethertypes, but are allocated separately at IANA.)
406 	 */
407 	if (ace->ace_proto != IP_ARP_PROTO_TYPE) {
408 		(void) mdb_snprintf(addrstr, sizeof (addrstr),
409 		    "Unknown proto %x", ace->ace_proto);
410 	} else if (mdb_vread(&inaddr, sizeof (inaddr),
411 	    (uintptr_t)ace->ace_proto_addr) != -1 &&
412 	    mdb_vread(&mask, sizeof (mask), (uintptr_t)ace->ace_proto_mask) !=
413 	    -1) {
414 		/*
415 		 * If it's the standard host mask, then print it normally.
416 		 * Otherwise, use "/n" notation.
417 		 */
418 		if (mask == (in_addr_t)~0) {
419 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I",
420 			    inaddr);
421 		} else {
422 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I/%d",
423 			    inaddr, mask == 0 ? 0 : 33 - mdb_ffs(mask));
424 		}
425 	} else {
426 		(void) strcpy(addrstr, "?");
427 	}
428 	mdb_printf("%?p  %-18s  %-8s  %s\n", addr, addrstr, flags, macstr);
429 	return (WALK_NEXT);
430 }
431 
432 /*
433  * Print out ARP cache entry (ace_t) elements.
434  */
435 /* ARGSUSED2 */
436 static int
437 ace_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
438 {
439 	ace_t ace;
440 
441 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
442 		mdb_printf("%<u>%?s  %-18s  %-8s  %s%</u>\n",
443 		    "ACE", "PROTOADDR", "FLAGS", "HWADDR");
444 	}
445 
446 	if (flags & DCMD_ADDRSPEC) {
447 		if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
448 			mdb_warn("failed to read ace_t at %p", addr);
449 			return (DCMD_ERR);
450 		}
451 		(void) ace_cb(addr, &ace, NULL);
452 	} else {
453 		if (mdb_walk("ace", ace_cb, NULL) == -1) {
454 			mdb_warn("cannot walk ace_t structures");
455 			return (DCMD_ERR);
456 		}
457 	}
458 	return (DCMD_OK);
459 }
460 
461 /*
462  * Print an ARP hardware and protocol address pair; used when printing an ARP
463  * message.
464  */
465 static void
466 print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
467 {
468 	char macstr[ARP_MAX_ADDR_LEN*3];
469 	in_addr_t inaddr;
470 
471 	if (arh->arh_hlen == 0)
472 		(void) strcpy(macstr, "(none)");
473 	else
474 		mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
475 	mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
476 	if (arh->arh_plen == 0) {
477 		mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
478 	} else if (ptype == IP_ARP_PROTO_TYPE) {
479 		mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
480 	} else if (arh->arh_plen == sizeof (in_addr_t)) {
481 		(void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
482 		mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
483 	} else {
484 		mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
485 	}
486 }
487 
488 /*
489  * Decode an ARP message and display it.
490  */
491 /* ARGSUSED2 */
492 static int
493 arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
494 {
495 	struct {
496 		arh_t arh;
497 		uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
498 	} arp;
499 	size_t blen;
500 	uint16_t htype, ptype, op;
501 	const char *cp;
502 
503 	if (!(flags & DCMD_ADDRSPEC)) {
504 		mdb_warn("address required to print ARP header\n");
505 		return (DCMD_ERR);
506 	}
507 
508 	if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
509 		mdb_warn("unable to read ARP header at %p", addr);
510 		return (DCMD_ERR);
511 	}
512 	mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
513 	mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
514 	mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));
515 
516 	switch (htype) {
517 	case ARPHRD_ETHER:
518 		cp = "Ether";
519 		break;
520 	case ARPHRD_IEEE802:
521 		cp = "IEEE802";
522 		break;
523 	case ARPHRD_IB:
524 		cp = "InfiniBand";
525 		break;
526 	default:
527 		cp = "Unknown";
528 		break;
529 	}
530 	mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
531 	mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
532 	    ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");
533 
534 	switch (op) {
535 	case ARPOP_REQUEST:
536 		cp = "ares_op$REQUEST";
537 		break;
538 	case ARPOP_REPLY:
539 		cp = "ares_op$REPLY";
540 		break;
541 	case REVARP_REQUEST:
542 		cp = "arev_op$REQUEST";
543 		break;
544 	case REVARP_REPLY:
545 		cp = "arev_op$REPLY";
546 		break;
547 	default:
548 		cp = "Unknown";
549 		break;
550 	}
551 	mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);
552 
553 	/*
554 	 * Note that we go to some length to attempt to print out the fixed
555 	 * header data before trying to decode the variable-length data.  This
556 	 * is done to maximize the amount of useful information shown when the
557 	 * buffer is truncated or otherwise corrupt.
558 	 */
559 	blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
560 	if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
561 		mdb_warn("unable to read ARP body at %p", addr);
562 		return (DCMD_ERR);
563 	}
564 
565 	print_arp('s', arp.addrs, &arp.arh, ptype);
566 	print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
567 	    &arp.arh, ptype);
568 	return (DCMD_OK);
569 }
570 
571 /*
572  * Print out an arp command formatted in a reasonable manner.  This implements
573  * the type switch used by ARP.
574  *
575  * It could also dump the data that follows the header (using offset and length
576  * in the various structures), but it currently does not.
577  */
578 /* ARGSUSED2 */
579 static int
580 arpcmd_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
581 {
582 	arc_t arc;
583 	const arp_cmd_tbl *tp;
584 	mdb_arg_t subargv;
585 
586 	if (!(flags & DCMD_ADDRSPEC)) {
587 		mdb_warn("address required to print ARP command\n");
588 		return (DCMD_ERR);
589 	}
590 	if (mdb_vread(&arc, sizeof (arc), addr) == -1) {
591 		mdb_warn("unable to read arc_t at %p", addr);
592 		return (DCMD_ERR);
593 	}
594 	for (tp = act_list; tp->act_cmd != 0; tp++)
595 		if (tp->act_cmd == arc.arc_cmd)
596 			break;
597 	mdb_printf("%p %s (%s) = ", addr, tp->act_name, tp->act_type);
598 	subargv.a_type = MDB_TYPE_STRING;
599 	subargv.a_un.a_str = tp->act_type;
600 	if (mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, &subargv) == -1)
601 		return (DCMD_ERR);
602 	else
603 		return (DCMD_OK);
604 }
605 
606 static size_t
607 mi_osize(const queue_t *q)
608 {
609 	/*
610 	 * The code in common/inet/mi.c allocates an extra word to store the
611 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
612 	 */
613 	struct mi_block {
614 		size_t mi_nbytes;
615 		struct mi_o_s mi_o;
616 	} m;
617 
618 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr - sizeof (m)) != -1)
619 		return (m.mi_nbytes - sizeof (m));
620 
621 	return (0);
622 }
623 
624 /*
625  * This is called when ::stream is used and an ARP module is seen on the
626  * stream.  Determine what sort of ARP usage is involved and show an
627  * appropriate message.
628  */
629 static void
630 arp_qinfo(const queue_t *qp, char *buf, size_t nbytes)
631 {
632 	size_t size = mi_osize(qp);
633 	ar_t ar;
634 
635 	if (size != sizeof (ar_t))
636 		return;
637 	if (mdb_vread(&ar, sizeof (ar), (uintptr_t)qp->q_ptr) == -1)
638 		return;
639 	ar_describe(&ar, buf, nbytes, B_TRUE);
640 }
641 
642 static uintptr_t
643 arp_rnext(const queue_t *q)
644 {
645 	size_t size = mi_osize(q);
646 	ar_t ar;
647 
648 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
649 	    (uintptr_t)q->q_ptr) != -1)
650 		return ((uintptr_t)ar.ar_rq);
651 
652 	return (NULL);
653 }
654 
655 static uintptr_t
656 arp_wnext(const queue_t *q)
657 {
658 	size_t size = mi_osize(q);
659 	ar_t ar;
660 
661 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
662 	    (uintptr_t)q->q_ptr) != -1)
663 		return ((uintptr_t)ar.ar_wq);
664 
665 	return (NULL);
666 }
667 
668 static const mdb_dcmd_t dcmds[] = {
669 	{ "ar", "?", "display ARP client streams", ar_cmd, NULL },
670 	{ "arl", "?", "display ARP link layers", arl_cmd, NULL },
671 	{ "ace", "?", "display ARP cache entries", ace_cmd, NULL },
672 	{ "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
673 	{ "arpcmd", ":", "display an ARP command", arpcmd_cmd, NULL },
674 	{ NULL }
675 };
676 
677 /* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
678 static const mdb_walker_t walkers[] = {
679 	{ "arl", "walk list of arl_t links",
680 	    arl_walk_init, arl_walk_step, NULL },
681 	{ "ace", "walk list of ace_t entries",
682 	    ace_walk_init, ace_walk_step, ace_walk_fini },
683 	{ NULL }
684 };
685 
686 static const mdb_qops_t arp_qops = { arp_qinfo, arp_rnext, arp_wnext };
687 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
688 
689 const mdb_modinfo_t *
690 _mdb_init(void)
691 {
692 	GElf_Sym sym;
693 
694 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
695 		mdb_qops_install(&arp_qops, (uintptr_t)sym.st_value);
696 
697 	return (&modinfo);
698 }
699 
700 void
701 _mdb_fini(void)
702 {
703 	GElf_Sym sym;
704 
705 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
706 		mdb_qops_remove(&arp_qops, (uintptr_t)sym.st_value);
707 }
708