xref: /onnv-gate/usr/src/cmd/mdb/common/modules/arp/arp.c (revision 3865:6dfee3b4c9ff)
12546Scarlsonj /*
22546Scarlsonj  * CDDL HEADER START
32546Scarlsonj  *
42546Scarlsonj  * The contents of this file are subject to the terms of the
52546Scarlsonj  * Common Development and Distribution License (the "License").
62546Scarlsonj  * You may not use this file except in compliance with the License.
72546Scarlsonj  *
82546Scarlsonj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92546Scarlsonj  * or http://www.opensolaris.org/os/licensing.
102546Scarlsonj  * See the License for the specific language governing permissions
112546Scarlsonj  * and limitations under the License.
122546Scarlsonj  *
132546Scarlsonj  * When distributing Covered Code, include this CDDL HEADER in each
142546Scarlsonj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152546Scarlsonj  * If applicable, add the following below this CDDL HEADER, with the
162546Scarlsonj  * fields enclosed by brackets "[]" replaced with your own identifying
172546Scarlsonj  * information: Portions Copyright [yyyy] [name of copyright owner]
182546Scarlsonj  *
192546Scarlsonj  * CDDL HEADER END
202546Scarlsonj  */
212546Scarlsonj /*
22*3865Szf203873  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
232546Scarlsonj  * Use is subject to license terms.
242546Scarlsonj  */
252546Scarlsonj 
262546Scarlsonj #pragma ident	"%Z%%M%	%I%	%E% SMI"
272546Scarlsonj 
282546Scarlsonj #include <stdio.h>
292546Scarlsonj #include <sys/types.h>
302546Scarlsonj #include <sys/stropts.h>
312546Scarlsonj #include <sys/stream.h>
322546Scarlsonj #include <sys/dlpi.h>
332958Sdr146992 #include <sys/hook.h>
342958Sdr146992 #include <sys/hook_event.h>
352546Scarlsonj #include <inet/led.h>
362546Scarlsonj #include <inet/common.h>
372546Scarlsonj #include <inet/mi.h>
382546Scarlsonj #include <inet/arp.h>
392546Scarlsonj #include <inet/arp_impl.h>
402546Scarlsonj #include <inet/ip.h>
412546Scarlsonj #include <netinet/arp.h>
422546Scarlsonj 
432546Scarlsonj #include <mdb/mdb_modapi.h>
442546Scarlsonj #include <mdb/mdb_ks.h>
452546Scarlsonj 
462546Scarlsonj typedef struct {
472546Scarlsonj 	uint32_t	act_cmd;
482546Scarlsonj 	char		*act_name;
492546Scarlsonj 	char		*act_type;
502546Scarlsonj } arp_cmd_tbl;
512546Scarlsonj 
522546Scarlsonj /*
532546Scarlsonj  * Table of ARP commands and structure types used for messages between ARP and
542546Scarlsonj  * IP.
552546Scarlsonj  */
562546Scarlsonj static const arp_cmd_tbl act_list[] = {
572546Scarlsonj 	{ AR_ENTRY_ADD,		"AR_ENTRY_ADD",		"arp`area_t" },
582546Scarlsonj 	{ AR_ENTRY_DELETE,	"AR_ENTRY_DELETE",	"arp`ared_t" },
592546Scarlsonj 	{ AR_ENTRY_QUERY,	"AR_ENTRY_QUERY",	"arp`areq_t" },
602546Scarlsonj 	{ AR_ENTRY_SQUERY,	"AR_ENTRY_SQUERY",	"arp`area_t" },
612546Scarlsonj 	{ AR_MAPPING_ADD,	"AR_MAPPING_ADD",	"arp`arma_t" },
622546Scarlsonj 	{ AR_CLIENT_NOTIFY,	"AR_CLIENT_NOTIFY",	"arp`arcn_t" },
632546Scarlsonj 	{ AR_INTERFACE_UP,	"AR_INTERFACE_UP",	"arp`arc_t" },
642546Scarlsonj 	{ AR_INTERFACE_DOWN,	"AR_INTERFACE_DOWN",	"arp`arc_t" },
652546Scarlsonj 	{ AR_INTERFACE_ON,	"AR_INTERFACE_ON",	"arp`arc_t" },
662546Scarlsonj 	{ AR_INTERFACE_OFF,	"AR_INTERFACE_OFF",	"arp`arc_t" },
672546Scarlsonj 	{ AR_DLPIOP_DONE,	"AR_DLPIOP_DONE",	"arp`arc_t" },
682546Scarlsonj 	{ AR_ARP_CLOSING,	"AR_ARP_CLOSING",	"arp`arc_t" },
692546Scarlsonj 	{ AR_ARP_EXTEND,	"AR_ARP_EXTEND",	"arp`arc_t" },
702546Scarlsonj 	{ 0,			"unknown command",	"arp`arc_t" }
712546Scarlsonj };
722546Scarlsonj 
732546Scarlsonj /*
742546Scarlsonj  * State information kept during walk over ACE hash table and unhashed mask
752546Scarlsonj  * list.
762546Scarlsonj  */
772546Scarlsonj typedef struct ace_walk_data {
782546Scarlsonj 	ace_t *awd_hash_tbl[ARP_HASH_SIZE];
792546Scarlsonj 	ace_t *awd_masks;
802546Scarlsonj 	int awd_idx;
812546Scarlsonj } ace_walk_data_t;
822546Scarlsonj 
83*3865Szf203873 /*
84*3865Szf203873  * Given the kernel address of an arl_t, return the stackid
85*3865Szf203873  */
862546Scarlsonj static int
87*3865Szf203873 arl_to_stackid(uintptr_t addr)
882546Scarlsonj {
89*3865Szf203873 	arl_t arl;
90*3865Szf203873 	queue_t rq;
91*3865Szf203873 	ar_t ar;
92*3865Szf203873 	arp_stack_t ass;
93*3865Szf203873 	netstack_t nss;
94*3865Szf203873 
95*3865Szf203873 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
96*3865Szf203873 		mdb_warn("failed to read arl_t %p", addr);
97*3865Szf203873 		return (0);
98*3865Szf203873 	}
99*3865Szf203873 
100*3865Szf203873 	addr = (uintptr_t)arl.arl_rq;
101*3865Szf203873 	if (mdb_vread(&rq, sizeof (rq), addr) == -1) {
102*3865Szf203873 		mdb_warn("failed to read queue_t %p", addr);
103*3865Szf203873 		return (0);
104*3865Szf203873 	}
105*3865Szf203873 
106*3865Szf203873 	addr = (uintptr_t)rq.q_ptr;
107*3865Szf203873 	if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
108*3865Szf203873 		mdb_warn("failed to read ar_t %p", addr);
109*3865Szf203873 		return (0);
110*3865Szf203873 	}
111*3865Szf203873 
112*3865Szf203873 	addr = (uintptr_t)ar.ar_as;
113*3865Szf203873 	if (mdb_vread(&ass, sizeof (ass), addr) == -1) {
114*3865Szf203873 		mdb_warn("failed to read arp_stack_t %p", addr);
115*3865Szf203873 		return (0);
116*3865Szf203873 	}
117*3865Szf203873 	addr = (uintptr_t)ass.as_netstack;
118*3865Szf203873 	if (mdb_vread(&nss, sizeof (nss), addr) == -1) {
119*3865Szf203873 		mdb_warn("failed to read netstack_t %p", addr);
120*3865Szf203873 		return (0);
121*3865Szf203873 	}
122*3865Szf203873 	return (nss.netstack_stackid);
123*3865Szf203873 }
124*3865Szf203873 
125*3865Szf203873 static int
126*3865Szf203873 arp_stacks_walk_init(mdb_walk_state_t *wsp)
127*3865Szf203873 {
128*3865Szf203873 	if (mdb_layered_walk("netstack", wsp) == -1) {
129*3865Szf203873 		mdb_warn("can't walk 'netstack'");
130*3865Szf203873 		return (WALK_ERR);
131*3865Szf203873 	}
132*3865Szf203873 	return (WALK_NEXT);
133*3865Szf203873 }
134*3865Szf203873 
135*3865Szf203873 static int
136*3865Szf203873 arp_stacks_walk_step(mdb_walk_state_t *wsp)
137*3865Szf203873 {
138*3865Szf203873 	uintptr_t addr;
139*3865Szf203873 	netstack_t nss;
140*3865Szf203873 
141*3865Szf203873 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
142*3865Szf203873 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
143*3865Szf203873 		return (WALK_ERR);
144*3865Szf203873 	}
145*3865Szf203873 	addr = (uintptr_t)nss.netstack_modules[NS_ARP];
146*3865Szf203873 
147*3865Szf203873 	return (wsp->walk_callback(addr, wsp->walk_layer, wsp->walk_cbdata));
148*3865Szf203873 }
149*3865Szf203873 
150*3865Szf203873 static int
151*3865Szf203873 arl_stack_walk_init(mdb_walk_state_t *wsp)
152*3865Szf203873 {
153*3865Szf203873 	uintptr_t addr;
154*3865Szf203873 
155*3865Szf203873 	if (wsp->walk_addr == NULL) {
156*3865Szf203873 		mdb_warn("arl_stack supports only local walks\n");
157*3865Szf203873 		return (WALK_ERR);
158*3865Szf203873 	}
159*3865Szf203873 
160*3865Szf203873 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_arl_head);
161*3865Szf203873 	if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
162*3865Szf203873 	    addr) == -1) {
1632546Scarlsonj 		mdb_warn("failed to read 'arl_g_head'");
1642546Scarlsonj 		return (WALK_ERR);
1652546Scarlsonj 	}
1662546Scarlsonj 	return (WALK_NEXT);
1672546Scarlsonj }
1682546Scarlsonj 
1692546Scarlsonj static int
170*3865Szf203873 arl_stack_walk_step(mdb_walk_state_t *wsp)
1712546Scarlsonj {
1722546Scarlsonj 	uintptr_t addr = wsp->walk_addr;
1732546Scarlsonj 	arl_t arl;
1742546Scarlsonj 
1752546Scarlsonj 	if (wsp->walk_addr == NULL)
1762546Scarlsonj 		return (WALK_DONE);
1772546Scarlsonj 
1782546Scarlsonj 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
1792546Scarlsonj 		mdb_warn("failed to read arl_t at %p", addr);
1802546Scarlsonj 		return (WALK_ERR);
1812546Scarlsonj 	}
1822546Scarlsonj 
1832546Scarlsonj 	wsp->walk_addr = (uintptr_t)arl.arl_next;
1842546Scarlsonj 
1852546Scarlsonj 	return ((*wsp->walk_callback)(addr, &arl, wsp->walk_cbdata));
1862546Scarlsonj }
1872546Scarlsonj 
1882546Scarlsonj static int
189*3865Szf203873 arl_walk_init(mdb_walk_state_t *wsp)
190*3865Szf203873 {
191*3865Szf203873 	if (mdb_layered_walk("arp_stacks", wsp) == -1) {
192*3865Szf203873 		mdb_warn("can't walk 'arp_stacks'");
193*3865Szf203873 		return (WALK_ERR);
194*3865Szf203873 	}
195*3865Szf203873 
196*3865Szf203873 	return (WALK_NEXT);
197*3865Szf203873 }
198*3865Szf203873 
199*3865Szf203873 static int
200*3865Szf203873 arl_walk_step(mdb_walk_state_t *wsp)
201*3865Szf203873 {
202*3865Szf203873 	if (mdb_pwalk("arl_stack", wsp->walk_callback,
203*3865Szf203873 		wsp->walk_cbdata, wsp->walk_addr) == -1) {
204*3865Szf203873 		mdb_warn("couldn't walk 'arl_stack' at %p", wsp->walk_addr);
205*3865Szf203873 		return (WALK_ERR);
206*3865Szf203873 	}
207*3865Szf203873 	return (WALK_NEXT);
208*3865Szf203873 }
209*3865Szf203873 
210*3865Szf203873 /*
211*3865Szf203873  * Called with walk_addr being the address of arp_stack_t
212*3865Szf203873  */
213*3865Szf203873 static int
214*3865Szf203873 ace_stack_walk_init(mdb_walk_state_t *wsp)
2152546Scarlsonj {
2162546Scarlsonj 	ace_walk_data_t *aw;
217*3865Szf203873 	uintptr_t addr;
2182546Scarlsonj 
219*3865Szf203873 	if (wsp->walk_addr == NULL) {
220*3865Szf203873 		mdb_warn("ace_stack supports only local walks\n");
2212546Scarlsonj 		return (WALK_ERR);
2222546Scarlsonj 	}
2232546Scarlsonj 
2242546Scarlsonj 	aw = mdb_alloc(sizeof (ace_walk_data_t), UM_SLEEP);
2252546Scarlsonj 
226*3865Szf203873 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_ce_hash_tbl);
227*3865Szf203873 	if (mdb_vread(aw->awd_hash_tbl, sizeof (aw->awd_hash_tbl),
228*3865Szf203873 	    addr) == -1) {
229*3865Szf203873 		mdb_warn("failed to read 'as_ce_hash_tbl'");
2302546Scarlsonj 		mdb_free(aw, sizeof (ace_walk_data_t));
2312546Scarlsonj 		return (WALK_ERR);
2322546Scarlsonj 	}
2332546Scarlsonj 
234*3865Szf203873 	addr = wsp->walk_addr + OFFSETOF(arp_stack_t, as_ce_mask_entries);
235*3865Szf203873 	if (mdb_vread(&aw->awd_masks, sizeof (aw->awd_masks),
236*3865Szf203873 	    addr) == -1) {
237*3865Szf203873 		mdb_warn("failed to read 'as_ce_mask_entries'");
2382546Scarlsonj 		mdb_free(aw, sizeof (ace_walk_data_t));
2392546Scarlsonj 		return (WALK_ERR);
2402546Scarlsonj 	}
2412546Scarlsonj 
2422546Scarlsonj 	/* The step routine will start off by incrementing to index 0 */
2432546Scarlsonj 	aw->awd_idx = -1;
2442546Scarlsonj 	wsp->walk_addr = 0;
2452546Scarlsonj 	wsp->walk_data = aw;
2462546Scarlsonj 
2472546Scarlsonj 	return (WALK_NEXT);
2482546Scarlsonj }
2492546Scarlsonj 
2502546Scarlsonj static int
251*3865Szf203873 ace_stack_walk_step(mdb_walk_state_t *wsp)
2522546Scarlsonj {
2532546Scarlsonj 	uintptr_t addr;
2542546Scarlsonj 	ace_walk_data_t *aw = wsp->walk_data;
2552546Scarlsonj 	ace_t ace;
2562546Scarlsonj 
2572546Scarlsonj 	/*
2582546Scarlsonj 	 * If we're at the end of the previous list, then find the start of the
2592546Scarlsonj 	 * next list to process.
2602546Scarlsonj 	 */
2612546Scarlsonj 	while (wsp->walk_addr == NULL) {
2622546Scarlsonj 		if (aw->awd_idx == ARP_HASH_SIZE)
2632546Scarlsonj 			return (WALK_DONE);
2642546Scarlsonj 		if (++aw->awd_idx == ARP_HASH_SIZE) {
2652546Scarlsonj 			wsp->walk_addr = (uintptr_t)aw->awd_masks;
2662546Scarlsonj 		} else {
2672546Scarlsonj 			wsp->walk_addr =
2682546Scarlsonj 			    (uintptr_t)aw->awd_hash_tbl[aw->awd_idx];
2692546Scarlsonj 		}
2702546Scarlsonj 	}
2712546Scarlsonj 
2722546Scarlsonj 	addr = wsp->walk_addr;
2732546Scarlsonj 	if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
2742546Scarlsonj 		mdb_warn("failed to read ace_t at %p", addr);
2752546Scarlsonj 		return (WALK_ERR);
2762546Scarlsonj 	}
2772546Scarlsonj 
2782546Scarlsonj 	wsp->walk_addr = (uintptr_t)ace.ace_next;
2792546Scarlsonj 
2802546Scarlsonj 	return (wsp->walk_callback(addr, &ace, wsp->walk_cbdata));
2812546Scarlsonj }
2822546Scarlsonj 
2832546Scarlsonj static void
284*3865Szf203873 ace_stack_walk_fini(mdb_walk_state_t *wsp)
2852546Scarlsonj {
2862546Scarlsonj 	mdb_free(wsp->walk_data, sizeof (ace_walk_data_t));
2872546Scarlsonj }
2882546Scarlsonj 
289*3865Szf203873 static int
290*3865Szf203873 ace_walk_init(mdb_walk_state_t *wsp)
291*3865Szf203873 {
292*3865Szf203873 	if (mdb_layered_walk("arp_stacks", wsp) == -1) {
293*3865Szf203873 		mdb_warn("can't walk 'arp_stacks'");
294*3865Szf203873 		return (WALK_ERR);
295*3865Szf203873 	}
296*3865Szf203873 
297*3865Szf203873 	return (WALK_NEXT);
298*3865Szf203873 }
299*3865Szf203873 
300*3865Szf203873 static int
301*3865Szf203873 ace_walk_step(mdb_walk_state_t *wsp)
302*3865Szf203873 {
303*3865Szf203873 	if (mdb_pwalk("ace_stack", wsp->walk_callback,
304*3865Szf203873 		wsp->walk_cbdata, wsp->walk_addr) == -1) {
305*3865Szf203873 		mdb_warn("couldn't walk 'ace_stack' at %p", wsp->walk_addr);
306*3865Szf203873 		return (WALK_ERR);
307*3865Szf203873 	}
308*3865Szf203873 	return (WALK_NEXT);
309*3865Szf203873 }
310*3865Szf203873 
311*3865Szf203873 
3122546Scarlsonj /* Common routine to produce an 'ar' text description */
3132546Scarlsonj static void
3142546Scarlsonj ar_describe(const ar_t *ar, char *buf, size_t nbytes, boolean_t addmac)
3152546Scarlsonj {
3162546Scarlsonj 	if (ar->ar_arl == NULL) {
3172546Scarlsonj 		queue_t wq, ipq;
3182546Scarlsonj 		ill_t ill;
3192546Scarlsonj 		char name[LIFNAMSIZ];
3202546Scarlsonj 		GElf_Sym sym;
3212546Scarlsonj 		boolean_t nextip;
3222546Scarlsonj 
3232546Scarlsonj 		if (mdb_vread(&wq, sizeof (wq), (uintptr_t)ar->ar_wq) == -1 ||
3242546Scarlsonj 		    mdb_vread(&ipq, sizeof (ipq), (uintptr_t)wq.q_next) == -1)
3252546Scarlsonj 			return;
3262546Scarlsonj 
3272546Scarlsonj 		nextip =
3282546Scarlsonj 		    (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0 &&
3292546Scarlsonj 		    (uintptr_t)sym.st_value == (uintptr_t)ipq.q_qinfo);
3302546Scarlsonj 
3312546Scarlsonj 		if (!ar->ar_on_ill_stream) {
3322546Scarlsonj 			(void) strcpy(buf, nextip ? "Client" : "Unknown");
3332546Scarlsonj 			return;
3342546Scarlsonj 		}
3352546Scarlsonj 
3362546Scarlsonj 		if (!nextip ||
3372546Scarlsonj 		    mdb_vread(&ill, sizeof (ill), (uintptr_t)ipq.q_ptr) == -1 ||
3382546Scarlsonj 		    mdb_readstr(name, sizeof (name),
3392546Scarlsonj 		    (uintptr_t)ill.ill_name) == -1) {
3402546Scarlsonj 			return;
3412546Scarlsonj 		}
3422546Scarlsonj 		(void) mdb_snprintf(buf, nbytes, "IP %s", name);
3432546Scarlsonj 	} else {
3442546Scarlsonj 		arl_t arl;
3453065Smeem 		arlphy_t ap;
3462546Scarlsonj 		ssize_t retv;
3472546Scarlsonj 		uint32_t alen;
3482546Scarlsonj 		uchar_t macaddr[ARP_MAX_ADDR_LEN];
3492546Scarlsonj 
3502546Scarlsonj 		if (mdb_vread(&arl, sizeof (arl), (uintptr_t)ar->ar_arl) == -1)
3512546Scarlsonj 			return;
3522546Scarlsonj 		retv = mdb_snprintf(buf, nbytes, "ARP %s ", arl.arl_name);
3532546Scarlsonj 		if (retv >= nbytes || !addmac)
3542546Scarlsonj 			return;
3553065Smeem 		if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl.arl_phy) == -1)
3563065Smeem 			return;
3573065Smeem 		alen = ap.ap_hw_addrlen;
3583065Smeem 		if (ap.ap_hw_addr == NULL || alen == 0 ||
3592546Scarlsonj 		    alen > sizeof (macaddr))
3602546Scarlsonj 			return;
3613065Smeem 		if (mdb_vread(macaddr, alen, (uintptr_t)ap.ap_hw_addr) == -1)
3622546Scarlsonj 			return;
3632546Scarlsonj 		mdb_mac_addr(macaddr, alen, buf + retv, nbytes - retv);
3642546Scarlsonj 	}
3652546Scarlsonj }
3662546Scarlsonj 
3672546Scarlsonj /* ARGSUSED2 */
3682546Scarlsonj static int
3692546Scarlsonj ar_cb(uintptr_t addr, const void *arptr, void *dummy)
3702546Scarlsonj {
3712546Scarlsonj 	const ar_t *ar = arptr;
3722546Scarlsonj 	char ardesc[sizeof ("ARP  ") + LIFNAMSIZ];
3732546Scarlsonj 
3742546Scarlsonj 	ar_describe(ar, ardesc, sizeof (ardesc), B_FALSE);
3752546Scarlsonj 	mdb_printf("%?p %?p %?p %s\n", addr, ar->ar_wq, ar->ar_arl, ardesc);
3762546Scarlsonj 	return (WALK_NEXT);
3772546Scarlsonj }
3782546Scarlsonj 
3792546Scarlsonj /*
3802546Scarlsonj  * Print out ARP client structures.
3812546Scarlsonj  */
3822546Scarlsonj /* ARGSUSED2 */
3832546Scarlsonj static int
3842546Scarlsonj ar_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3852546Scarlsonj {
3862546Scarlsonj 	ar_t ar;
3872546Scarlsonj 
3882546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
3892546Scarlsonj 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
3902546Scarlsonj 		    "AR", "WQ", "ARL", "TYPE");
3912546Scarlsonj 	}
3922546Scarlsonj 
3932546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
3942546Scarlsonj 		if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
3952546Scarlsonj 			mdb_warn("failed to read ar_t at %p", addr);
3962546Scarlsonj 			return (DCMD_ERR);
3972546Scarlsonj 		}
3982546Scarlsonj 		(void) ar_cb(addr, &ar, NULL);
3992546Scarlsonj 	} else {
4002546Scarlsonj 		if (mdb_walk("ar", ar_cb, NULL) == -1) {
4012546Scarlsonj 			mdb_warn("cannot walk ar_t structures");
4022546Scarlsonj 			return (DCMD_ERR);
4032546Scarlsonj 		}
4042546Scarlsonj 	}
4052546Scarlsonj 	return (DCMD_OK);
4062546Scarlsonj }
4072546Scarlsonj 
4082546Scarlsonj /* ARGSUSED2 */
4092546Scarlsonj static int
4102546Scarlsonj arl_cb(uintptr_t addr, const void *arlptr, void *dummy)
4112546Scarlsonj {
4122546Scarlsonj 	const arl_t *arl = arlptr;
4133065Smeem 	arlphy_t ap;
4142546Scarlsonj 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
4152546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
4162546Scarlsonj 	char flags[4];
4172546Scarlsonj 	const char *primstr;
4182546Scarlsonj 
4192546Scarlsonj 	mdb_printf("%?p  ", addr);
4202546Scarlsonj 	if (arl->arl_dlpi_pending == DL_PRIM_INVAL)
4212546Scarlsonj 		mdb_printf("%16s", "--");
4222546Scarlsonj 	else if ((primstr = mdb_dlpi_prim(arl->arl_dlpi_pending)) != NULL)
4232546Scarlsonj 		mdb_printf("%16s", primstr);
4242546Scarlsonj 	else
4252546Scarlsonj 		mdb_printf("%16x", arl->arl_dlpi_pending);
4263065Smeem 
4273065Smeem 	if (mdb_vread(&ap, sizeof (ap), (uintptr_t)arl->arl_phy) == -1 ||
4283065Smeem 	    ap.ap_hw_addrlen == 0 || ap.ap_hw_addrlen > sizeof (macaddr)) {
4292546Scarlsonj 		(void) strcpy(macstr, "--");
4303065Smeem 	} else if (mdb_vread(macaddr, ap.ap_hw_addrlen,
4313065Smeem 	    (uintptr_t)ap.ap_hw_addr) == -1) {
4322546Scarlsonj 		(void) strcpy(macstr, "?");
4332546Scarlsonj 	} else {
4343065Smeem 		mdb_mac_addr(macaddr, ap.ap_hw_addrlen, macstr,
4352546Scarlsonj 		    sizeof (macstr));
4362546Scarlsonj 	}
4372546Scarlsonj 
4382546Scarlsonj 	/* Print both the link-layer state and the NOARP flag */
4392546Scarlsonj 	flags[0] = '\0';
4402546Scarlsonj 	if (arl->arl_flags & ARL_F_NOARP)
4412546Scarlsonj 		(void) strcat(flags, "N");
4422546Scarlsonj 	switch (arl->arl_state) {
4432546Scarlsonj 	case ARL_S_DOWN:
4442546Scarlsonj 		(void) strcat(flags, "d");
4452546Scarlsonj 		break;
4462546Scarlsonj 	case ARL_S_PENDING:
4472546Scarlsonj 		(void) strcat(flags, "P");
4482546Scarlsonj 		break;
4492546Scarlsonj 	case ARL_S_UP:
4502546Scarlsonj 		(void) strcat(flags, "U");
4512546Scarlsonj 		break;
4522546Scarlsonj 	default:
4532546Scarlsonj 		(void) strcat(flags, "?");
4542546Scarlsonj 		break;
4552546Scarlsonj 	}
456*3865Szf203873 	mdb_printf("  %8d  %-3s  %-9s  %-17s %5d\n",
4572546Scarlsonj 	    mdb_mblk_count(arl->arl_dlpi_deferred), flags, arl->arl_name,
458*3865Szf203873 	    macstr, arl_to_stackid((uintptr_t)addr));
4592546Scarlsonj 	return (WALK_NEXT);
4602546Scarlsonj }
4612546Scarlsonj 
4622546Scarlsonj /*
4632546Scarlsonj  * Print out ARP link-layer elements.
4642546Scarlsonj  */
4652546Scarlsonj /* ARGSUSED2 */
4662546Scarlsonj static int
4672546Scarlsonj arl_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4682546Scarlsonj {
4692546Scarlsonj 	arl_t arl;
4702546Scarlsonj 
4712546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
472*3865Szf203873 		mdb_printf("%<u>%?s  %16s  %8s  %3s  %9s  %-17s %5s%</u>\n",
4732546Scarlsonj 		    "ARL", "DLPI REQ", "DLPI CNT", "FLG", "INTERFACE",
474*3865Szf203873 		    "HWADDR", "STACK");
4752546Scarlsonj 	}
4762546Scarlsonj 
4772546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
4782546Scarlsonj 		if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
4792546Scarlsonj 			mdb_warn("failed to read arl_t at %p", addr);
4802546Scarlsonj 			return (DCMD_ERR);
4812546Scarlsonj 		}
4822546Scarlsonj 		(void) arl_cb(addr, &arl, NULL);
4832546Scarlsonj 	} else {
4842546Scarlsonj 		if (mdb_walk("arl", arl_cb, NULL) == -1) {
4852546Scarlsonj 			mdb_warn("cannot walk arl_t structures");
4862546Scarlsonj 			return (DCMD_ERR);
4872546Scarlsonj 		}
4882546Scarlsonj 	}
4892546Scarlsonj 	return (DCMD_OK);
4902546Scarlsonj }
4912546Scarlsonj 
4922546Scarlsonj /* ARGSUSED2 */
4932546Scarlsonj static int
4942546Scarlsonj ace_cb(uintptr_t addr, const void *aceptr, void *dummy)
4952546Scarlsonj {
4962546Scarlsonj 	const ace_t *ace = aceptr;
4972546Scarlsonj 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
4982546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
4992546Scarlsonj 	/* The %b format isn't compact enough for long listings */
5002546Scarlsonj 	static const char ace_flags[] = "SPDRMLdA ofya";
5012546Scarlsonj 	const char *cp;
5022546Scarlsonj 	char flags[sizeof (ace_flags)], *fp;
5032546Scarlsonj 	int flg;
5042546Scarlsonj 	in_addr_t inaddr, mask;
5052546Scarlsonj 	char addrstr[sizeof ("255.255.255.255/32")];
5062546Scarlsonj 
5072546Scarlsonj 	/* Walk the list of flags and produce a string */
5082546Scarlsonj 	cp = ace_flags;
5092546Scarlsonj 	fp = flags;
5102546Scarlsonj 	for (flg = 1; *cp != '\0'; flg <<= 1, cp++) {
5112546Scarlsonj 		if ((flg & ace->ace_flags) && *cp != ' ')
5122546Scarlsonj 			*fp++ = *cp;
5132546Scarlsonj 	}
5142546Scarlsonj 	*fp = '\0';
5152546Scarlsonj 
5162546Scarlsonj 	/* If it's not resolved, then it has no hardware address */
5172546Scarlsonj 	if (!(ace->ace_flags & ACE_F_RESOLVED) ||
5182546Scarlsonj 	    ace->ace_hw_addr_length == 0 ||
5192546Scarlsonj 	    ace->ace_hw_addr_length > sizeof (macaddr)) {
5202546Scarlsonj 		(void) strcpy(macstr, "--");
5212546Scarlsonj 	} else if (mdb_vread(macaddr, ace->ace_hw_addr_length,
5222546Scarlsonj 	    (uintptr_t)ace->ace_hw_addr) == -1) {
5232546Scarlsonj 		(void) strcpy(macstr, "?");
5242546Scarlsonj 	} else {
5252546Scarlsonj 		mdb_mac_addr(macaddr, ace->ace_hw_addr_length, macstr,
5262546Scarlsonj 		    sizeof (macstr));
5272546Scarlsonj 	}
5282546Scarlsonj 
5292546Scarlsonj 	/*
5302546Scarlsonj 	 * Nothing other than IP uses ARP these days, so we don't try very hard
5312546Scarlsonj 	 * here to switch out on ARP protocol type.  (Note that ARP protocol
5322546Scarlsonj 	 * types are roughly Ethertypes, but are allocated separately at IANA.)
5332546Scarlsonj 	 */
5342546Scarlsonj 	if (ace->ace_proto != IP_ARP_PROTO_TYPE) {
5352546Scarlsonj 		(void) mdb_snprintf(addrstr, sizeof (addrstr),
5362546Scarlsonj 		    "Unknown proto %x", ace->ace_proto);
5372546Scarlsonj 	} else if (mdb_vread(&inaddr, sizeof (inaddr),
5382546Scarlsonj 	    (uintptr_t)ace->ace_proto_addr) != -1 &&
5392546Scarlsonj 	    mdb_vread(&mask, sizeof (mask), (uintptr_t)ace->ace_proto_mask) !=
5402546Scarlsonj 	    -1) {
5412546Scarlsonj 		/*
5422546Scarlsonj 		 * If it's the standard host mask, then print it normally.
5432546Scarlsonj 		 * Otherwise, use "/n" notation.
5442546Scarlsonj 		 */
5452546Scarlsonj 		if (mask == (in_addr_t)~0) {
5462546Scarlsonj 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I",
5472546Scarlsonj 			    inaddr);
5482546Scarlsonj 		} else {
5492546Scarlsonj 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I/%d",
5502546Scarlsonj 			    inaddr, mask == 0 ? 0 : 33 - mdb_ffs(mask));
5512546Scarlsonj 		}
5522546Scarlsonj 	} else {
5532546Scarlsonj 		(void) strcpy(addrstr, "?");
5542546Scarlsonj 	}
555*3865Szf203873 	mdb_printf("%?p  %-18s  %-8s  %-17s %5d\n", addr, addrstr, flags,
556*3865Szf203873 	    macstr, arl_to_stackid((uintptr_t)ace->ace_arl));
5572546Scarlsonj 	return (WALK_NEXT);
5582546Scarlsonj }
5592546Scarlsonj 
5602546Scarlsonj /*
5612546Scarlsonj  * Print out ARP cache entry (ace_t) elements.
5622546Scarlsonj  */
5632546Scarlsonj /* ARGSUSED2 */
5642546Scarlsonj static int
5652546Scarlsonj ace_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5662546Scarlsonj {
5672546Scarlsonj 	ace_t ace;
5682546Scarlsonj 
5692546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
570*3865Szf203873 		mdb_printf("%<u>%?s  %-18s  %-8s  %-17s %5s%</u>\n",
571*3865Szf203873 		    "ACE", "PROTOADDR", "FLAGS", "HWADDR", "STACK");
5722546Scarlsonj 	}
5732546Scarlsonj 
5742546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
5752546Scarlsonj 		if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
5762546Scarlsonj 			mdb_warn("failed to read ace_t at %p", addr);
5772546Scarlsonj 			return (DCMD_ERR);
5782546Scarlsonj 		}
5792546Scarlsonj 		(void) ace_cb(addr, &ace, NULL);
5802546Scarlsonj 	} else {
5812546Scarlsonj 		if (mdb_walk("ace", ace_cb, NULL) == -1) {
5822546Scarlsonj 			mdb_warn("cannot walk ace_t structures");
5832546Scarlsonj 			return (DCMD_ERR);
5842546Scarlsonj 		}
5852546Scarlsonj 	}
5862546Scarlsonj 	return (DCMD_OK);
5872546Scarlsonj }
5882546Scarlsonj 
5892546Scarlsonj /*
5902546Scarlsonj  * Print an ARP hardware and protocol address pair; used when printing an ARP
5912546Scarlsonj  * message.
5922546Scarlsonj  */
5932546Scarlsonj static void
5942546Scarlsonj print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
5952546Scarlsonj {
5962546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
5972546Scarlsonj 	in_addr_t inaddr;
5982546Scarlsonj 
5992546Scarlsonj 	if (arh->arh_hlen == 0)
6002546Scarlsonj 		(void) strcpy(macstr, "(none)");
6012546Scarlsonj 	else
6022546Scarlsonj 		mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
6032546Scarlsonj 	mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
6042546Scarlsonj 	if (arh->arh_plen == 0) {
6052546Scarlsonj 		mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
6062546Scarlsonj 	} else if (ptype == IP_ARP_PROTO_TYPE) {
6072546Scarlsonj 		mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
6082546Scarlsonj 	} else if (arh->arh_plen == sizeof (in_addr_t)) {
6092546Scarlsonj 		(void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
6102546Scarlsonj 		mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
6112546Scarlsonj 	} else {
6122546Scarlsonj 		mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
6132546Scarlsonj 	}
6142546Scarlsonj }
6152546Scarlsonj 
6162546Scarlsonj /*
6172546Scarlsonj  * Decode an ARP message and display it.
6182546Scarlsonj  */
6192546Scarlsonj /* ARGSUSED2 */
6202546Scarlsonj static int
6212546Scarlsonj arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
6222546Scarlsonj {
6232546Scarlsonj 	struct {
6242546Scarlsonj 		arh_t arh;
6252546Scarlsonj 		uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
6262546Scarlsonj 	} arp;
6272546Scarlsonj 	size_t blen;
6282546Scarlsonj 	uint16_t htype, ptype, op;
6292546Scarlsonj 	const char *cp;
6302546Scarlsonj 
6312546Scarlsonj 	if (!(flags & DCMD_ADDRSPEC)) {
6322546Scarlsonj 		mdb_warn("address required to print ARP header\n");
6332546Scarlsonj 		return (DCMD_ERR);
6342546Scarlsonj 	}
6352546Scarlsonj 
6362546Scarlsonj 	if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
6372546Scarlsonj 		mdb_warn("unable to read ARP header at %p", addr);
6382546Scarlsonj 		return (DCMD_ERR);
6392546Scarlsonj 	}
6402546Scarlsonj 	mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
6412546Scarlsonj 	mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
6422546Scarlsonj 	mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));
6432546Scarlsonj 
6442546Scarlsonj 	switch (htype) {
6452546Scarlsonj 	case ARPHRD_ETHER:
6462546Scarlsonj 		cp = "Ether";
6472546Scarlsonj 		break;
6482546Scarlsonj 	case ARPHRD_IEEE802:
6492546Scarlsonj 		cp = "IEEE802";
6502546Scarlsonj 		break;
6512546Scarlsonj 	case ARPHRD_IB:
6522546Scarlsonj 		cp = "InfiniBand";
6532546Scarlsonj 		break;
6542546Scarlsonj 	default:
6552546Scarlsonj 		cp = "Unknown";
6562546Scarlsonj 		break;
6572546Scarlsonj 	}
6582546Scarlsonj 	mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
6592546Scarlsonj 	mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
6602546Scarlsonj 	    ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");
6612546Scarlsonj 
6622546Scarlsonj 	switch (op) {
6632546Scarlsonj 	case ARPOP_REQUEST:
6642546Scarlsonj 		cp = "ares_op$REQUEST";
6652546Scarlsonj 		break;
6662546Scarlsonj 	case ARPOP_REPLY:
6672546Scarlsonj 		cp = "ares_op$REPLY";
6682546Scarlsonj 		break;
6692546Scarlsonj 	case REVARP_REQUEST:
6702546Scarlsonj 		cp = "arev_op$REQUEST";
6712546Scarlsonj 		break;
6722546Scarlsonj 	case REVARP_REPLY:
6732546Scarlsonj 		cp = "arev_op$REPLY";
6742546Scarlsonj 		break;
6752546Scarlsonj 	default:
6762546Scarlsonj 		cp = "Unknown";
6772546Scarlsonj 		break;
6782546Scarlsonj 	}
6792546Scarlsonj 	mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);
6802546Scarlsonj 
6812546Scarlsonj 	/*
6822546Scarlsonj 	 * Note that we go to some length to attempt to print out the fixed
6832546Scarlsonj 	 * header data before trying to decode the variable-length data.  This
6842546Scarlsonj 	 * is done to maximize the amount of useful information shown when the
6852546Scarlsonj 	 * buffer is truncated or otherwise corrupt.
6862546Scarlsonj 	 */
6872546Scarlsonj 	blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
6882546Scarlsonj 	if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
6892546Scarlsonj 		mdb_warn("unable to read ARP body at %p", addr);
6902546Scarlsonj 		return (DCMD_ERR);
6912546Scarlsonj 	}
6922546Scarlsonj 
6932546Scarlsonj 	print_arp('s', arp.addrs, &arp.arh, ptype);
6942546Scarlsonj 	print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
6952546Scarlsonj 	    &arp.arh, ptype);
6962546Scarlsonj 	return (DCMD_OK);
6972546Scarlsonj }
6982546Scarlsonj 
6992546Scarlsonj /*
7002546Scarlsonj  * Print out an arp command formatted in a reasonable manner.  This implements
7012546Scarlsonj  * the type switch used by ARP.
7022546Scarlsonj  *
7032546Scarlsonj  * It could also dump the data that follows the header (using offset and length
7042546Scarlsonj  * in the various structures), but it currently does not.
7052546Scarlsonj  */
7062546Scarlsonj /* ARGSUSED2 */
7072546Scarlsonj static int
7082546Scarlsonj arpcmd_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7092546Scarlsonj {
7102546Scarlsonj 	arc_t arc;
7112546Scarlsonj 	const arp_cmd_tbl *tp;
7122546Scarlsonj 	mdb_arg_t subargv;
7132546Scarlsonj 
7142546Scarlsonj 	if (!(flags & DCMD_ADDRSPEC)) {
7152546Scarlsonj 		mdb_warn("address required to print ARP command\n");
7162546Scarlsonj 		return (DCMD_ERR);
7172546Scarlsonj 	}
7182546Scarlsonj 	if (mdb_vread(&arc, sizeof (arc), addr) == -1) {
7192546Scarlsonj 		mdb_warn("unable to read arc_t at %p", addr);
7202546Scarlsonj 		return (DCMD_ERR);
7212546Scarlsonj 	}
7222546Scarlsonj 	for (tp = act_list; tp->act_cmd != 0; tp++)
7232546Scarlsonj 		if (tp->act_cmd == arc.arc_cmd)
7242546Scarlsonj 			break;
7252546Scarlsonj 	mdb_printf("%p %s (%s) = ", addr, tp->act_name, tp->act_type);
7262546Scarlsonj 	subargv.a_type = MDB_TYPE_STRING;
7272546Scarlsonj 	subargv.a_un.a_str = tp->act_type;
7282546Scarlsonj 	if (mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, &subargv) == -1)
7292546Scarlsonj 		return (DCMD_ERR);
7302546Scarlsonj 	else
7312546Scarlsonj 		return (DCMD_OK);
7322546Scarlsonj }
7332546Scarlsonj 
7342546Scarlsonj static size_t
7352546Scarlsonj mi_osize(const queue_t *q)
7362546Scarlsonj {
7372546Scarlsonj 	/*
7382546Scarlsonj 	 * The code in common/inet/mi.c allocates an extra word to store the
7392546Scarlsonj 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
7402546Scarlsonj 	 */
7412546Scarlsonj 	struct mi_block {
7422546Scarlsonj 		size_t mi_nbytes;
7432546Scarlsonj 		struct mi_o_s mi_o;
7442546Scarlsonj 	} m;
7452546Scarlsonj 
7462546Scarlsonj 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr - sizeof (m)) != -1)
7472546Scarlsonj 		return (m.mi_nbytes - sizeof (m));
7482546Scarlsonj 
7492546Scarlsonj 	return (0);
7502546Scarlsonj }
7512546Scarlsonj 
7522546Scarlsonj /*
7532546Scarlsonj  * This is called when ::stream is used and an ARP module is seen on the
7542546Scarlsonj  * stream.  Determine what sort of ARP usage is involved and show an
7552546Scarlsonj  * appropriate message.
7562546Scarlsonj  */
7572546Scarlsonj static void
7582546Scarlsonj arp_qinfo(const queue_t *qp, char *buf, size_t nbytes)
7592546Scarlsonj {
7602546Scarlsonj 	size_t size = mi_osize(qp);
7612546Scarlsonj 	ar_t ar;
7622546Scarlsonj 
7632546Scarlsonj 	if (size != sizeof (ar_t))
7642546Scarlsonj 		return;
7652546Scarlsonj 	if (mdb_vread(&ar, sizeof (ar), (uintptr_t)qp->q_ptr) == -1)
7662546Scarlsonj 		return;
7672546Scarlsonj 	ar_describe(&ar, buf, nbytes, B_TRUE);
7682546Scarlsonj }
7692546Scarlsonj 
7702546Scarlsonj static uintptr_t
7712546Scarlsonj arp_rnext(const queue_t *q)
7722546Scarlsonj {
7732546Scarlsonj 	size_t size = mi_osize(q);
7742546Scarlsonj 	ar_t ar;
7752546Scarlsonj 
7762546Scarlsonj 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
7772546Scarlsonj 	    (uintptr_t)q->q_ptr) != -1)
7782546Scarlsonj 		return ((uintptr_t)ar.ar_rq);
7792546Scarlsonj 
7802546Scarlsonj 	return (NULL);
7812546Scarlsonj }
7822546Scarlsonj 
7832546Scarlsonj static uintptr_t
7842546Scarlsonj arp_wnext(const queue_t *q)
7852546Scarlsonj {
7862546Scarlsonj 	size_t size = mi_osize(q);
7872546Scarlsonj 	ar_t ar;
7882546Scarlsonj 
7892546Scarlsonj 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
7902546Scarlsonj 	    (uintptr_t)q->q_ptr) != -1)
7912546Scarlsonj 		return ((uintptr_t)ar.ar_wq);
7922546Scarlsonj 
7932546Scarlsonj 	return (NULL);
7942546Scarlsonj }
7952546Scarlsonj 
7962546Scarlsonj static const mdb_dcmd_t dcmds[] = {
797*3865Szf203873 	{ "ar", "?", "display ARP client streams for all stacks",
798*3865Szf203873 	    ar_cmd, NULL },
799*3865Szf203873 	{ "arl", "?", "display ARP link layers for all stacks", arl_cmd, NULL },
800*3865Szf203873 	{ "ace", "?", "display ARP cache entries for all stacks",
801*3865Szf203873 	    ace_cmd, NULL },
8022546Scarlsonj 	{ "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
8032546Scarlsonj 	{ "arpcmd", ":", "display an ARP command", arpcmd_cmd, NULL },
8042546Scarlsonj 	{ NULL }
8052546Scarlsonj };
8062546Scarlsonj 
8072546Scarlsonj /* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
8082546Scarlsonj static const mdb_walker_t walkers[] = {
809*3865Szf203873 	{ "arl", "walk list of arl_t links for all stacks",
8102546Scarlsonj 	    arl_walk_init, arl_walk_step, NULL },
811*3865Szf203873 	{ "arl_stack", "walk list of arl_t links",
812*3865Szf203873 	    arl_stack_walk_init, arl_stack_walk_step, NULL },
813*3865Szf203873 	{ "ace", "walk list of ace_t entries for all stacks",
814*3865Szf203873 	    ace_walk_init, ace_walk_step, NULL },
815*3865Szf203873 	{ "ace_stack", "walk list of ace_t entries",
816*3865Szf203873 	    ace_stack_walk_init, ace_stack_walk_step, ace_stack_walk_fini },
817*3865Szf203873 	{ "arp_stacks", "walk all the arp_stack_t",
818*3865Szf203873 	    arp_stacks_walk_init, arp_stacks_walk_step, NULL },
8192546Scarlsonj 	{ NULL }
8202546Scarlsonj };
8212546Scarlsonj 
8222546Scarlsonj static const mdb_qops_t arp_qops = { arp_qinfo, arp_rnext, arp_wnext };
8232546Scarlsonj static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
8242546Scarlsonj 
8252546Scarlsonj const mdb_modinfo_t *
8262546Scarlsonj _mdb_init(void)
8272546Scarlsonj {
8282546Scarlsonj 	GElf_Sym sym;
8292546Scarlsonj 
8302546Scarlsonj 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
8312546Scarlsonj 		mdb_qops_install(&arp_qops, (uintptr_t)sym.st_value);
8322546Scarlsonj 
8332546Scarlsonj 	return (&modinfo);
8342546Scarlsonj }
8352546Scarlsonj 
8362546Scarlsonj void
8372546Scarlsonj _mdb_fini(void)
8382546Scarlsonj {
8392546Scarlsonj 	GElf_Sym sym;
8402546Scarlsonj 
8412546Scarlsonj 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
8422546Scarlsonj 		mdb_qops_remove(&arp_qops, (uintptr_t)sym.st_value);
8432546Scarlsonj }
844