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