xref: /onnv-gate/usr/src/cmd/mdb/common/modules/arp/arp.c (revision 2958:98aa41c076f5)
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 /*
222546Scarlsonj  * Copyright 2006 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>
33*2958Sdr146992 #include <sys/hook.h>
34*2958Sdr146992 #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 
832546Scarlsonj static int
842546Scarlsonj arl_walk_init(mdb_walk_state_t *wsp)
852546Scarlsonj {
862546Scarlsonj 	if (wsp->walk_addr == NULL &&
872546Scarlsonj 	    mdb_readvar(&wsp->walk_addr, "arl_g_head") == -1) {
882546Scarlsonj 		mdb_warn("failed to read 'arl_g_head'");
892546Scarlsonj 		return (WALK_ERR);
902546Scarlsonj 	}
912546Scarlsonj 	return (WALK_NEXT);
922546Scarlsonj }
932546Scarlsonj 
942546Scarlsonj static int
952546Scarlsonj arl_walk_step(mdb_walk_state_t *wsp)
962546Scarlsonj {
972546Scarlsonj 	uintptr_t addr = wsp->walk_addr;
982546Scarlsonj 	arl_t arl;
992546Scarlsonj 
1002546Scarlsonj 	if (wsp->walk_addr == NULL)
1012546Scarlsonj 		return (WALK_DONE);
1022546Scarlsonj 
1032546Scarlsonj 	if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
1042546Scarlsonj 		mdb_warn("failed to read arl_t at %p", addr);
1052546Scarlsonj 		return (WALK_ERR);
1062546Scarlsonj 	}
1072546Scarlsonj 
1082546Scarlsonj 	wsp->walk_addr = (uintptr_t)arl.arl_next;
1092546Scarlsonj 
1102546Scarlsonj 	return ((*wsp->walk_callback)(addr, &arl, wsp->walk_cbdata));
1112546Scarlsonj }
1122546Scarlsonj 
1132546Scarlsonj static int
1142546Scarlsonj ace_walk_init(mdb_walk_state_t *wsp)
1152546Scarlsonj {
1162546Scarlsonj 	ace_walk_data_t *aw;
1172546Scarlsonj 
1182546Scarlsonj 	if (wsp->walk_addr != NULL) {
1192546Scarlsonj 		mdb_warn("ace supports only global walks\n");
1202546Scarlsonj 		return (WALK_ERR);
1212546Scarlsonj 	}
1222546Scarlsonj 
1232546Scarlsonj 	aw = mdb_alloc(sizeof (ace_walk_data_t), UM_SLEEP);
1242546Scarlsonj 
1252546Scarlsonj 	if (mdb_readsym(aw->awd_hash_tbl, sizeof (aw->awd_hash_tbl),
1262546Scarlsonj 	    "ar_ce_hash_tbl") == -1) {
1272546Scarlsonj 		mdb_warn("failed to read 'ar_ce_hash_tbl'");
1282546Scarlsonj 		mdb_free(aw, sizeof (ace_walk_data_t));
1292546Scarlsonj 		return (WALK_ERR);
1302546Scarlsonj 	}
1312546Scarlsonj 
1322546Scarlsonj 	if (mdb_readvar(&aw->awd_masks, "ar_ce_mask_entries") == -1) {
1332546Scarlsonj 		mdb_warn("failed to read 'ar_ce_mask_entries'");
1342546Scarlsonj 		mdb_free(aw, sizeof (ace_walk_data_t));
1352546Scarlsonj 		return (WALK_ERR);
1362546Scarlsonj 	}
1372546Scarlsonj 
1382546Scarlsonj 	/* The step routine will start off by incrementing to index 0 */
1392546Scarlsonj 	aw->awd_idx = -1;
1402546Scarlsonj 	wsp->walk_addr = 0;
1412546Scarlsonj 	wsp->walk_data = aw;
1422546Scarlsonj 
1432546Scarlsonj 	return (WALK_NEXT);
1442546Scarlsonj }
1452546Scarlsonj 
1462546Scarlsonj static int
1472546Scarlsonj ace_walk_step(mdb_walk_state_t *wsp)
1482546Scarlsonj {
1492546Scarlsonj 	uintptr_t addr;
1502546Scarlsonj 	ace_walk_data_t *aw = wsp->walk_data;
1512546Scarlsonj 	ace_t ace;
1522546Scarlsonj 
1532546Scarlsonj 	/*
1542546Scarlsonj 	 * If we're at the end of the previous list, then find the start of the
1552546Scarlsonj 	 * next list to process.
1562546Scarlsonj 	 */
1572546Scarlsonj 	while (wsp->walk_addr == NULL) {
1582546Scarlsonj 		if (aw->awd_idx == ARP_HASH_SIZE)
1592546Scarlsonj 			return (WALK_DONE);
1602546Scarlsonj 		if (++aw->awd_idx == ARP_HASH_SIZE) {
1612546Scarlsonj 			wsp->walk_addr = (uintptr_t)aw->awd_masks;
1622546Scarlsonj 		} else {
1632546Scarlsonj 			wsp->walk_addr =
1642546Scarlsonj 			    (uintptr_t)aw->awd_hash_tbl[aw->awd_idx];
1652546Scarlsonj 		}
1662546Scarlsonj 	}
1672546Scarlsonj 
1682546Scarlsonj 	addr = wsp->walk_addr;
1692546Scarlsonj 	if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
1702546Scarlsonj 		mdb_warn("failed to read ace_t at %p", addr);
1712546Scarlsonj 		return (WALK_ERR);
1722546Scarlsonj 	}
1732546Scarlsonj 
1742546Scarlsonj 	wsp->walk_addr = (uintptr_t)ace.ace_next;
1752546Scarlsonj 
1762546Scarlsonj 	return (wsp->walk_callback(addr, &ace, wsp->walk_cbdata));
1772546Scarlsonj }
1782546Scarlsonj 
1792546Scarlsonj static void
1802546Scarlsonj ace_walk_fini(mdb_walk_state_t *wsp)
1812546Scarlsonj {
1822546Scarlsonj 	mdb_free(wsp->walk_data, sizeof (ace_walk_data_t));
1832546Scarlsonj }
1842546Scarlsonj 
1852546Scarlsonj /* Common routine to produce an 'ar' text description */
1862546Scarlsonj static void
1872546Scarlsonj ar_describe(const ar_t *ar, char *buf, size_t nbytes, boolean_t addmac)
1882546Scarlsonj {
1892546Scarlsonj 	if (ar->ar_arl == NULL) {
1902546Scarlsonj 		queue_t wq, ipq;
1912546Scarlsonj 		ill_t ill;
1922546Scarlsonj 		char name[LIFNAMSIZ];
1932546Scarlsonj 		GElf_Sym sym;
1942546Scarlsonj 		boolean_t nextip;
1952546Scarlsonj 
1962546Scarlsonj 		if (mdb_vread(&wq, sizeof (wq), (uintptr_t)ar->ar_wq) == -1 ||
1972546Scarlsonj 		    mdb_vread(&ipq, sizeof (ipq), (uintptr_t)wq.q_next) == -1)
1982546Scarlsonj 			return;
1992546Scarlsonj 
2002546Scarlsonj 		nextip =
2012546Scarlsonj 		    (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0 &&
2022546Scarlsonj 		    (uintptr_t)sym.st_value == (uintptr_t)ipq.q_qinfo);
2032546Scarlsonj 
2042546Scarlsonj 		if (!ar->ar_on_ill_stream) {
2052546Scarlsonj 			(void) strcpy(buf, nextip ? "Client" : "Unknown");
2062546Scarlsonj 			return;
2072546Scarlsonj 		}
2082546Scarlsonj 
2092546Scarlsonj 		if (!nextip ||
2102546Scarlsonj 		    mdb_vread(&ill, sizeof (ill), (uintptr_t)ipq.q_ptr) == -1 ||
2112546Scarlsonj 		    mdb_readstr(name, sizeof (name),
2122546Scarlsonj 		    (uintptr_t)ill.ill_name) == -1) {
2132546Scarlsonj 			return;
2142546Scarlsonj 		}
2152546Scarlsonj 		(void) mdb_snprintf(buf, nbytes, "IP %s", name);
2162546Scarlsonj 	} else {
2172546Scarlsonj 		arl_t arl;
2182546Scarlsonj 		ssize_t retv;
2192546Scarlsonj 		uint32_t alen;
2202546Scarlsonj 		uchar_t macaddr[ARP_MAX_ADDR_LEN];
2212546Scarlsonj 
2222546Scarlsonj 		if (mdb_vread(&arl, sizeof (arl), (uintptr_t)ar->ar_arl) == -1)
2232546Scarlsonj 			return;
2242546Scarlsonj 		retv = mdb_snprintf(buf, nbytes, "ARP %s ", arl.arl_name);
2252546Scarlsonj 		if (retv >= nbytes || !addmac)
2262546Scarlsonj 			return;
2272546Scarlsonj 		alen = arl.arl_hw_addr_length;
2282546Scarlsonj 		if (arl.arl_hw_addr == NULL || alen == 0 ||
2292546Scarlsonj 		    alen > sizeof (macaddr))
2302546Scarlsonj 			return;
2312546Scarlsonj 		if (mdb_vread(macaddr, alen, (uintptr_t)arl.arl_hw_addr) == -1)
2322546Scarlsonj 			return;
2332546Scarlsonj 		mdb_mac_addr(macaddr, alen, buf + retv, nbytes - retv);
2342546Scarlsonj 	}
2352546Scarlsonj }
2362546Scarlsonj 
2372546Scarlsonj /* ARGSUSED2 */
2382546Scarlsonj static int
2392546Scarlsonj ar_cb(uintptr_t addr, const void *arptr, void *dummy)
2402546Scarlsonj {
2412546Scarlsonj 	const ar_t *ar = arptr;
2422546Scarlsonj 	char ardesc[sizeof ("ARP  ") + LIFNAMSIZ];
2432546Scarlsonj 
2442546Scarlsonj 	ar_describe(ar, ardesc, sizeof (ardesc), B_FALSE);
2452546Scarlsonj 	mdb_printf("%?p %?p %?p %s\n", addr, ar->ar_wq, ar->ar_arl, ardesc);
2462546Scarlsonj 	return (WALK_NEXT);
2472546Scarlsonj }
2482546Scarlsonj 
2492546Scarlsonj /*
2502546Scarlsonj  * Print out ARP client structures.
2512546Scarlsonj  */
2522546Scarlsonj /* ARGSUSED2 */
2532546Scarlsonj static int
2542546Scarlsonj ar_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2552546Scarlsonj {
2562546Scarlsonj 	ar_t ar;
2572546Scarlsonj 
2582546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
2592546Scarlsonj 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
2602546Scarlsonj 		    "AR", "WQ", "ARL", "TYPE");
2612546Scarlsonj 	}
2622546Scarlsonj 
2632546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
2642546Scarlsonj 		if (mdb_vread(&ar, sizeof (ar), addr) == -1) {
2652546Scarlsonj 			mdb_warn("failed to read ar_t at %p", addr);
2662546Scarlsonj 			return (DCMD_ERR);
2672546Scarlsonj 		}
2682546Scarlsonj 		(void) ar_cb(addr, &ar, NULL);
2692546Scarlsonj 	} else {
2702546Scarlsonj 		if (mdb_walk("ar", ar_cb, NULL) == -1) {
2712546Scarlsonj 			mdb_warn("cannot walk ar_t structures");
2722546Scarlsonj 			return (DCMD_ERR);
2732546Scarlsonj 		}
2742546Scarlsonj 	}
2752546Scarlsonj 	return (DCMD_OK);
2762546Scarlsonj }
2772546Scarlsonj 
2782546Scarlsonj /* ARGSUSED2 */
2792546Scarlsonj static int
2802546Scarlsonj arl_cb(uintptr_t addr, const void *arlptr, void *dummy)
2812546Scarlsonj {
2822546Scarlsonj 	const arl_t *arl = arlptr;
2832546Scarlsonj 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
2842546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
2852546Scarlsonj 	char flags[4];
2862546Scarlsonj 	const char *primstr;
2872546Scarlsonj 
2882546Scarlsonj 	mdb_printf("%?p  ", addr);
2892546Scarlsonj 	if (arl->arl_dlpi_pending == DL_PRIM_INVAL)
2902546Scarlsonj 		mdb_printf("%16s", "--");
2912546Scarlsonj 	else if ((primstr = mdb_dlpi_prim(arl->arl_dlpi_pending)) != NULL)
2922546Scarlsonj 		mdb_printf("%16s", primstr);
2932546Scarlsonj 	else
2942546Scarlsonj 		mdb_printf("%16x", arl->arl_dlpi_pending);
2952546Scarlsonj 	if (arl->arl_hw_addr_length == 0 ||
2962546Scarlsonj 	    arl->arl_hw_addr_length > sizeof (macaddr)) {
2972546Scarlsonj 		(void) strcpy(macstr, "--");
2982546Scarlsonj 	} else if (mdb_vread(macaddr, arl->arl_hw_addr_length,
2992546Scarlsonj 	    (uintptr_t)arl->arl_hw_addr) == -1) {
3002546Scarlsonj 		(void) strcpy(macstr, "?");
3012546Scarlsonj 	} else {
3022546Scarlsonj 		mdb_mac_addr(macaddr, arl->arl_hw_addr_length, macstr,
3032546Scarlsonj 		    sizeof (macstr));
3042546Scarlsonj 	}
3052546Scarlsonj 
3062546Scarlsonj 	/* Print both the link-layer state and the NOARP flag */
3072546Scarlsonj 	flags[0] = '\0';
3082546Scarlsonj 	if (arl->arl_flags & ARL_F_NOARP)
3092546Scarlsonj 		(void) strcat(flags, "N");
3102546Scarlsonj 	switch (arl->arl_state) {
3112546Scarlsonj 	case ARL_S_DOWN:
3122546Scarlsonj 		(void) strcat(flags, "d");
3132546Scarlsonj 		break;
3142546Scarlsonj 	case ARL_S_PENDING:
3152546Scarlsonj 		(void) strcat(flags, "P");
3162546Scarlsonj 		break;
3172546Scarlsonj 	case ARL_S_UP:
3182546Scarlsonj 		(void) strcat(flags, "U");
3192546Scarlsonj 		break;
3202546Scarlsonj 	default:
3212546Scarlsonj 		(void) strcat(flags, "?");
3222546Scarlsonj 		break;
3232546Scarlsonj 	}
3242546Scarlsonj 	mdb_printf("  %8d  %-3s  %-9s  %s\n",
3252546Scarlsonj 	    mdb_mblk_count(arl->arl_dlpi_deferred), flags, arl->arl_name,
3262546Scarlsonj 	    macstr);
3272546Scarlsonj 	return (WALK_NEXT);
3282546Scarlsonj }
3292546Scarlsonj 
3302546Scarlsonj /*
3312546Scarlsonj  * Print out ARP link-layer elements.
3322546Scarlsonj  */
3332546Scarlsonj /* ARGSUSED2 */
3342546Scarlsonj static int
3352546Scarlsonj arl_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3362546Scarlsonj {
3372546Scarlsonj 	arl_t arl;
3382546Scarlsonj 
3392546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
3402546Scarlsonj 		mdb_printf("%<u>%?s  %16s  %8s  %3s  %9s  %s%</u>\n",
3412546Scarlsonj 		    "ARL", "DLPI REQ", "DLPI CNT", "FLG", "INTERFACE",
3422546Scarlsonj 		    "HW ADDR");
3432546Scarlsonj 	}
3442546Scarlsonj 
3452546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
3462546Scarlsonj 		if (mdb_vread(&arl, sizeof (arl), addr) == -1) {
3472546Scarlsonj 			mdb_warn("failed to read arl_t at %p", addr);
3482546Scarlsonj 			return (DCMD_ERR);
3492546Scarlsonj 		}
3502546Scarlsonj 		(void) arl_cb(addr, &arl, NULL);
3512546Scarlsonj 	} else {
3522546Scarlsonj 		if (mdb_walk("arl", arl_cb, NULL) == -1) {
3532546Scarlsonj 			mdb_warn("cannot walk arl_t structures");
3542546Scarlsonj 			return (DCMD_ERR);
3552546Scarlsonj 		}
3562546Scarlsonj 	}
3572546Scarlsonj 	return (DCMD_OK);
3582546Scarlsonj }
3592546Scarlsonj 
3602546Scarlsonj /* ARGSUSED2 */
3612546Scarlsonj static int
3622546Scarlsonj ace_cb(uintptr_t addr, const void *aceptr, void *dummy)
3632546Scarlsonj {
3642546Scarlsonj 	const ace_t *ace = aceptr;
3652546Scarlsonj 	uchar_t macaddr[ARP_MAX_ADDR_LEN];
3662546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
3672546Scarlsonj 	/* The %b format isn't compact enough for long listings */
3682546Scarlsonj 	static const char ace_flags[] = "SPDRMLdA ofya";
3692546Scarlsonj 	const char *cp;
3702546Scarlsonj 	char flags[sizeof (ace_flags)], *fp;
3712546Scarlsonj 	int flg;
3722546Scarlsonj 	in_addr_t inaddr, mask;
3732546Scarlsonj 	char addrstr[sizeof ("255.255.255.255/32")];
3742546Scarlsonj 
3752546Scarlsonj 	/* Walk the list of flags and produce a string */
3762546Scarlsonj 	cp = ace_flags;
3772546Scarlsonj 	fp = flags;
3782546Scarlsonj 	for (flg = 1; *cp != '\0'; flg <<= 1, cp++) {
3792546Scarlsonj 		if ((flg & ace->ace_flags) && *cp != ' ')
3802546Scarlsonj 			*fp++ = *cp;
3812546Scarlsonj 	}
3822546Scarlsonj 	*fp = '\0';
3832546Scarlsonj 
3842546Scarlsonj 	/* If it's not resolved, then it has no hardware address */
3852546Scarlsonj 	if (!(ace->ace_flags & ACE_F_RESOLVED) ||
3862546Scarlsonj 	    ace->ace_hw_addr_length == 0 ||
3872546Scarlsonj 	    ace->ace_hw_addr_length > sizeof (macaddr)) {
3882546Scarlsonj 		(void) strcpy(macstr, "--");
3892546Scarlsonj 	} else if (mdb_vread(macaddr, ace->ace_hw_addr_length,
3902546Scarlsonj 	    (uintptr_t)ace->ace_hw_addr) == -1) {
3912546Scarlsonj 		(void) strcpy(macstr, "?");
3922546Scarlsonj 	} else {
3932546Scarlsonj 		mdb_mac_addr(macaddr, ace->ace_hw_addr_length, macstr,
3942546Scarlsonj 		    sizeof (macstr));
3952546Scarlsonj 	}
3962546Scarlsonj 
3972546Scarlsonj 	/*
3982546Scarlsonj 	 * Nothing other than IP uses ARP these days, so we don't try very hard
3992546Scarlsonj 	 * here to switch out on ARP protocol type.  (Note that ARP protocol
4002546Scarlsonj 	 * types are roughly Ethertypes, but are allocated separately at IANA.)
4012546Scarlsonj 	 */
4022546Scarlsonj 	if (ace->ace_proto != IP_ARP_PROTO_TYPE) {
4032546Scarlsonj 		(void) mdb_snprintf(addrstr, sizeof (addrstr),
4042546Scarlsonj 		    "Unknown proto %x", ace->ace_proto);
4052546Scarlsonj 	} else if (mdb_vread(&inaddr, sizeof (inaddr),
4062546Scarlsonj 	    (uintptr_t)ace->ace_proto_addr) != -1 &&
4072546Scarlsonj 	    mdb_vread(&mask, sizeof (mask), (uintptr_t)ace->ace_proto_mask) !=
4082546Scarlsonj 	    -1) {
4092546Scarlsonj 		/*
4102546Scarlsonj 		 * If it's the standard host mask, then print it normally.
4112546Scarlsonj 		 * Otherwise, use "/n" notation.
4122546Scarlsonj 		 */
4132546Scarlsonj 		if (mask == (in_addr_t)~0) {
4142546Scarlsonj 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I",
4152546Scarlsonj 			    inaddr);
4162546Scarlsonj 		} else {
4172546Scarlsonj 			(void) mdb_snprintf(addrstr, sizeof (addrstr), "%I/%d",
4182546Scarlsonj 			    inaddr, mask == 0 ? 0 : 33 - mdb_ffs(mask));
4192546Scarlsonj 		}
4202546Scarlsonj 	} else {
4212546Scarlsonj 		(void) strcpy(addrstr, "?");
4222546Scarlsonj 	}
4232546Scarlsonj 	mdb_printf("%?p  %-18s  %-8s  %s\n", addr, addrstr, flags, macstr);
4242546Scarlsonj 	return (WALK_NEXT);
4252546Scarlsonj }
4262546Scarlsonj 
4272546Scarlsonj /*
4282546Scarlsonj  * Print out ARP cache entry (ace_t) elements.
4292546Scarlsonj  */
4302546Scarlsonj /* ARGSUSED2 */
4312546Scarlsonj static int
4322546Scarlsonj ace_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4332546Scarlsonj {
4342546Scarlsonj 	ace_t ace;
4352546Scarlsonj 
4362546Scarlsonj 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
4372546Scarlsonj 		mdb_printf("%<u>%?s  %-18s  %-8s  %s%</u>\n",
4382546Scarlsonj 		    "ACE", "PROTOADDR", "FLAGS", "HWADDR");
4392546Scarlsonj 	}
4402546Scarlsonj 
4412546Scarlsonj 	if (flags & DCMD_ADDRSPEC) {
4422546Scarlsonj 		if (mdb_vread(&ace, sizeof (ace), addr) == -1) {
4432546Scarlsonj 			mdb_warn("failed to read ace_t at %p", addr);
4442546Scarlsonj 			return (DCMD_ERR);
4452546Scarlsonj 		}
4462546Scarlsonj 		(void) ace_cb(addr, &ace, NULL);
4472546Scarlsonj 	} else {
4482546Scarlsonj 		if (mdb_walk("ace", ace_cb, NULL) == -1) {
4492546Scarlsonj 			mdb_warn("cannot walk ace_t structures");
4502546Scarlsonj 			return (DCMD_ERR);
4512546Scarlsonj 		}
4522546Scarlsonj 	}
4532546Scarlsonj 	return (DCMD_OK);
4542546Scarlsonj }
4552546Scarlsonj 
4562546Scarlsonj /*
4572546Scarlsonj  * Print an ARP hardware and protocol address pair; used when printing an ARP
4582546Scarlsonj  * message.
4592546Scarlsonj  */
4602546Scarlsonj static void
4612546Scarlsonj print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
4622546Scarlsonj {
4632546Scarlsonj 	char macstr[ARP_MAX_ADDR_LEN*3];
4642546Scarlsonj 	in_addr_t inaddr;
4652546Scarlsonj 
4662546Scarlsonj 	if (arh->arh_hlen == 0)
4672546Scarlsonj 		(void) strcpy(macstr, "(none)");
4682546Scarlsonj 	else
4692546Scarlsonj 		mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
4702546Scarlsonj 	mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
4712546Scarlsonj 	if (arh->arh_plen == 0) {
4722546Scarlsonj 		mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
4732546Scarlsonj 	} else if (ptype == IP_ARP_PROTO_TYPE) {
4742546Scarlsonj 		mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
4752546Scarlsonj 	} else if (arh->arh_plen == sizeof (in_addr_t)) {
4762546Scarlsonj 		(void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
4772546Scarlsonj 		mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
4782546Scarlsonj 	} else {
4792546Scarlsonj 		mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
4802546Scarlsonj 	}
4812546Scarlsonj }
4822546Scarlsonj 
4832546Scarlsonj /*
4842546Scarlsonj  * Decode an ARP message and display it.
4852546Scarlsonj  */
4862546Scarlsonj /* ARGSUSED2 */
4872546Scarlsonj static int
4882546Scarlsonj arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4892546Scarlsonj {
4902546Scarlsonj 	struct {
4912546Scarlsonj 		arh_t arh;
4922546Scarlsonj 		uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
4932546Scarlsonj 	} arp;
4942546Scarlsonj 	size_t blen;
4952546Scarlsonj 	uint16_t htype, ptype, op;
4962546Scarlsonj 	const char *cp;
4972546Scarlsonj 
4982546Scarlsonj 	if (!(flags & DCMD_ADDRSPEC)) {
4992546Scarlsonj 		mdb_warn("address required to print ARP header\n");
5002546Scarlsonj 		return (DCMD_ERR);
5012546Scarlsonj 	}
5022546Scarlsonj 
5032546Scarlsonj 	if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
5042546Scarlsonj 		mdb_warn("unable to read ARP header at %p", addr);
5052546Scarlsonj 		return (DCMD_ERR);
5062546Scarlsonj 	}
5072546Scarlsonj 	mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
5082546Scarlsonj 	mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
5092546Scarlsonj 	mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));
5102546Scarlsonj 
5112546Scarlsonj 	switch (htype) {
5122546Scarlsonj 	case ARPHRD_ETHER:
5132546Scarlsonj 		cp = "Ether";
5142546Scarlsonj 		break;
5152546Scarlsonj 	case ARPHRD_IEEE802:
5162546Scarlsonj 		cp = "IEEE802";
5172546Scarlsonj 		break;
5182546Scarlsonj 	case ARPHRD_IB:
5192546Scarlsonj 		cp = "InfiniBand";
5202546Scarlsonj 		break;
5212546Scarlsonj 	default:
5222546Scarlsonj 		cp = "Unknown";
5232546Scarlsonj 		break;
5242546Scarlsonj 	}
5252546Scarlsonj 	mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
5262546Scarlsonj 	mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
5272546Scarlsonj 	    ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");
5282546Scarlsonj 
5292546Scarlsonj 	switch (op) {
5302546Scarlsonj 	case ARPOP_REQUEST:
5312546Scarlsonj 		cp = "ares_op$REQUEST";
5322546Scarlsonj 		break;
5332546Scarlsonj 	case ARPOP_REPLY:
5342546Scarlsonj 		cp = "ares_op$REPLY";
5352546Scarlsonj 		break;
5362546Scarlsonj 	case REVARP_REQUEST:
5372546Scarlsonj 		cp = "arev_op$REQUEST";
5382546Scarlsonj 		break;
5392546Scarlsonj 	case REVARP_REPLY:
5402546Scarlsonj 		cp = "arev_op$REPLY";
5412546Scarlsonj 		break;
5422546Scarlsonj 	default:
5432546Scarlsonj 		cp = "Unknown";
5442546Scarlsonj 		break;
5452546Scarlsonj 	}
5462546Scarlsonj 	mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);
5472546Scarlsonj 
5482546Scarlsonj 	/*
5492546Scarlsonj 	 * Note that we go to some length to attempt to print out the fixed
5502546Scarlsonj 	 * header data before trying to decode the variable-length data.  This
5512546Scarlsonj 	 * is done to maximize the amount of useful information shown when the
5522546Scarlsonj 	 * buffer is truncated or otherwise corrupt.
5532546Scarlsonj 	 */
5542546Scarlsonj 	blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
5552546Scarlsonj 	if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
5562546Scarlsonj 		mdb_warn("unable to read ARP body at %p", addr);
5572546Scarlsonj 		return (DCMD_ERR);
5582546Scarlsonj 	}
5592546Scarlsonj 
5602546Scarlsonj 	print_arp('s', arp.addrs, &arp.arh, ptype);
5612546Scarlsonj 	print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
5622546Scarlsonj 	    &arp.arh, ptype);
5632546Scarlsonj 	return (DCMD_OK);
5642546Scarlsonj }
5652546Scarlsonj 
5662546Scarlsonj /*
5672546Scarlsonj  * Print out an arp command formatted in a reasonable manner.  This implements
5682546Scarlsonj  * the type switch used by ARP.
5692546Scarlsonj  *
5702546Scarlsonj  * It could also dump the data that follows the header (using offset and length
5712546Scarlsonj  * in the various structures), but it currently does not.
5722546Scarlsonj  */
5732546Scarlsonj /* ARGSUSED2 */
5742546Scarlsonj static int
5752546Scarlsonj arpcmd_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5762546Scarlsonj {
5772546Scarlsonj 	arc_t arc;
5782546Scarlsonj 	const arp_cmd_tbl *tp;
5792546Scarlsonj 	mdb_arg_t subargv;
5802546Scarlsonj 
5812546Scarlsonj 	if (!(flags & DCMD_ADDRSPEC)) {
5822546Scarlsonj 		mdb_warn("address required to print ARP command\n");
5832546Scarlsonj 		return (DCMD_ERR);
5842546Scarlsonj 	}
5852546Scarlsonj 	if (mdb_vread(&arc, sizeof (arc), addr) == -1) {
5862546Scarlsonj 		mdb_warn("unable to read arc_t at %p", addr);
5872546Scarlsonj 		return (DCMD_ERR);
5882546Scarlsonj 	}
5892546Scarlsonj 	for (tp = act_list; tp->act_cmd != 0; tp++)
5902546Scarlsonj 		if (tp->act_cmd == arc.arc_cmd)
5912546Scarlsonj 			break;
5922546Scarlsonj 	mdb_printf("%p %s (%s) = ", addr, tp->act_name, tp->act_type);
5932546Scarlsonj 	subargv.a_type = MDB_TYPE_STRING;
5942546Scarlsonj 	subargv.a_un.a_str = tp->act_type;
5952546Scarlsonj 	if (mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, &subargv) == -1)
5962546Scarlsonj 		return (DCMD_ERR);
5972546Scarlsonj 	else
5982546Scarlsonj 		return (DCMD_OK);
5992546Scarlsonj }
6002546Scarlsonj 
6012546Scarlsonj static size_t
6022546Scarlsonj mi_osize(const queue_t *q)
6032546Scarlsonj {
6042546Scarlsonj 	/*
6052546Scarlsonj 	 * The code in common/inet/mi.c allocates an extra word to store the
6062546Scarlsonj 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
6072546Scarlsonj 	 */
6082546Scarlsonj 	struct mi_block {
6092546Scarlsonj 		size_t mi_nbytes;
6102546Scarlsonj 		struct mi_o_s mi_o;
6112546Scarlsonj 	} m;
6122546Scarlsonj 
6132546Scarlsonj 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr - sizeof (m)) != -1)
6142546Scarlsonj 		return (m.mi_nbytes - sizeof (m));
6152546Scarlsonj 
6162546Scarlsonj 	return (0);
6172546Scarlsonj }
6182546Scarlsonj 
6192546Scarlsonj /*
6202546Scarlsonj  * This is called when ::stream is used and an ARP module is seen on the
6212546Scarlsonj  * stream.  Determine what sort of ARP usage is involved and show an
6222546Scarlsonj  * appropriate message.
6232546Scarlsonj  */
6242546Scarlsonj static void
6252546Scarlsonj arp_qinfo(const queue_t *qp, char *buf, size_t nbytes)
6262546Scarlsonj {
6272546Scarlsonj 	size_t size = mi_osize(qp);
6282546Scarlsonj 	ar_t ar;
6292546Scarlsonj 
6302546Scarlsonj 	if (size != sizeof (ar_t))
6312546Scarlsonj 		return;
6322546Scarlsonj 	if (mdb_vread(&ar, sizeof (ar), (uintptr_t)qp->q_ptr) == -1)
6332546Scarlsonj 		return;
6342546Scarlsonj 	ar_describe(&ar, buf, nbytes, B_TRUE);
6352546Scarlsonj }
6362546Scarlsonj 
6372546Scarlsonj static uintptr_t
6382546Scarlsonj arp_rnext(const queue_t *q)
6392546Scarlsonj {
6402546Scarlsonj 	size_t size = mi_osize(q);
6412546Scarlsonj 	ar_t ar;
6422546Scarlsonj 
6432546Scarlsonj 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
6442546Scarlsonj 	    (uintptr_t)q->q_ptr) != -1)
6452546Scarlsonj 		return ((uintptr_t)ar.ar_rq);
6462546Scarlsonj 
6472546Scarlsonj 	return (NULL);
6482546Scarlsonj }
6492546Scarlsonj 
6502546Scarlsonj static uintptr_t
6512546Scarlsonj arp_wnext(const queue_t *q)
6522546Scarlsonj {
6532546Scarlsonj 	size_t size = mi_osize(q);
6542546Scarlsonj 	ar_t ar;
6552546Scarlsonj 
6562546Scarlsonj 	if (size == sizeof (ar_t) && mdb_vread(&ar, sizeof (ar),
6572546Scarlsonj 	    (uintptr_t)q->q_ptr) != -1)
6582546Scarlsonj 		return ((uintptr_t)ar.ar_wq);
6592546Scarlsonj 
6602546Scarlsonj 	return (NULL);
6612546Scarlsonj }
6622546Scarlsonj 
6632546Scarlsonj static const mdb_dcmd_t dcmds[] = {
6642546Scarlsonj 	{ "ar", "?", "display ARP client streams", ar_cmd, NULL },
6652546Scarlsonj 	{ "arl", "?", "display ARP link layers", arl_cmd, NULL },
6662546Scarlsonj 	{ "ace", "?", "display ARP cache entries", ace_cmd, NULL },
6672546Scarlsonj 	{ "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
6682546Scarlsonj 	{ "arpcmd", ":", "display an ARP command", arpcmd_cmd, NULL },
6692546Scarlsonj 	{ NULL }
6702546Scarlsonj };
6712546Scarlsonj 
6722546Scarlsonj /* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
6732546Scarlsonj static const mdb_walker_t walkers[] = {
6742546Scarlsonj 	{ "arl", "walk list of arl_t links",
6752546Scarlsonj 	    arl_walk_init, arl_walk_step, NULL },
6762546Scarlsonj 	{ "ace", "walk list of ace_t entries",
6772546Scarlsonj 	    ace_walk_init, ace_walk_step, ace_walk_fini },
6782546Scarlsonj 	{ NULL }
6792546Scarlsonj };
6802546Scarlsonj 
6812546Scarlsonj static const mdb_qops_t arp_qops = { arp_qinfo, arp_rnext, arp_wnext };
6822546Scarlsonj static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
6832546Scarlsonj 
6842546Scarlsonj const mdb_modinfo_t *
6852546Scarlsonj _mdb_init(void)
6862546Scarlsonj {
6872546Scarlsonj 	GElf_Sym sym;
6882546Scarlsonj 
6892546Scarlsonj 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
6902546Scarlsonj 		mdb_qops_install(&arp_qops, (uintptr_t)sym.st_value);
6912546Scarlsonj 
6922546Scarlsonj 	return (&modinfo);
6932546Scarlsonj }
6942546Scarlsonj 
6952546Scarlsonj void
6962546Scarlsonj _mdb_fini(void)
6972546Scarlsonj {
6982546Scarlsonj 	GElf_Sym sym;
6992546Scarlsonj 
7002546Scarlsonj 	if (mdb_lookup_by_obj("arp", "winit", &sym) == 0)
7012546Scarlsonj 		mdb_qops_remove(&arp_qops, (uintptr_t)sym.st_value);
7022546Scarlsonj }
703