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