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
interpret_icmp(int flags,struct icmp * icmp,int iplen,int ilen)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
interpret_icmpv6(int flags,icmp6_t * icmp6,int iplen,int ilen)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
interpret_lifetime(char * buf,uint32_t net_lifetime)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
interpret_options(char * optc,int ilen)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
interpret_mldv2qry(icmp6_t * icmp6,int ilen)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
interpret_mldv2rpt(icmp6_t * icmp6,int ilen)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