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