xref: /illumos-gate/usr/src/cmd/mdb/common/modules/arp/arp.c (revision eda50310abb3984bab11856a2aca8936d26881cb)
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 		ssize_t retv;
219 		uint32_t alen;
220 		uchar_t macaddr[ARP_MAX_ADDR_LEN];
221 
222 		if (mdb_vread(&arl, sizeof (arl), (uintptr_t)ar->ar_arl) == -1)
223 			return;
224 		retv = mdb_snprintf(buf, nbytes, "ARP %s ", arl.arl_name);
225 		if (retv >= nbytes || !addmac)
226 			return;
227 		alen = arl.arl_hw_addr_length;
228 		if (arl.arl_hw_addr == NULL || alen == 0 ||
229 		    alen > sizeof (macaddr))
230 			return;
231 		if (mdb_vread(macaddr, alen, (uintptr_t)arl.arl_hw_addr) == -1)
232 			return;
233 		mdb_mac_addr(macaddr, alen, buf + retv, nbytes - retv);
234 	}
235 }
236 
237 /* ARGSUSED2 */
238 static int
239 ar_cb(uintptr_t addr, const void *arptr, void *dummy)
240 {
241 	const ar_t *ar = arptr;
242 	char ardesc[sizeof ("ARP  ") + LIFNAMSIZ];
243 
244 	ar_describe(ar, ardesc, sizeof (ardesc), B_FALSE);
245 	mdb_printf("%?p %?p %?p %s\n", addr, ar->ar_wq, ar->ar_arl, ardesc);
246 	return (WALK_NEXT);
247 }
248 
249 /*
250  * Print out ARP client structures.
251  */
252 /* ARGSUSED2 */
253 static int
254 ar_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
255 {
256 	ar_t ar;
257 
258 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
259 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
260 		    "AR", "WQ", "ARL", "TYPE");
261 	}
262 
263 	if (flags & DCMD_ADDRSPEC) {
264 		if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
265 			mdb_warn("failed to read ar_t at %p", addr);
266 			return (DCMD_ERR);
267 		}
268 		(void) ar_cb(addr, &ar, NULL);
269 	} else {
270 		if (mdb_walk("ar", ar_cb, NULL) == -1) {
271 			mdb_warn("cannot walk ar_t structures");
272 			return (DCMD_ERR);
273 		}
274 	}
275 	return (DCMD_OK);
276 }
277 
278 /* ARGSUSED2 */
279 static int
280 arl_cb(uintptr_t addr, const void *arlptr, void *dummy)
281 {
282 	const arl_t *arl = arlptr;
283 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
284 	char macstr[ARP_MAX_ADDR_LEN*3];
285 	char flags[4];
286 	const char *primstr;
287 
288 	mdb_printf("%?p  ", addr);
289 	if (arl->arl_dlpi_pending == DL_PRIM_INVAL)
290 		mdb_printf("%16s", "--");
291 	else if ((primstr = mdb_dlpi_prim(arl->arl_dlpi_pending)) != NULL)
292 		mdb_printf("%16s", primstr);
293 	else
294 		mdb_printf("%16x", arl->arl_dlpi_pending);
295 	if (arl->arl_hw_addr_length == 0 ||
296 	    arl->arl_hw_addr_length > sizeof (macaddr)) {
297 		(void) strcpy(macstr, "--");
298 	} else if (mdb_vread(macaddr, arl->arl_hw_addr_length,
299 	    (uintptr_t)arl->arl_hw_addr) == -1) {
300 		(void) strcpy(macstr, "?");
301 	} else {
302 		mdb_mac_addr(macaddr, arl->arl_hw_addr_length, macstr,
303 		    sizeof (macstr));
304 	}
305 
306 	/* Print both the link-layer state and the NOARP flag */
307 	flags[0] = '\0';
308 	if (arl->arl_flags & ARL_F_NOARP)
309 		(void) strcat(flags, "N");
310 	switch (arl->arl_state) {
311 	case ARL_S_DOWN:
312 		(void) strcat(flags, "d");
313 		break;
314 	case ARL_S_PENDING:
315 		(void) strcat(flags, "P");
316 		break;
317 	case ARL_S_UP:
318 		(void) strcat(flags, "U");
319 		break;
320 	default:
321 		(void) strcat(flags, "?");
322 		break;
323 	}
324 	mdb_printf("  %8d  %-3s  %-9s  %s\n",
325 	    mdb_mblk_count(arl->arl_dlpi_deferred), flags, arl->arl_name,
326 	    macstr);
327 	return (WALK_NEXT);
328 }
329 
330 /*
331  * Print out ARP link-layer elements.
332  */
333 /* ARGSUSED2 */
334 static int
335 arl_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
336 {
337 	arl_t arl;
338 
339 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
340 		mdb_printf("%<u>%?s  %16s  %8s  %3s  %9s  %s%</u>\n",
341 		    "ARL", "DLPI REQ", "DLPI CNT", "FLG", "INTERFACE",
342 		    "HW ADDR");
343 	}
344 
345 	if (flags & DCMD_ADDRSPEC) {
346 		if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
347 			mdb_warn("failed to read arl_t at %p", addr);
348 			return (DCMD_ERR);
349 		}
350 		(void) arl_cb(addr, &arl, NULL);
351 	} else {
352 		if (mdb_walk("arl", arl_cb, NULL) == -1) {
353 			mdb_warn("cannot walk arl_t structures");
354 			return (DCMD_ERR);
355 		}
356 	}
357 	return (DCMD_OK);
358 }
359 
360 /* ARGSUSED2 */
361 static int
362 ace_cb(uintptr_t addr, const void *aceptr, void *dummy)
363 {
364 	const ace_t *ace = aceptr;
365 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
366 	char macstr[ARP_MAX_ADDR_LEN*3];
367 	/* The %b format isn't compact enough for long listings */
368 	static const char ace_flags[] = "SPDRMLdA ofya";
369 	const char *cp;
370 	char flags[sizeof (ace_flags)], *fp;
371 	int flg;
372 	in_addr_t inaddr, mask;
373 	char addrstr[sizeof ("255.255.255.255/32")];
374 
375 	/* Walk the list of flags and produce a string */
376 	cp = ace_flags;
377 	fp = flags;
378 	for (flg = 1; *cp != '\0'; flg <<= 1, cp++) {
379 		if ((flg & ace->ace_flags) && *cp != ' ')
380 			*fp++ = *cp;
381 	}
382 	*fp = '\0';
383 
384 	/* If it's not resolved, then it has no hardware address */
385 	if (!(ace->ace_flags & ACE_F_RESOLVED) ||
386 	    ace->ace_hw_addr_length == 0 ||
387 	    ace->ace_hw_addr_length > sizeof (macaddr)) {
388 		(void) strcpy(macstr, "--");
389 	} else if (mdb_vread(macaddr, ace->ace_hw_addr_length,
390 	    (uintptr_t)ace->ace_hw_addr) == -1) {
391 		(void) strcpy(macstr, "?");
392 	} else {
393 		mdb_mac_addr(macaddr, ace->ace_hw_addr_length, macstr,
394 		    sizeof (macstr));
395 	}
396 
397 	/*
398 	 * Nothing other than IP uses ARP these days, so we don't try very hard
399 	 * here to switch out on ARP protocol type.  (Note that ARP protocol
400 	 * types are roughly Ethertypes, but are allocated separately at IANA.)
401 	 */
402 	if (ace->ace_proto != IP_ARP_PROTO_TYPE) {
403 		(void) mdb_snprintf(addrstr, sizeof (addrstr),
404 		    "Unknown proto %x", ace->ace_proto);
405 	} else if (mdb_vread(&inaddr, sizeof (inaddr),
406 	    (uintptr_t)ace->ace_proto_addr) != -1 &&
407 	    mdb_vread(&mask, sizeof (mask), (uintptr_t)ace->ace_proto_mask) !=
408 	    -1) {
409 		/*
410 		 * If it's the standard host mask, then print it normally.
411 		 * Otherwise, use "/n" notation.
412 		 */
413 		if (mask == (in_addr_t)~0) {
414 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I",
415 			    inaddr);
416 		} else {
417 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I/%d",
418 			    inaddr, mask == 0 ? 0 : 33 - mdb_ffs(mask));
419 		}
420 	} else {
421 		(void) strcpy(addrstr, "?");
422 	}
423 	mdb_printf("%?p  %-18s  %-8s  %s\n", addr, addrstr, flags, macstr);
424 	return (WALK_NEXT);
425 }
426 
427 /*
428  * Print out ARP cache entry (ace_t) elements.
429  */
430 /* ARGSUSED2 */
431 static int
432 ace_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
433 {
434 	ace_t ace;
435 
436 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
437 		mdb_printf("%<u>%?s  %-18s  %-8s  %s%</u>\n",
438 		    "ACE", "PROTOADDR", "FLAGS", "HWADDR");
439 	}
440 
441 	if (flags & DCMD_ADDRSPEC) {
442 		if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
443 			mdb_warn("failed to read ace_t at %p", addr);
444 			return (DCMD_ERR);
445 		}
446 		(void) ace_cb(addr, &ace, NULL);
447 	} else {
448 		if (mdb_walk("ace", ace_cb, NULL) == -1) {
449 			mdb_warn("cannot walk ace_t structures");
450 			return (DCMD_ERR);
451 		}
452 	}
453 	return (DCMD_OK);
454 }
455 
456 /*
457  * Print an ARP hardware and protocol address pair; used when printing an ARP
458  * message.
459  */
460 static void
461 print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
462 {
463 	char macstr[ARP_MAX_ADDR_LEN*3];
464 	in_addr_t inaddr;
465 
466 	if (arh->arh_hlen == 0)
467 		(void) strcpy(macstr, "(none)");
468 	else
469 		mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
470 	mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
471 	if (arh->arh_plen == 0) {
472 		mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
473 	} else if (ptype == IP_ARP_PROTO_TYPE) {
474 		mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
475 	} else if (arh->arh_plen == sizeof (in_addr_t)) {
476 		(void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
477 		mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
478 	} else {
479 		mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
480 	}
481 }
482 
483 /*
484  * Decode an ARP message and display it.
485  */
486 /* ARGSUSED2 */
487 static int
488 arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
489 {
490 	struct {
491 		arh_t arh;
492 		uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
493 	} arp;
494 	size_t blen;
495 	uint16_t htype, ptype, op;
496 	const char *cp;
497 
498 	if (!(flags & DCMD_ADDRSPEC)) {
499 		mdb_warn("address required to print ARP header\n");
500 		return (DCMD_ERR);
501 	}
502 
503 	if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
504 		mdb_warn("unable to read ARP header at %p", addr);
505 		return (DCMD_ERR);
506 	}
507 	mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
508 	mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
509 	mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));
510 
511 	switch (htype) {
512 	case ARPHRD_ETHER:
513 		cp = "Ether";
514 		break;
515 	case ARPHRD_IEEE802:
516 		cp = "IEEE802";
517 		break;
518 	case ARPHRD_IB:
519 		cp = "InfiniBand";
520 		break;
521 	default:
522 		cp = "Unknown";
523 		break;
524 	}
525 	mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
526 	mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
527 	    ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");
528 
529 	switch (op) {
530 	case ARPOP_REQUEST:
531 		cp = "ares_op$REQUEST";
532 		break;
533 	case ARPOP_REPLY:
534 		cp = "ares_op$REPLY";
535 		break;
536 	case REVARP_REQUEST:
537 		cp = "arev_op$REQUEST";
538 		break;
539 	case REVARP_REPLY:
540 		cp = "arev_op$REPLY";
541 		break;
542 	default:
543 		cp = "Unknown";
544 		break;
545 	}
546 	mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);
547 
548 	/*
549 	 * Note that we go to some length to attempt to print out the fixed
550 	 * header data before trying to decode the variable-length data.  This
551 	 * is done to maximize the amount of useful information shown when the
552 	 * buffer is truncated or otherwise corrupt.
553 	 */
554 	blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
555 	if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
556 		mdb_warn("unable to read ARP body at %p", addr);
557 		return (DCMD_ERR);
558 	}
559 
560 	print_arp('s', arp.addrs, &arp.arh, ptype);
561 	print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
562 	    &arp.arh, ptype);
563 	return (DCMD_OK);
564 }
565 
566 /*
567  * Print out an arp command formatted in a reasonable manner.  This implements
568  * the type switch used by ARP.
569  *
570  * It could also dump the data that follows the header (using offset and length
571  * in the various structures), but it currently does not.
572  */
573 /* ARGSUSED2 */
574 static int
575 arpcmd_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
576 {
577 	arc_t arc;
578 	const arp_cmd_tbl *tp;
579 	mdb_arg_t subargv;
580 
581 	if (!(flags & DCMD_ADDRSPEC)) {
582 		mdb_warn("address required to print ARP command\n");
583 		return (DCMD_ERR);
584 	}
585 	if (mdb_vread(&arc, sizeof (arc), addr) == -1) {
586 		mdb_warn("unable to read arc_t at %p", addr);
587 		return (DCMD_ERR);
588 	}
589 	for (tp = act_list; tp->act_cmd != 0; tp++)
590 		if (tp->act_cmd == arc.arc_cmd)
591 			break;
592 	mdb_printf("%p %s (%s) = ", addr, tp->act_name, tp->act_type);
593 	subargv.a_type = MDB_TYPE_STRING;
594 	subargv.a_un.a_str = tp->act_type;
595 	if (mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, &subargv) == -1)
596 		return (DCMD_ERR);
597 	else
598 		return (DCMD_OK);
599 }
600 
601 static size_t
602 mi_osize(const queue_t *q)
603 {
604 	/*
605 	 * The code in common/inet/mi.c allocates an extra word to store the
606 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
607 	 */
608 	struct mi_block {
609 		size_t mi_nbytes;
610 		struct mi_o_s mi_o;
611 	} m;
612 
613 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr - sizeof (m)) != -1)
614 		return (m.mi_nbytes - sizeof (m));
615 
616 	return (0);
617 }
618 
619 /*
620  * This is called when ::stream is used and an ARP module is seen on the
621  * stream.  Determine what sort of ARP usage is involved and show an
622  * appropriate message.
623  */
624 static void
625 arp_qinfo(const queue_t *qp, char *buf, size_t nbytes)
626 {
627 	size_t size = mi_osize(qp);
628 	ar_t ar;
629 
630 	if (size != sizeof (ar_t))
631 		return;
632 	if (mdb_vread(&ar, sizeof (ar), (uintptr_t)qp->q_ptr) == -1)
633 		return;
634 	ar_describe(&ar, buf, nbytes, B_TRUE);
635 }
636 
637 static uintptr_t
638 arp_rnext(const queue_t *q)
639 {
640 	size_t size = mi_osize(q);
641 	ar_t ar;
642 
643 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
644 	    (uintptr_t)q->q_ptr) != -1)
645 		return ((uintptr_t)ar.ar_rq);
646 
647 	return (NULL);
648 }
649 
650 static uintptr_t
651 arp_wnext(const queue_t *q)
652 {
653 	size_t size = mi_osize(q);
654 	ar_t ar;
655 
656 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
657 	    (uintptr_t)q->q_ptr) != -1)
658 		return ((uintptr_t)ar.ar_wq);
659 
660 	return (NULL);
661 }
662 
663 static const mdb_dcmd_t dcmds[] = {
664 	{ "ar", "?", "display ARP client streams", ar_cmd, NULL },
665 	{ "arl", "?", "display ARP link layers", arl_cmd, NULL },
666 	{ "ace", "?", "display ARP cache entries", ace_cmd, NULL },
667 	{ "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
668 	{ "arpcmd", ":", "display an ARP command", arpcmd_cmd, NULL },
669 	{ NULL }
670 };
671 
672 /* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
673 static const mdb_walker_t walkers[] = {
674 	{ "arl", "walk list of arl_t links",
675 	    arl_walk_init, arl_walk_step, NULL },
676 	{ "ace", "walk list of ace_t entries",
677 	    ace_walk_init, ace_walk_step, ace_walk_fini },
678 	{ NULL }
679 };
680 
681 static const mdb_qops_t arp_qops = { arp_qinfo, arp_rnext, arp_wnext };
682 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
683 
684 const mdb_modinfo_t *
685 _mdb_init(void)
686 {
687 	GElf_Sym sym;
688 
689 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
690 		mdb_qops_install(&arp_qops, (uintptr_t)sym.st_value);
691 
692 	return (&modinfo);
693 }
694 
695 void
696 _mdb_fini(void)
697 {
698 	GElf_Sym sym;
699 
700 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
701 		mdb_qops_remove(&arp_qops, (uintptr_t)sym.st_value);
702 }
703