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