xref: /illumos-gate/usr/src/cmd/mdb/common/modules/arp/arp.c (revision bcd524b5c10222cf2a1ef37ac7ea8bf1baa3a2ee)
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 2007 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 /*
84  * Given the kernel address of an arl_t, return the stackid
85  */
86 static int
87 arl_to_stackid(uintptr_t addr)
88 {
89 	arl_t arl;
90 	queue_t rq;
91 	ar_t ar;
92 	arp_stack_t ass;
93 	netstack_t nss;
94 
95 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
96 		mdb_warn("failed to read arl_t %p", addr);
97 		return (0);
98 	}
99 
100 	addr = (uintptr_t)arl.arl_rq;
101 	if (mdb_vread(&rq, sizeof (rq), addr) == -1) {
102 		mdb_warn("failed to read queue_t %p", addr);
103 		return (0);
104 	}
105 
106 	addr = (uintptr_t)rq.q_ptr;
107 	if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
108 		mdb_warn("failed to read ar_t %p", addr);
109 		return (0);
110 	}
111 
112 	addr = (uintptr_t)ar.ar_as;
113 	if (mdb_vread(&ass, sizeof (ass), addr) == -1) {
114 		mdb_warn("failed to read arp_stack_t %p", addr);
115 		return (0);
116 	}
117 	addr = (uintptr_t)ass.as_netstack;
118 	if (mdb_vread(&nss, sizeof (nss), addr) == -1) {
119 		mdb_warn("failed to read netstack_t %p", addr);
120 		return (0);
121 	}
122 	return (nss.netstack_stackid);
123 }
124 
125 static int
126 arp_stacks_walk_init(mdb_walk_state_t *wsp)
127 {
128 	if (mdb_layered_walk("netstack", wsp) == -1) {
129 		mdb_warn("can't walk 'netstack'");
130 		return (WALK_ERR);
131 	}
132 	return (WALK_NEXT);
133 }
134 
135 static int
136 arp_stacks_walk_step(mdb_walk_state_t *wsp)
137 {
138 	uintptr_t addr;
139 	netstack_t nss;
140 
141 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
142 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
143 		return (WALK_ERR);
144 	}
145 	addr = (uintptr_t)nss.netstack_modules[NS_ARP];
146 
147 	return (wsp->walk_callback(addr, wsp->walk_layer, wsp->walk_cbdata));
148 }
149 
150 static int
151 arl_stack_walk_init(mdb_walk_state_t *wsp)
152 {
153 	uintptr_t addr;
154 
155 	if (wsp->walk_addr == NULL) {
156 		mdb_warn("arl_stack supports only local walks\n");
157 		return (WALK_ERR);
158 	}
159 
160 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_arl_head);
161 	if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
162 	    addr) == -1) {
163 		mdb_warn("failed to read 'arl_g_head'");
164 		return (WALK_ERR);
165 	}
166 	return (WALK_NEXT);
167 }
168 
169 static int
170 arl_stack_walk_step(mdb_walk_state_t *wsp)
171 {
172 	uintptr_t addr = wsp->walk_addr;
173 	arl_t arl;
174 
175 	if (wsp->walk_addr == NULL)
176 		return (WALK_DONE);
177 
178 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
179 		mdb_warn("failed to read arl_t at %p", addr);
180 		return (WALK_ERR);
181 	}
182 
183 	wsp->walk_addr = (uintptr_t)arl.arl_next;
184 
185 	return ((*wsp->walk_callback)(addr, &arl, wsp->walk_cbdata));
186 }
187 
188 static int
189 arl_walk_init(mdb_walk_state_t *wsp)
190 {
191 	if (mdb_layered_walk("arp_stacks", wsp) == -1) {
192 		mdb_warn("can't walk 'arp_stacks'");
193 		return (WALK_ERR);
194 	}
195 
196 	return (WALK_NEXT);
197 }
198 
199 static int
200 arl_walk_step(mdb_walk_state_t *wsp)
201 {
202 	if (mdb_pwalk("arl_stack", wsp->walk_callback,
203 		wsp->walk_cbdata, wsp->walk_addr) == -1) {
204 		mdb_warn("couldn't walk 'arl_stack' at %p", wsp->walk_addr);
205 		return (WALK_ERR);
206 	}
207 	return (WALK_NEXT);
208 }
209 
210 /*
211  * Called with walk_addr being the address of arp_stack_t
212  */
213 static int
214 ace_stack_walk_init(mdb_walk_state_t *wsp)
215 {
216 	ace_walk_data_t *aw;
217 	uintptr_t addr;
218 
219 	if (wsp->walk_addr == NULL) {
220 		mdb_warn("ace_stack supports only local walks\n");
221 		return (WALK_ERR);
222 	}
223 
224 	aw = mdb_alloc(sizeof (ace_walk_data_t), UM_SLEEP);
225 
226 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_ce_hash_tbl);
227 	if (mdb_vread(aw->awd_hash_tbl, sizeof (aw->awd_hash_tbl),
228 	    addr) == -1) {
229 		mdb_warn("failed to read 'as_ce_hash_tbl'");
230 		mdb_free(aw, sizeof (ace_walk_data_t));
231 		return (WALK_ERR);
232 	}
233 
234 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_ce_mask_entries);
235 	if (mdb_vread(&aw->awd_masks, sizeof (aw->awd_masks),
236 	    addr) == -1) {
237 		mdb_warn("failed to read 'as_ce_mask_entries'");
238 		mdb_free(aw, sizeof (ace_walk_data_t));
239 		return (WALK_ERR);
240 	}
241 
242 	/* The step routine will start off by incrementing to index 0 */
243 	aw->awd_idx = -1;
244 	wsp->walk_addr = 0;
245 	wsp->walk_data = aw;
246 
247 	return (WALK_NEXT);
248 }
249 
250 static int
251 ace_stack_walk_step(mdb_walk_state_t *wsp)
252 {
253 	uintptr_t addr;
254 	ace_walk_data_t *aw = wsp->walk_data;
255 	ace_t ace;
256 
257 	/*
258 	 * If we're at the end of the previous list, then find the start of the
259 	 * next list to process.
260 	 */
261 	while (wsp->walk_addr == NULL) {
262 		if (aw->awd_idx == ARP_HASH_SIZE)
263 			return (WALK_DONE);
264 		if (++aw->awd_idx == ARP_HASH_SIZE) {
265 			wsp->walk_addr = (uintptr_t)aw->awd_masks;
266 		} else {
267 			wsp->walk_addr =
268 			    (uintptr_t)aw->awd_hash_tbl[aw->awd_idx];
269 		}
270 	}
271 
272 	addr = wsp->walk_addr;
273 	if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
274 		mdb_warn("failed to read ace_t at %p", addr);
275 		return (WALK_ERR);
276 	}
277 
278 	wsp->walk_addr = (uintptr_t)ace.ace_next;
279 
280 	return (wsp->walk_callback(addr, &ace, wsp->walk_cbdata));
281 }
282 
283 static void
284 ace_stack_walk_fini(mdb_walk_state_t *wsp)
285 {
286 	mdb_free(wsp->walk_data, sizeof (ace_walk_data_t));
287 }
288 
289 static int
290 ace_walk_init(mdb_walk_state_t *wsp)
291 {
292 	if (mdb_layered_walk("arp_stacks", wsp) == -1) {
293 		mdb_warn("can't walk 'arp_stacks'");
294 		return (WALK_ERR);
295 	}
296 
297 	return (WALK_NEXT);
298 }
299 
300 static int
301 ace_walk_step(mdb_walk_state_t *wsp)
302 {
303 	if (mdb_pwalk("ace_stack", wsp->walk_callback,
304 		wsp->walk_cbdata, wsp->walk_addr) == -1) {
305 		mdb_warn("couldn't walk 'ace_stack' at %p", wsp->walk_addr);
306 		return (WALK_ERR);
307 	}
308 	return (WALK_NEXT);
309 }
310 
311 
312 /* Common routine to produce an 'ar' text description */
313 static void
314 ar_describe(const ar_t *ar, char *buf, size_t nbytes, boolean_t addmac)
315 {
316 	if (ar->ar_arl == NULL) {
317 		queue_t wq, ipq;
318 		ill_t ill;
319 		char name[LIFNAMSIZ];
320 		GElf_Sym sym;
321 		boolean_t nextip;
322 
323 		if (mdb_vread(&wq, sizeof (wq), (uintptr_t)ar->ar_wq) == -1 ||
324 		    mdb_vread(&ipq, sizeof (ipq), (uintptr_t)wq.q_next) == -1)
325 			return;
326 
327 		nextip =
328 		    (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0 &&
329 		    (uintptr_t)sym.st_value == (uintptr_t)ipq.q_qinfo);
330 
331 		if (!ar->ar_on_ill_stream) {
332 			(void) strcpy(buf, nextip ? "Client" : "Unknown");
333 			return;
334 		}
335 
336 		if (!nextip ||
337 		    mdb_vread(&ill, sizeof (ill), (uintptr_t)ipq.q_ptr) == -1 ||
338 		    mdb_readstr(name, sizeof (name),
339 		    (uintptr_t)ill.ill_name) == -1) {
340 			return;
341 		}
342 		(void) mdb_snprintf(buf, nbytes, "IP %s", name);
343 	} else {
344 		arl_t arl;
345 		arlphy_t ap;
346 		ssize_t retv;
347 		uint32_t alen;
348 		uchar_t macaddr[ARP_MAX_ADDR_LEN];
349 
350 		if (mdb_vread(&arl, sizeof (arl), (uintptr_t)ar->ar_arl) == -1)
351 			return;
352 		retv = mdb_snprintf(buf, nbytes, "ARP %s ", arl.arl_name);
353 		if (retv >= nbytes || !addmac)
354 			return;
355 		if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl.arl_phy) == -1)
356 			return;
357 		alen = ap.ap_hw_addrlen;
358 		if (ap.ap_hw_addr == NULL || alen == 0 ||
359 		    alen > sizeof (macaddr))
360 			return;
361 		if (mdb_vread(macaddr, alen, (uintptr_t)ap.ap_hw_addr) == -1)
362 			return;
363 		mdb_mac_addr(macaddr, alen, buf + retv, nbytes - retv);
364 	}
365 }
366 
367 /* ARGSUSED2 */
368 static int
369 ar_cb(uintptr_t addr, const void *arptr, void *dummy)
370 {
371 	const ar_t *ar = arptr;
372 	char ardesc[sizeof ("ARP  ") + LIFNAMSIZ];
373 
374 	ar_describe(ar, ardesc, sizeof (ardesc), B_FALSE);
375 	mdb_printf("%?p %?p %?p %s\n", addr, ar->ar_wq, ar->ar_arl, ardesc);
376 	return (WALK_NEXT);
377 }
378 
379 /*
380  * Print out ARP client structures.
381  */
382 /* ARGSUSED2 */
383 static int
384 ar_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
385 {
386 	ar_t ar;
387 
388 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
389 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
390 		    "AR", "WQ", "ARL", "TYPE");
391 	}
392 
393 	if (flags & DCMD_ADDRSPEC) {
394 		if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
395 			mdb_warn("failed to read ar_t at %p", addr);
396 			return (DCMD_ERR);
397 		}
398 		(void) ar_cb(addr, &ar, NULL);
399 	} else {
400 		if (mdb_walk("ar", ar_cb, NULL) == -1) {
401 			mdb_warn("cannot walk ar_t structures");
402 			return (DCMD_ERR);
403 		}
404 	}
405 	return (DCMD_OK);
406 }
407 
408 /* ARGSUSED2 */
409 static int
410 arl_cb(uintptr_t addr, const void *arlptr, void *dummy)
411 {
412 	const arl_t *arl = arlptr;
413 	arlphy_t ap;
414 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
415 	char macstr[ARP_MAX_ADDR_LEN*3];
416 	char flags[4];
417 	const char *primstr;
418 
419 	mdb_printf("%?p  ", addr);
420 	if (arl->arl_dlpi_pending == DL_PRIM_INVAL)
421 		mdb_printf("%16s", "--");
422 	else if ((primstr = mdb_dlpi_prim(arl->arl_dlpi_pending)) != NULL)
423 		mdb_printf("%16s", primstr);
424 	else
425 		mdb_printf("%16x", arl->arl_dlpi_pending);
426 
427 	if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl->arl_phy) == -1 ||
428 	    ap.ap_hw_addrlen == 0 || ap.ap_hw_addrlen > sizeof (macaddr)) {
429 		(void) strcpy(macstr, "--");
430 	} else if (mdb_vread(macaddr, ap.ap_hw_addrlen,
431 	    (uintptr_t)ap.ap_hw_addr) == -1) {
432 		(void) strcpy(macstr, "?");
433 	} else {
434 		mdb_mac_addr(macaddr, ap.ap_hw_addrlen, macstr,
435 		    sizeof (macstr));
436 	}
437 
438 	/* Print both the link-layer state and the NOARP flag */
439 	flags[0] = '\0';
440 	if (arl->arl_flags & ARL_F_NOARP)
441 		(void) strcat(flags, "N");
442 	switch (arl->arl_state) {
443 	case ARL_S_DOWN:
444 		(void) strcat(flags, "d");
445 		break;
446 	case ARL_S_PENDING:
447 		(void) strcat(flags, "P");
448 		break;
449 	case ARL_S_UP:
450 		(void) strcat(flags, "U");
451 		break;
452 	default:
453 		(void) strcat(flags, "?");
454 		break;
455 	}
456 	mdb_printf("  %8d  %-3s  %-9s  %-17s %5d\n",
457 	    mdb_mblk_count(arl->arl_dlpi_deferred), flags, arl->arl_name,
458 	    macstr, arl_to_stackid((uintptr_t)addr));
459 	return (WALK_NEXT);
460 }
461 
462 /*
463  * Print out ARP link-layer elements.
464  */
465 /* ARGSUSED2 */
466 static int
467 arl_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
468 {
469 	arl_t arl;
470 
471 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
472 		mdb_printf("%<u>%?s  %16s  %8s  %3s  %9s  %-17s %5s%</u>\n",
473 		    "ARL", "DLPI REQ", "DLPI CNT", "FLG", "INTERFACE",
474 		    "HWADDR", "STACK");
475 	}
476 
477 	if (flags & DCMD_ADDRSPEC) {
478 		if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
479 			mdb_warn("failed to read arl_t at %p", addr);
480 			return (DCMD_ERR);
481 		}
482 		(void) arl_cb(addr, &arl, NULL);
483 	} else {
484 		if (mdb_walk("arl", arl_cb, NULL) == -1) {
485 			mdb_warn("cannot walk arl_t structures");
486 			return (DCMD_ERR);
487 		}
488 	}
489 	return (DCMD_OK);
490 }
491 
492 /* ARGSUSED2 */
493 static int
494 ace_cb(uintptr_t addr, const void *aceptr, void *dummy)
495 {
496 	const ace_t *ace = aceptr;
497 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
498 	char macstr[ARP_MAX_ADDR_LEN*3];
499 	/* The %b format isn't compact enough for long listings */
500 	static const char ace_flags[] = "SPDRMLdA ofya";
501 	const char *cp;
502 	char flags[sizeof (ace_flags)], *fp;
503 	int flg;
504 	in_addr_t inaddr, mask;
505 	char addrstr[sizeof ("255.255.255.255/32")];
506 
507 	/* Walk the list of flags and produce a string */
508 	cp = ace_flags;
509 	fp = flags;
510 	for (flg = 1; *cp != '\0'; flg <<= 1, cp++) {
511 		if ((flg & ace->ace_flags) && *cp != ' ')
512 			*fp++ = *cp;
513 	}
514 	*fp = '\0';
515 
516 	/* If it's not resolved, then it has no hardware address */
517 	if (!(ace->ace_flags & ACE_F_RESOLVED) ||
518 	    ace->ace_hw_addr_length == 0 ||
519 	    ace->ace_hw_addr_length > sizeof (macaddr)) {
520 		(void) strcpy(macstr, "--");
521 	} else if (mdb_vread(macaddr, ace->ace_hw_addr_length,
522 	    (uintptr_t)ace->ace_hw_addr) == -1) {
523 		(void) strcpy(macstr, "?");
524 	} else {
525 		mdb_mac_addr(macaddr, ace->ace_hw_addr_length, macstr,
526 		    sizeof (macstr));
527 	}
528 
529 	/*
530 	 * Nothing other than IP uses ARP these days, so we don't try very hard
531 	 * here to switch out on ARP protocol type.  (Note that ARP protocol
532 	 * types are roughly Ethertypes, but are allocated separately at IANA.)
533 	 */
534 	if (ace->ace_proto != IP_ARP_PROTO_TYPE) {
535 		(void) mdb_snprintf(addrstr, sizeof (addrstr),
536 		    "Unknown proto %x", ace->ace_proto);
537 	} else if (mdb_vread(&inaddr, sizeof (inaddr),
538 	    (uintptr_t)ace->ace_proto_addr) != -1 &&
539 	    mdb_vread(&mask, sizeof (mask), (uintptr_t)ace->ace_proto_mask) !=
540 	    -1) {
541 		/*
542 		 * If it's the standard host mask, then print it normally.
543 		 * Otherwise, use "/n" notation.
544 		 */
545 		if (mask == (in_addr_t)~0) {
546 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I",
547 			    inaddr);
548 		} else {
549 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I/%d",
550 			    inaddr, mask == 0 ? 0 : 33 - mdb_ffs(mask));
551 		}
552 	} else {
553 		(void) strcpy(addrstr, "?");
554 	}
555 	mdb_printf("%?p  %-18s  %-8s  %-17s %5d\n", addr, addrstr, flags,
556 	    macstr, arl_to_stackid((uintptr_t)ace->ace_arl));
557 	return (WALK_NEXT);
558 }
559 
560 /*
561  * Print out ARP cache entry (ace_t) elements.
562  */
563 /* ARGSUSED2 */
564 static int
565 ace_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
566 {
567 	ace_t ace;
568 
569 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
570 		mdb_printf("%<u>%?s  %-18s  %-8s  %-17s %5s%</u>\n",
571 		    "ACE", "PROTOADDR", "FLAGS", "HWADDR", "STACK");
572 	}
573 
574 	if (flags & DCMD_ADDRSPEC) {
575 		if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
576 			mdb_warn("failed to read ace_t at %p", addr);
577 			return (DCMD_ERR);
578 		}
579 		(void) ace_cb(addr, &ace, NULL);
580 	} else {
581 		if (mdb_walk("ace", ace_cb, NULL) == -1) {
582 			mdb_warn("cannot walk ace_t structures");
583 			return (DCMD_ERR);
584 		}
585 	}
586 	return (DCMD_OK);
587 }
588 
589 /*
590  * Print an ARP hardware and protocol address pair; used when printing an ARP
591  * message.
592  */
593 static void
594 print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
595 {
596 	char macstr[ARP_MAX_ADDR_LEN*3];
597 	in_addr_t inaddr;
598 
599 	if (arh->arh_hlen == 0)
600 		(void) strcpy(macstr, "(none)");
601 	else
602 		mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
603 	mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
604 	if (arh->arh_plen == 0) {
605 		mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
606 	} else if (ptype == IP_ARP_PROTO_TYPE) {
607 		mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
608 	} else if (arh->arh_plen == sizeof (in_addr_t)) {
609 		(void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
610 		mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
611 	} else {
612 		mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
613 	}
614 }
615 
616 /*
617  * Decode an ARP message and display it.
618  */
619 /* ARGSUSED2 */
620 static int
621 arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
622 {
623 	struct {
624 		arh_t arh;
625 		uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
626 	} arp;
627 	size_t blen;
628 	uint16_t htype, ptype, op;
629 	const char *cp;
630 
631 	if (!(flags & DCMD_ADDRSPEC)) {
632 		mdb_warn("address required to print ARP header\n");
633 		return (DCMD_ERR);
634 	}
635 
636 	if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
637 		mdb_warn("unable to read ARP header at %p", addr);
638 		return (DCMD_ERR);
639 	}
640 	mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
641 	mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
642 	mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));
643 
644 	switch (htype) {
645 	case ARPHRD_ETHER:
646 		cp = "Ether";
647 		break;
648 	case ARPHRD_IEEE802:
649 		cp = "IEEE802";
650 		break;
651 	case ARPHRD_IB:
652 		cp = "InfiniBand";
653 		break;
654 	default:
655 		cp = "Unknown";
656 		break;
657 	}
658 	mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
659 	mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
660 	    ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");
661 
662 	switch (op) {
663 	case ARPOP_REQUEST:
664 		cp = "ares_op$REQUEST";
665 		break;
666 	case ARPOP_REPLY:
667 		cp = "ares_op$REPLY";
668 		break;
669 	case REVARP_REQUEST:
670 		cp = "arev_op$REQUEST";
671 		break;
672 	case REVARP_REPLY:
673 		cp = "arev_op$REPLY";
674 		break;
675 	default:
676 		cp = "Unknown";
677 		break;
678 	}
679 	mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);
680 
681 	/*
682 	 * Note that we go to some length to attempt to print out the fixed
683 	 * header data before trying to decode the variable-length data.  This
684 	 * is done to maximize the amount of useful information shown when the
685 	 * buffer is truncated or otherwise corrupt.
686 	 */
687 	blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
688 	if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
689 		mdb_warn("unable to read ARP body at %p", addr);
690 		return (DCMD_ERR);
691 	}
692 
693 	print_arp('s', arp.addrs, &arp.arh, ptype);
694 	print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
695 	    &arp.arh, ptype);
696 	return (DCMD_OK);
697 }
698 
699 /*
700  * Print out an arp command formatted in a reasonable manner.  This implements
701  * the type switch used by ARP.
702  *
703  * It could also dump the data that follows the header (using offset and length
704  * in the various structures), but it currently does not.
705  */
706 /* ARGSUSED2 */
707 static int
708 arpcmd_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
709 {
710 	arc_t arc;
711 	const arp_cmd_tbl *tp;
712 	mdb_arg_t subargv;
713 
714 	if (!(flags & DCMD_ADDRSPEC)) {
715 		mdb_warn("address required to print ARP command\n");
716 		return (DCMD_ERR);
717 	}
718 	if (mdb_vread(&arc, sizeof (arc), addr) == -1) {
719 		mdb_warn("unable to read arc_t at %p", addr);
720 		return (DCMD_ERR);
721 	}
722 	for (tp = act_list; tp->act_cmd != 0; tp++)
723 		if (tp->act_cmd == arc.arc_cmd)
724 			break;
725 	mdb_printf("%p %s (%s) = ", addr, tp->act_name, tp->act_type);
726 	subargv.a_type = MDB_TYPE_STRING;
727 	subargv.a_un.a_str = tp->act_type;
728 	if (mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, &subargv) == -1)
729 		return (DCMD_ERR);
730 	else
731 		return (DCMD_OK);
732 }
733 
734 static size_t
735 mi_osize(const queue_t *q)
736 {
737 	/*
738 	 * The code in common/inet/mi.c allocates an extra word to store the
739 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
740 	 */
741 	struct mi_block {
742 		size_t mi_nbytes;
743 		struct mi_o_s mi_o;
744 	} m;
745 
746 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr - sizeof (m)) != -1)
747 		return (m.mi_nbytes - sizeof (m));
748 
749 	return (0);
750 }
751 
752 /*
753  * This is called when ::stream is used and an ARP module is seen on the
754  * stream.  Determine what sort of ARP usage is involved and show an
755  * appropriate message.
756  */
757 static void
758 arp_qinfo(const queue_t *qp, char *buf, size_t nbytes)
759 {
760 	size_t size = mi_osize(qp);
761 	ar_t ar;
762 
763 	if (size != sizeof (ar_t))
764 		return;
765 	if (mdb_vread(&ar, sizeof (ar), (uintptr_t)qp->q_ptr) == -1)
766 		return;
767 	ar_describe(&ar, buf, nbytes, B_TRUE);
768 }
769 
770 static uintptr_t
771 arp_rnext(const queue_t *q)
772 {
773 	size_t size = mi_osize(q);
774 	ar_t ar;
775 
776 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
777 	    (uintptr_t)q->q_ptr) != -1)
778 		return ((uintptr_t)ar.ar_rq);
779 
780 	return (NULL);
781 }
782 
783 static uintptr_t
784 arp_wnext(const queue_t *q)
785 {
786 	size_t size = mi_osize(q);
787 	ar_t ar;
788 
789 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
790 	    (uintptr_t)q->q_ptr) != -1)
791 		return ((uintptr_t)ar.ar_wq);
792 
793 	return (NULL);
794 }
795 
796 static const mdb_dcmd_t dcmds[] = {
797 	{ "ar", "?", "display ARP client streams for all stacks",
798 	    ar_cmd, NULL },
799 	{ "arl", "?", "display ARP link layers for all stacks", arl_cmd, NULL },
800 	{ "ace", "?", "display ARP cache entries for all stacks",
801 	    ace_cmd, NULL },
802 	{ "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
803 	{ "arpcmd", ":", "display an ARP command", arpcmd_cmd, NULL },
804 	{ NULL }
805 };
806 
807 /* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
808 static const mdb_walker_t walkers[] = {
809 	{ "arl", "walk list of arl_t links for all stacks",
810 	    arl_walk_init, arl_walk_step, NULL },
811 	{ "arl_stack", "walk list of arl_t links",
812 	    arl_stack_walk_init, arl_stack_walk_step, NULL },
813 	{ "ace", "walk list of ace_t entries for all stacks",
814 	    ace_walk_init, ace_walk_step, NULL },
815 	{ "ace_stack", "walk list of ace_t entries",
816 	    ace_stack_walk_init, ace_stack_walk_step, ace_stack_walk_fini },
817 	{ "arp_stacks", "walk all the arp_stack_t",
818 	    arp_stacks_walk_init, arp_stacks_walk_step, NULL },
819 	{ NULL }
820 };
821 
822 static const mdb_qops_t arp_qops = { arp_qinfo, arp_rnext, arp_wnext };
823 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
824 
825 const mdb_modinfo_t *
826 _mdb_init(void)
827 {
828 	GElf_Sym sym;
829 
830 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
831 		mdb_qops_install(&arp_qops, (uintptr_t)sym.st_value);
832 
833 	return (&modinfo);
834 }
835 
836 void
837 _mdb_fini(void)
838 {
839 	GElf_Sym sym;
840 
841 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
842 		mdb_qops_remove(&arp_qops, (uintptr_t)sym.st_value);
843 }
844