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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org> 27 */ 28 29 #include <stdio.h> 30 #include <string.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <net/if.h> 34 #include <sys/stropts.h> 35 #include <sys/sysmacros.h> 36 #include <netinet/in_systm.h> 37 #include <netinet/in.h> 38 #include <netinet/ip.h> 39 #include <netinet/ip_icmp.h> 40 #include <netinet/udp.h> 41 #include <netinet/tcp.h> 42 #include <netinet/icmp6.h> 43 #include <netinet/ip6.h> 44 #include <inet/ip.h> 45 #include <inet/ip6.h> 46 #include <arpa/inet.h> 47 #include <netdb.h> 48 #include <resolv.h> 49 #include "snoop.h" 50 #include "snoop_mip.h" 51 52 static void interpret_options(char *, int); 53 static void interpret_mldv2qry(icmp6_t *, int); 54 static void interpret_mldv2rpt(icmp6_t *, int); 55 56 57 /* Mobile-IP routines from snoop_mip.c */ 58 extern void interpret_icmp_mip_ext(uchar_t *, int); 59 extern const char *get_mip_adv_desc(uint8_t); 60 61 /* Router advertisement message structure. */ 62 struct icmp_ra_addr { 63 uint32_t addr; 64 uint32_t preference; 65 }; 66 67 /*ARGSUSED*/ 68 void 69 interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen) 70 { 71 char *pt, *pc, *px; 72 char *line; 73 char buff[67627]; /* Router adv. can have 256 routers .... */ 74 /* Each router has a name 256 char long .. */ 75 char extbuff[MAXHOSTNAMELEN + 1]; 76 struct udphdr *orig_uhdr; 77 int num_rtr_addrs = 0; 78 extern char *prot_nest_prefix; 79 80 if (ilen < ICMP_MINLEN) 81 return; /* incomplete header */ 82 83 pt = "Unknown"; 84 pc = ""; 85 px = ""; 86 87 switch (icmp->icmp_type) { 88 case ICMP_ECHOREPLY: 89 pt = "Echo reply"; 90 (void) sprintf(buff, "ID: %d Sequence number: %d", 91 ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq)); 92 pc = buff; 93 break; 94 case ICMP_UNREACH: 95 pt = "Destination unreachable"; 96 switch (icmp->icmp_code) { 97 case ICMP_UNREACH_NET: 98 if (ilen >= ICMP_ADVLENMIN) { 99 (void) sprintf(buff, "Net %s unreachable", 100 addrtoname(AF_INET, 101 &icmp->icmp_ip.ip_dst)); 102 pc = buff; 103 } else { 104 pc = "Bad net"; 105 } 106 break; 107 case ICMP_UNREACH_HOST: 108 if (ilen >= ICMP_ADVLENMIN) { 109 (void) sprintf(buff, "Host %s unreachable", 110 addrtoname(AF_INET, 111 &icmp->icmp_ip.ip_dst)); 112 pc = buff; 113 } else { 114 pc = "Bad host"; 115 } 116 break; 117 case ICMP_UNREACH_PROTOCOL: 118 if (ilen >= ICMP_ADVLENMIN) { 119 (void) sprintf(buff, "Bad protocol %d", 120 icmp->icmp_ip.ip_p); 121 pc = buff; 122 } else { 123 pc = "Bad protocol"; 124 } 125 break; 126 case ICMP_UNREACH_PORT: 127 if (ilen >= ICMP_ADVLENMIN) { 128 orig_uhdr = (struct udphdr *)((uchar_t *)icmp + 129 ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4); 130 switch (icmp->icmp_ip.ip_p) { 131 case IPPROTO_TCP: 132 (void) sprintf(buff, "TCP port %d" 133 " unreachable", 134 ntohs(orig_uhdr->uh_dport)); 135 pc = buff; 136 break; 137 case IPPROTO_UDP: 138 (void) sprintf(buff, "UDP port %d" 139 " unreachable", 140 ntohs(orig_uhdr->uh_dport)); 141 pc = buff; 142 break; 143 default: 144 pc = "Port unreachable"; 145 break; 146 } 147 } else { 148 pc = "Bad port"; 149 } 150 break; 151 case ICMP_UNREACH_NEEDFRAG: 152 if (ntohs(icmp->icmp_nextmtu) != 0) { 153 (void) sprintf(buff, "Needed to fragment:" 154 " next hop MTU = %d", 155 ntohs(icmp->icmp_nextmtu)); 156 pc = buff; 157 } else { 158 pc = "Needed to fragment"; 159 } 160 break; 161 case ICMP_UNREACH_SRCFAIL: 162 pc = "Source route failed"; 163 break; 164 case ICMP_UNREACH_NET_UNKNOWN: 165 pc = "Unknown network"; 166 break; 167 case ICMP_UNREACH_HOST_UNKNOWN: 168 pc = "Unknown host"; 169 break; 170 case ICMP_UNREACH_ISOLATED: 171 pc = "Source host isolated"; 172 break; 173 case ICMP_UNREACH_NET_PROHIB: 174 pc = "Net administratively prohibited"; 175 break; 176 case ICMP_UNREACH_HOST_PROHIB: 177 pc = "Host administratively prohibited"; 178 break; 179 case ICMP_UNREACH_TOSNET: 180 pc = "Net unreachable for this TOS"; 181 break; 182 case ICMP_UNREACH_TOSHOST: 183 pc = "Host unreachable for this TOS"; 184 break; 185 case ICMP_UNREACH_FILTER_PROHIB: 186 pc = "Communication administratively prohibited"; 187 break; 188 case ICMP_UNREACH_HOST_PRECEDENCE: 189 pc = "Host precedence violation"; 190 break; 191 case ICMP_UNREACH_PRECEDENCE_CUTOFF: 192 pc = "Precedence cutoff in effect"; 193 break; 194 default: 195 break; 196 } 197 break; 198 case ICMP_SOURCEQUENCH: 199 pt = "Packet lost, slow down"; 200 break; 201 case ICMP_REDIRECT: 202 pt = "Redirect"; 203 switch (icmp->icmp_code) { 204 case ICMP_REDIRECT_NET: 205 pc = "for network"; 206 break; 207 case ICMP_REDIRECT_HOST: 208 pc = "for host"; 209 break; 210 case ICMP_REDIRECT_TOSNET: 211 pc = "for tos and net"; 212 break; 213 case ICMP_REDIRECT_TOSHOST: 214 pc = "for tos and host"; 215 break; 216 default: 217 break; 218 } 219 (void) sprintf(buff, "%s %s to %s", 220 pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst), 221 addrtoname(AF_INET, &icmp->icmp_gwaddr)); 222 pc = buff; 223 break; 224 case ICMP_ECHO: 225 pt = "Echo request"; 226 (void) sprintf(buff, "ID: %d Sequence number: %d", 227 ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq)); 228 pc = buff; 229 break; 230 case ICMP_ROUTERADVERT: 231 232 #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs 233 #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa 234 #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime 235 236 pt = "Router advertisement"; 237 (void) sprintf(buff, "Lifetime %ds [%d]:", 238 ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs); 239 if (icmp->icmp_wpa == 2) { 240 struct icmp_ra_addr *ra; 241 char ra_buf[MAXHOSTNAMELEN + 32]; 242 char ra_ext_buf[50]; 243 struct in_addr sin; 244 int icmp_ra_len; 245 int i; 246 247 /* Cannot trust anything from the network... */ 248 num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8, 249 icmp->icmp_num_addrs); 250 251 ra = (struct icmp_ra_addr *)icmp->icmp_data; 252 for (i = 0; i < num_rtr_addrs; i++) { 253 sin.s_addr = ra->addr; 254 (void) snprintf(ra_buf, sizeof (ra_buf), 255 " {%s %u}", 256 addrtoname(AF_INET, &sin), 257 ntohl(ra->preference)); 258 if (strlcat(buff, ra_buf, sizeof (buff)) >= 259 sizeof (buff)) { 260 buff[sizeof (buff) - 261 strlen("<Too Long>)")] = '\0'; 262 (void) strlcat(buff, "<Too Long>", 263 sizeof (buff)); 264 break; 265 } 266 ra++; 267 } 268 269 icmp_ra_len = ICMP_MINLEN + num_rtr_addrs * 270 sizeof (struct icmp_ra_addr); 271 if (ilen > icmp_ra_len) { 272 int curr_len = ilen - icmp_ra_len; 273 int ocurr_len; 274 exthdr_t *exthdr = (exthdr_t *)ra; 275 276 extbuff[0] = '\0'; 277 278 while (curr_len > 0) { 279 /* Append Mobile-IP description */ 280 (void) snprintf(ra_ext_buf, 281 sizeof (ra_ext_buf), ", %s", 282 get_mip_adv_desc(exthdr->type)); 283 (void) strlcat(extbuff, ra_ext_buf, 284 sizeof (extbuff)); 285 286 /* Special case for padding */ 287 if (exthdr->type == 288 ICMP_ADV_MSG_PADDING_EXT) { 289 290 curr_len--; 291 exthdr = (exthdr_t *) 292 ((char *)exthdr + 1); 293 continue; 294 } 295 296 /* else normal extension */ 297 ocurr_len = curr_len; 298 curr_len -= sizeof (*exthdr) + 299 exthdr->length; 300 /* detect bad length */ 301 if (ocurr_len < curr_len) 302 break; 303 exthdr = (exthdr_t *) 304 ((char *)exthdr + sizeof (*exthdr) + 305 exthdr->length); 306 } 307 px = extbuff; 308 } 309 pc = buff; 310 } 311 break; 312 case ICMP_ROUTERSOLICIT: 313 pt = "Router solicitation"; 314 break; 315 case ICMP_TIMXCEED: 316 pt = "Time exceeded"; 317 switch (icmp->icmp_code) { 318 case ICMP_TIMXCEED_INTRANS: 319 pc = "in transit"; 320 break; 321 case ICMP_TIMXCEED_REASS: 322 pc = "in reassembly"; 323 break; 324 default: 325 break; 326 } 327 break; 328 case ICMP_PARAMPROB: 329 pt = "IP parameter problem"; 330 switch (icmp->icmp_code) { 331 case ICMP_PARAMPROB_OPTABSENT: 332 pc = "Required option missing"; 333 break; 334 case ICMP_PARAMPROB_BADLENGTH: 335 pc = "Bad length"; 336 break; 337 case 0: /* Should this be the default? */ 338 (void) sprintf(buff, "Problem at octet %d\n", 339 icmp->icmp_pptr); 340 pc = buff; 341 default: 342 break; 343 } 344 break; 345 case ICMP_TSTAMP: 346 pt = "Timestamp request"; 347 break; 348 case ICMP_TSTAMPREPLY: 349 pt = "Timestamp reply"; 350 break; 351 case ICMP_IREQ: 352 pt = "Information request"; 353 break; 354 case ICMP_IREQREPLY: 355 pt = "Information reply"; 356 break; 357 case ICMP_MASKREQ: 358 pt = "Address mask request"; 359 break; 360 case ICMP_MASKREPLY: 361 pt = "Address mask reply"; 362 (void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask)); 363 pc = buff; 364 break; 365 default: 366 break; 367 } 368 369 if (flags & F_SUM) { 370 line = get_sum_line(); 371 if (*pc) { 372 if (*px) { 373 (void) sprintf(line, "ICMP %s (%s)%s", 374 pt, pc, px); 375 } else { 376 (void) sprintf(line, "ICMP %s (%s)", pt, pc); 377 } 378 } else { 379 (void) sprintf(line, "ICMP %s", pt); 380 } 381 } 382 383 if (flags & F_DTAIL) { 384 show_header("ICMP: ", "ICMP Header", ilen); 385 show_space(); 386 (void) sprintf(get_line(0, 0), "Type = %d (%s)", 387 icmp->icmp_type, pt); 388 if (*pc) { 389 (void) sprintf(get_line(0, 0), "Code = %d (%s)", 390 icmp->icmp_code, pc); 391 } else { 392 (void) sprintf(get_line(0, 0), "Code = %d", 393 icmp->icmp_code); 394 } 395 (void) sprintf(get_line(0, 0), "Checksum = %x", 396 ntohs(icmp->icmp_cksum)); 397 398 if (icmp->icmp_type == ICMP_UNREACH || 399 icmp->icmp_type == ICMP_REDIRECT) { 400 if (ilen > 28) { 401 show_space(); 402 (void) sprintf(get_line(0, 0), 403 "[ subject header follows ]"); 404 show_space(); 405 prot_nest_prefix = "ICMP:"; 406 (void) interpret_ip(flags, 407 (struct ip *)icmp->icmp_data, 28); 408 prot_nest_prefix = ""; 409 } 410 } else if (icmp->icmp_type == ICMP_PARAMPROB) { 411 if (ilen > 28) { 412 show_space(); 413 (void) sprintf(get_line(0, 0), 414 "[ subject header follows ]"); 415 show_space(); 416 prot_nest_prefix = "ICMP:"; 417 (void) interpret_ip(flags, 418 (struct ip *)icmp->icmp_data, 28); 419 prot_nest_prefix = ""; 420 } 421 } else if (icmp->icmp_type == ICMP_ROUTERADVERT) { 422 if (icmp->icmp_wpa == 2) { 423 int icmp_ra_len; 424 425 show_space(); 426 icmp_ra_len = ICMP_MINLEN + 427 num_rtr_addrs * 428 sizeof (struct icmp_ra_addr); 429 prot_nest_prefix = ""; 430 if (ilen > icmp_ra_len) { 431 interpret_icmp_mip_ext( 432 (uchar_t *)icmp + icmp_ra_len, 433 ilen - icmp_ra_len); 434 } 435 } 436 } 437 show_space(); 438 } 439 } 440 441 /*ARGSUSED*/ 442 void 443 interpret_icmpv6(int flags, icmp6_t *icmp6, int iplen, int ilen) 444 { 445 char *pt, *pc; 446 char *line; 447 extern char *prot_nest_prefix; 448 char addrstr[INET6_ADDRSTRLEN]; 449 char buff[2048]; 450 451 if (ilen < ICMP6_MINLEN) 452 return; /* incomplete header */ 453 454 pt = "Unknown"; 455 pc = ""; 456 457 switch (icmp6->icmp6_type) { 458 case ICMP6_DST_UNREACH: 459 pt = "Destination unreachable"; 460 switch (icmp6->icmp6_code) { 461 case ICMP6_DST_UNREACH_NOROUTE: 462 pc = "No route to destination"; 463 break; 464 case ICMP6_DST_UNREACH_ADMIN: 465 pc = "Communication administratively prohibited"; 466 break; 467 case ICMP6_DST_UNREACH_ADDR: 468 pc = "Address unreachable"; 469 break; 470 case ICMP6_DST_UNREACH_NOPORT: 471 if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN + 472 sizeof (struct udphdr)) { 473 474 ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1]; 475 476 switch (orig_ip6hdr->ip6_nxt) { 477 case IPPROTO_TCP: { 478 struct tcphdr *orig_thdr = 479 (struct tcphdr *)&orig_ip6hdr[1]; 480 481 (void) sprintf(buff, "TCP port %hu" 482 " unreachable", 483 ntohs(orig_thdr->th_dport)); 484 pc = buff; 485 break; 486 } 487 case IPPROTO_UDP: { 488 struct udphdr *orig_uhdr = 489 (struct udphdr *)&orig_ip6hdr[1]; 490 491 (void) sprintf(buff, "UDP port %hu" 492 " unreachable", 493 ntohs(orig_uhdr->uh_dport)); 494 pc = buff; 495 break; 496 } 497 default: 498 pc = "Port unreachable"; 499 break; 500 } 501 } else { 502 pc = "Bad port"; 503 } 504 break; 505 default: 506 break; 507 } 508 break; 509 case ICMP6_PACKET_TOO_BIG: 510 pt = "Packet too big"; 511 break; 512 case ND_REDIRECT: 513 pt = "Redirect"; 514 break; 515 case ICMP6_TIME_EXCEEDED: 516 pt = "Time exceeded"; 517 switch (icmp6->icmp6_code) { 518 case ICMP6_TIME_EXCEED_TRANSIT: 519 pc = "Hop limit exceeded in transit"; 520 break; 521 case ICMP6_TIME_EXCEED_REASSEMBLY: 522 pc = "Fragment reassembly time exceeded"; 523 break; 524 default: 525 break; 526 } 527 break; 528 case ICMP6_PARAM_PROB: 529 pt = "Parameter problem"; 530 switch (icmp6->icmp6_code) { 531 case ICMP6_PARAMPROB_HEADER: 532 pc = "Erroneous header field"; 533 break; 534 case ICMP6_PARAMPROB_NEXTHEADER: 535 pc = "Unrecognized next header type"; 536 break; 537 case ICMP6_PARAMPROB_OPTION: 538 pc = "Unrecognized IPv6 option"; 539 break; 540 } 541 break; 542 case ICMP6_ECHO_REQUEST: 543 pt = "Echo request"; 544 (void) sprintf(buff, "ID: %d Sequence number: %d", 545 ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq)); 546 pc = buff; 547 break; 548 case ICMP6_ECHO_REPLY: 549 pt = "Echo reply"; 550 (void) sprintf(buff, "ID: %d Sequence number: %d", 551 ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq)); 552 pc = buff; 553 break; 554 case MLD_LISTENER_QUERY: 555 if (ilen == MLD_MINLEN) 556 pt = "Group membership query - MLDv1"; 557 else if (ilen >= MLD_V2_QUERY_MINLEN) 558 pt = "Group membership query - MLDv2"; 559 else 560 pt = "Unknown membership query"; 561 break; 562 case MLD_LISTENER_REPORT: 563 pt = "Group membership report - MLDv1"; 564 break; 565 case MLD_LISTENER_REDUCTION: 566 pt = "Group membership termination - MLDv1"; 567 break; 568 case MLD_V2_LISTENER_REPORT: 569 pt = "Group membership report - MLDv2"; 570 break; 571 case ND_ROUTER_SOLICIT: 572 pt = "Router solicitation"; 573 break; 574 case ND_ROUTER_ADVERT: 575 pt = "Router advertisement"; 576 break; 577 case ND_NEIGHBOR_SOLICIT: 578 pt = "Neighbor solicitation"; 579 break; 580 case ND_NEIGHBOR_ADVERT: 581 pt = "Neighbor advertisement"; 582 break; 583 default: 584 break; 585 } 586 587 if (flags & F_SUM) { 588 line = get_sum_line(); 589 if (*pc) 590 (void) sprintf(line, "ICMPv6 %s (%s)", pt, pc); 591 else 592 (void) sprintf(line, "ICMPv6 %s", pt); 593 } 594 595 if (flags & F_DTAIL) { 596 show_header("ICMPv6: ", "ICMPv6 Header", ilen); 597 show_space(); 598 (void) sprintf(get_line(0, 0), "Type = %d (%s)", 599 icmp6->icmp6_type, pt); 600 if (*pc) 601 (void) sprintf(get_line(0, 0), "Code = %d (%s)", 602 icmp6->icmp6_code, pc); 603 else 604 (void) sprintf(get_line(0, 0), "Code = %d", 605 icmp6->icmp6_code); 606 (void) sprintf(get_line(0, 0), "Checksum = %x", 607 ntohs(icmp6->icmp6_cksum)); 608 609 switch (icmp6->icmp6_type) { 610 case ICMP6_DST_UNREACH: 611 if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) { 612 show_space(); 613 (void) sprintf(get_line(0, 0), 614 "[ subject header follows ]"); 615 show_space(); 616 prot_nest_prefix = "ICMPv6:"; 617 (void) interpret_ipv6(flags, (ip6_t *)&icmp6[1], 618 ICMP6_MINLEN + IPV6_HDR_LEN); 619 prot_nest_prefix = ""; 620 } 621 break; 622 case ICMP6_PACKET_TOO_BIG: 623 show_space(); 624 (void) sprintf(get_line(0, 0), 625 " Packet too big MTU = %d", 626 ntohl(icmp6->icmp6_mtu)); 627 show_space(); 628 break; 629 case ND_REDIRECT: { 630 nd_redirect_t *rd = (nd_redirect_t *)icmp6; 631 632 (void) sprintf(get_line(0, 0), "Target address= %s", 633 inet_ntop(AF_INET6, (char *)&rd->nd_rd_target, 634 addrstr, INET6_ADDRSTRLEN)); 635 636 (void) sprintf(get_line(0, 0), 637 "Destination address= %s", 638 inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst, 639 addrstr, INET6_ADDRSTRLEN)); 640 show_space(); 641 interpret_options((char *)icmp6 + sizeof (*rd), 642 ilen - sizeof (*rd)); 643 break; 644 } 645 case ND_NEIGHBOR_SOLICIT: { 646 struct nd_neighbor_solicit *ns; 647 if (ilen < sizeof (*ns)) 648 break; 649 ns = (struct nd_neighbor_solicit *)icmp6; 650 (void) sprintf(get_line(0, 0), "Target node = %s, %s", 651 inet_ntop(AF_INET6, (char *)&ns->nd_ns_target, 652 addrstr, INET6_ADDRSTRLEN), 653 addrtoname(AF_INET6, &ns->nd_ns_target)); 654 show_space(); 655 interpret_options((char *)icmp6 + sizeof (*ns), 656 ilen - sizeof (*ns)); 657 break; 658 } 659 660 case ND_NEIGHBOR_ADVERT: { 661 struct nd_neighbor_advert *na; 662 663 if (ilen < sizeof (*na)) 664 break; 665 na = (struct nd_neighbor_advert *)icmp6; 666 (void) sprintf(get_line(0, 0), "Target node = %s, %s", 667 inet_ntop(AF_INET6, (char *)&na->nd_na_target, 668 addrstr, INET6_ADDRSTRLEN), 669 addrtoname(AF_INET6, &na->nd_na_target)); 670 (void) sprintf(get_line(0, 0), 671 "Router flag: %s, Solicited flag: %s, " 672 "Override flag: %s", 673 na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ? 674 "SET" : "NOT SET", 675 na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ? 676 "SET" : "NOT SET", 677 na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ? 678 "SET" : "NOT SET"); 679 680 show_space(); 681 interpret_options((char *)icmp6 + sizeof (*na), 682 ilen - sizeof (*na)); 683 } 684 break; 685 686 case ND_ROUTER_SOLICIT: { 687 if (ilen < sizeof (struct nd_router_solicit)) 688 break; 689 interpret_options( 690 (char *)icmp6 + sizeof (struct nd_router_solicit), 691 ilen - sizeof (struct nd_router_solicit)); 692 break; 693 } 694 695 case ND_ROUTER_ADVERT: { 696 struct nd_router_advert *ra; 697 698 if (ilen < sizeof (*ra)) 699 break; 700 ra = (struct nd_router_advert *)icmp6; 701 (void) sprintf(get_line(0, 0), 702 "Max hops= %d, Router lifetime= %d", 703 ra->nd_ra_curhoplimit, 704 ntohs(ra->nd_ra_router_lifetime)); 705 706 (void) sprintf(get_line(0, 0), 707 "Managed addr conf flag: %s, Other conf flag: %s", 708 ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ? 709 "SET" : "NOT SET", 710 ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ? 711 "SET" : "NOT SET"); 712 713 (void) sprintf(get_line(0, 0), 714 "Reachable time: %u, Reachable retrans time %u", 715 ntohl(ra->nd_ra_reachable), 716 ntohl(ra->nd_ra_retransmit)); 717 show_space(); 718 719 interpret_options((char *)icmp6 + sizeof (*ra), 720 ilen - sizeof (*ra)); 721 break; 722 } 723 case ICMP6_PARAM_PROB: 724 if (ilen < sizeof (*icmp6)) 725 break; 726 (void) sprintf(get_line(0, 0), "Ptr = %u", 727 ntohl(icmp6->icmp6_pptr)); 728 show_space(); 729 break; 730 731 case MLD_LISTENER_QUERY: { 732 struct mld_hdr *mldg = (struct mld_hdr *)icmp6; 733 734 if (ilen < MLD_MINLEN) 735 break; 736 737 if (ilen >= MLD_V2_QUERY_MINLEN) { 738 interpret_mldv2qry(icmp6, ilen); 739 } else { 740 (void) snprintf(get_line(0, 0), 741 get_line_remain(), 742 "Multicast address= %s", 743 inet_ntop(AF_INET6, mldg->mld_addr.s6_addr, 744 addrstr, INET6_ADDRSTRLEN)); 745 } 746 show_space(); 747 break; 748 } 749 750 case MLD_LISTENER_REPORT: 751 case MLD_LISTENER_REDUCTION: { 752 struct mld_hdr *mldg; 753 754 if (ilen < sizeof (*mldg)) 755 break; 756 mldg = (struct mld_hdr *)icmp6; 757 (void) snprintf(get_line(0, 0), get_line_remain(), 758 "Multicast address= %s", inet_ntop(AF_INET6, 759 mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN)); 760 show_space(); 761 break; 762 } 763 764 case MLD_V2_LISTENER_REPORT: { 765 interpret_mldv2rpt(icmp6, ilen); 766 show_space(); 767 break; 768 } 769 770 default: 771 break; 772 } 773 } 774 } 775 776 #define LIFETIME_INFINITY 0xffffffffUL 777 778 static void 779 interpret_lifetime(char *buf, uint32_t net_lifetime) 780 { 781 uint32_t lifetime = ntohl(net_lifetime); 782 783 if (lifetime == 0) { 784 sprintf(buf, "INVALID"); 785 return; 786 } 787 if (lifetime == LIFETIME_INFINITY) { 788 sprintf(buf, "INFINITY"); 789 return; 790 } 791 sprintf(buf, "%lu", lifetime); 792 } 793 794 static void 795 interpret_options(char *optc, int ilen) 796 { 797 #define PREFIX_OPTION_LENGTH 4 798 #define MTU_OPTION_LENGTH 1 799 800 struct nd_opt_hdr *opt; 801 802 for (; ilen >= sizeof (*opt); ) { 803 opt = (struct nd_opt_hdr *)optc; 804 if (opt->nd_opt_len == 0) 805 return; 806 switch (opt->nd_opt_type) { 807 case ND_OPT_SOURCE_LINKADDR: 808 case ND_OPT_TARGET_LINKADDR: 809 { 810 struct nd_opt_lla *lopt; 811 char *buf, chbuf[128]; 812 uint_t addr_len; 813 int i; 814 815 if (ilen < (int)opt->nd_opt_len * 8) 816 break; 817 818 buf = chbuf; 819 820 lopt = (struct nd_opt_lla *)opt; 821 if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) { 822 (void) sprintf(get_line(0, 0), 823 "+++ ICMPv6 Source LL Addr option +++"); 824 } else { 825 (void) sprintf(get_line(0, 0), 826 "+++ ICMPv6 Target LL Addr option +++"); 827 } 828 829 /* 830 * The option length is in 8 octet units, and 831 * includes the first two bytes (the type and 832 * lenght fields) of the option. 833 */ 834 addr_len = lopt->nd_opt_lla_len * 8 - 2; 835 for (i = 0; i < addr_len; i++) { 836 snprintf(buf, sizeof (chbuf) - (buf - chbuf), 837 "%x:", lopt->nd_opt_lla_hdw_addr[i]); 838 buf += strlen(buf); 839 if (buf >= &chbuf[sizeof (chbuf)]) { 840 buf = NULL; 841 chbuf[sizeof (chbuf) - 842 strlen("<Too Long>)")] = '\0'; 843 (void) strlcat(chbuf, "<Too Long>", 844 sizeof (chbuf)); 845 break; 846 } 847 } 848 if (buf) 849 *(buf - 1) = '\0'; /* Erase last colon */ 850 (void) sprintf(get_line(0, 0), 851 "Link Layer address: %s", chbuf); 852 show_space(); 853 break; 854 } 855 case ND_OPT_MTU: { 856 struct nd_opt_mtu *mopt; 857 if (opt->nd_opt_len != MTU_OPTION_LENGTH || 858 ilen < sizeof (struct nd_opt_mtu)) 859 break; 860 (void) sprintf(get_line(0, 0), 861 "+++ ICMPv6 MTU option +++"); 862 mopt = (struct nd_opt_mtu *)opt; 863 (void) sprintf(get_line(0, 0), 864 "MTU = %u ", ntohl(mopt->nd_opt_mtu_mtu)); 865 show_space(); 866 break; 867 } 868 case ND_OPT_PREFIX_INFORMATION: { 869 struct nd_opt_prefix_info *popt; 870 char validstr[30]; 871 char preferredstr[30]; 872 char prefixstr[INET6_ADDRSTRLEN]; 873 874 if (opt->nd_opt_len != PREFIX_OPTION_LENGTH || 875 ilen < sizeof (struct nd_opt_prefix_info)) 876 break; 877 popt = (struct nd_opt_prefix_info *)opt; 878 (void) sprintf(get_line(0, 0), 879 "+++ ICMPv6 Prefix option +++"); 880 (void) sprintf(get_line(0, 0), 881 "Prefix length = %d ", popt->nd_opt_pi_prefix_len); 882 (void) sprintf(get_line(0, 0), 883 "Onlink flag: %s, Autonomous addr conf flag: %s", 884 popt->nd_opt_pi_flags_reserved & 885 ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET", 886 popt->nd_opt_pi_flags_reserved & 887 ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET"); 888 889 interpret_lifetime(validstr, 890 popt->nd_opt_pi_valid_time); 891 interpret_lifetime(preferredstr, 892 popt->nd_opt_pi_preferred_time); 893 894 (void) sprintf(get_line(0, 0), 895 "Valid Lifetime %s, Preferred Lifetime %s", 896 validstr, preferredstr); 897 (void) sprintf(get_line(0, 0), "Prefix %s", 898 inet_ntop(AF_INET6, 899 (char *)&popt->nd_opt_pi_prefix, prefixstr, 900 INET6_ADDRSTRLEN)); 901 show_space(); 902 break; 903 } 904 case ND_OPT_DNS_RESOLVER: { 905 char addrstr[INET6_ADDRSTRLEN]; 906 char lifestr[30]; 907 int i, naddr; 908 struct nd_opt_dns_resolver *optr = 909 (struct nd_opt_dns_resolver *)optc; 910 911 if (opt->nd_opt_len < 3 || ilen < opt->nd_opt_len * 8) 912 break; 913 914 (void) sprintf(get_line(0, 0), 915 "+++ ICMPv6 Recursive DNS Server option +++"); 916 917 interpret_lifetime(lifestr, optr->nd_opt_dnsr_lifetime); 918 (void) sprintf(get_line(0, 0), "Lifetime %s", lifestr); 919 920 naddr = (opt->nd_opt_len - 1) / 2; 921 922 for (i = 0; i < naddr; i++) { 923 const char *ns = inet_ntop(AF_INET6, 924 &optr->nd_opt_dnsr_addr[i], 925 addrstr, 926 INET6_ADDRSTRLEN); 927 sprintf(get_line(0, 0), "Nameserver %s", ns); 928 } 929 show_space(); 930 break; 931 } 932 case ND_OPT_DNS_SEARCHLIST: { 933 struct nd_opt_dns_sl *opts = 934 (struct nd_opt_dns_sl *)optc; 935 char lifestr[30]; 936 uchar_t *msg, *namep, *end; 937 938 (void) sprintf(get_line(0, 0), 939 "+++ ICMPv6 DNS Search List option +++"); 940 interpret_lifetime(lifestr, opts->nd_opt_dnss_lifetime); 941 (void) sprintf(get_line(0, 0), "Lifetime %s", lifestr); 942 943 msg = &opts->nd_opt_dnss_names[0]; 944 end = (uint8_t *)(optc + opt->nd_opt_len * 8); 945 namep = msg; 946 947 /* 948 * Names are encoded in DNS wire format and then 949 * padded with zero bytes to the end of the option. 950 * dn_expand() returns the length of the 951 * wire-format name so the parser can advance 952 * to the next name in the message, or -1 on failure. 953 * 954 * The only 1-byte encoded DNS name is '.' (the root), 955 * which is meaningless in a DNS search path. 956 * It is encoded as a single zero byte, so if we 957 * see it we can quit parsing. 958 */ 959 while (namep < end) { 960 char namebuf[256]; 961 962 int count = dn_expand(msg, end, namep, 963 namebuf, sizeof (namebuf)); 964 965 if (count <= 1) 966 break; 967 968 (void) sprintf(get_line(0, 0), 969 "Name: %s", namebuf); 970 namep += count; 971 } 972 show_space(); 973 break; 974 } 975 default: 976 break; 977 } 978 optc += opt->nd_opt_len * 8; 979 ilen -= opt->nd_opt_len * 8; 980 } 981 } 982 983 static void 984 interpret_mldv2qry(icmp6_t *icmp6, int ilen) 985 { 986 mld2q_t *qry; 987 in6_addr_t *src; 988 int rem = ilen; 989 int srccnt; 990 char addrstr[INET6_ADDRSTRLEN]; 991 992 if (ilen < sizeof (*qry)) { 993 (void) snprintf(get_line(0, 0), get_line_remain(), 994 "Malformed MLD Query"); 995 return; 996 } 997 qry = (mld2q_t *)icmp6; 998 rem -= sizeof (*qry); 999 srccnt = ntohs(qry->mld2q_numsrc); 1000 (void) snprintf(get_line(0, 0), get_line_remain(), 1001 "Multicast address= %s", inet_ntop(AF_INET6, 1002 &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN)); 1003 (void) snprintf(get_line(0, 0), get_line_remain(), 1004 "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es"); 1005 1006 src = (in6_addr_t *)&qry[1]; 1007 while (srccnt > 0 && rem >= sizeof (*src)) { 1008 rem -= sizeof (*src); 1009 1010 (void) snprintf(get_line(0, 0), get_line_remain(), " %s", 1011 inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN)); 1012 1013 srccnt--; 1014 src++; 1015 } 1016 } 1017 1018 #define MAX_MLDV2_REPORT_TYPE 6 1019 1020 const char *mldv2rpt_types[] = { 1021 "<unknown>", 1022 "MODE_IS_INCLUDE", 1023 "MODE_IS_EXCLUDE", 1024 "CHANGE_TO_INCLUDE", 1025 "CHANGE_TO_EXCLUDE", 1026 "ALLOW_NEW_SOURCES", 1027 "BLOCK_OLD_SOURCES", 1028 }; 1029 1030 static void 1031 interpret_mldv2rpt(icmp6_t *icmp6, int ilen) 1032 { 1033 mld2r_t *rpt; 1034 mld2mar_t *mar; 1035 in6_addr_t *src; 1036 int rem = ilen, auxlen; 1037 uint16_t marcnt, srccnt; 1038 char addrstr[INET6_ADDRSTRLEN]; 1039 1040 if (ilen < sizeof (*rpt)) { 1041 (void) snprintf(get_line(0, 0), get_line_remain(), 1042 "Malformed MLDv2 Report"); 1043 return; 1044 } 1045 rpt = (mld2r_t *)icmp6; 1046 mar = (mld2mar_t *)&rpt[1]; 1047 marcnt = ntohs(rpt->mld2r_nummar); 1048 (void) snprintf(get_line(0, 0), get_line_remain(), 1049 "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s"); 1050 rem -= sizeof (*rpt); 1051 while (marcnt > 0 && rem >= sizeof (*mar)) { 1052 rem -= sizeof (*mar); 1053 1054 (void) snprintf(get_line(0, 0), get_line_remain(), 1055 "Multicast address= %s type = %s", inet_ntop(AF_INET6, 1056 &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN), 1057 (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ? 1058 "<unknown>" : mldv2rpt_types[mar->mld2mar_type]); 1059 srccnt = ntohs(mar->mld2mar_numsrc); 1060 (void) snprintf(get_line(0, 0), get_line_remain(), 1061 "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es"); 1062 1063 src = (in6_addr_t *)&mar[1]; 1064 while (srccnt > 0 && rem >= sizeof (*src)) { 1065 rem -= sizeof (*src); 1066 1067 (void) snprintf(get_line(0, 0), get_line_remain(), 1068 " %s", inet_ntop(AF_INET6, src, addrstr, 1069 INET6_ADDRSTRLEN)); 1070 1071 srccnt--; 1072 src++; 1073 } 1074 1075 marcnt--; 1076 auxlen = mar->mld2mar_auxlen * 4; 1077 rem -= auxlen; 1078 mar = (mld2mar_t *)((uint8_t *)src + auxlen); 1079 } 1080 } 1081