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 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #include <sys/stropts.h>
32 #include <sys/sysmacros.h>
33 #include <netinet/in_systm.h>
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 #include <netinet/ip_icmp.h>
37 #include <netinet/udp.h>
38 #include <netinet/tcp.h>
39 #include <netinet/icmp6.h>
40 #include <netinet/ip6.h>
41 #include <inet/ip.h>
42 #include <inet/ip6.h>
43 #include <arpa/inet.h>
44 #include <netdb.h>
45 #include "snoop.h"
46 #include "snoop_mip.h"
47
48 static void interpret_options(char *, int);
49 static void interpret_mldv2qry(icmp6_t *, int);
50 static void interpret_mldv2rpt(icmp6_t *, int);
51
52
53 /* Mobile-IP routines from snoop_mip.c */
54 extern void interpret_icmp_mip_ext(uchar_t *, int);
55 extern const char *get_mip_adv_desc(uint8_t);
56
57 /* Router advertisement message structure. */
58 struct icmp_ra_addr {
59 uint32_t addr;
60 uint32_t preference;
61 };
62
63 /*ARGSUSED*/
64 void
interpret_icmp(int flags,struct icmp * icmp,int iplen,int ilen)65 interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
66 {
67 char *pt, *pc, *px;
68 char *line;
69 char buff[67627]; /* Router adv. can have 256 routers .... */
70 /* Each router has a name 256 char long .. */
71 char extbuff[MAXHOSTNAMELEN + 1];
72 struct udphdr *orig_uhdr;
73 int num_rtr_addrs = 0;
74 extern char *prot_nest_prefix;
75
76 if (ilen < ICMP_MINLEN)
77 return; /* incomplete header */
78
79 pt = "Unknown";
80 pc = "";
81 px = "";
82
83 switch (icmp->icmp_type) {
84 case ICMP_ECHOREPLY:
85 pt = "Echo reply";
86 (void) sprintf(buff, "ID: %d Sequence number: %d",
87 ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
88 pc = buff;
89 break;
90 case ICMP_UNREACH:
91 pt = "Destination unreachable";
92 switch (icmp->icmp_code) {
93 case ICMP_UNREACH_NET:
94 if (ilen >= ICMP_ADVLENMIN) {
95 (void) sprintf(buff, "Net %s unreachable",
96 addrtoname(AF_INET,
97 &icmp->icmp_ip.ip_dst));
98 pc = buff;
99 } else {
100 pc = "Bad net";
101 }
102 break;
103 case ICMP_UNREACH_HOST:
104 if (ilen >= ICMP_ADVLENMIN) {
105 (void) sprintf(buff, "Host %s unreachable",
106 addrtoname(AF_INET,
107 &icmp->icmp_ip.ip_dst));
108 pc = buff;
109 } else {
110 pc = "Bad host";
111 }
112 break;
113 case ICMP_UNREACH_PROTOCOL:
114 if (ilen >= ICMP_ADVLENMIN) {
115 (void) sprintf(buff, "Bad protocol %d",
116 icmp->icmp_ip.ip_p);
117 pc = buff;
118 } else {
119 pc = "Bad protocol";
120 }
121 break;
122 case ICMP_UNREACH_PORT:
123 if (ilen >= ICMP_ADVLENMIN) {
124 orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
125 ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
126 switch (icmp->icmp_ip.ip_p) {
127 case IPPROTO_TCP:
128 (void) sprintf(buff, "TCP port %d"
129 " unreachable",
130 ntohs(orig_uhdr->uh_dport));
131 pc = buff;
132 break;
133 case IPPROTO_UDP:
134 (void) sprintf(buff, "UDP port %d"
135 " unreachable",
136 ntohs(orig_uhdr->uh_dport));
137 pc = buff;
138 break;
139 default:
140 pc = "Port unreachable";
141 break;
142 }
143 } else {
144 pc = "Bad port";
145 }
146 break;
147 case ICMP_UNREACH_NEEDFRAG:
148 if (ntohs(icmp->icmp_nextmtu) != 0) {
149 (void) sprintf(buff, "Needed to fragment:"
150 " next hop MTU = %d",
151 ntohs(icmp->icmp_nextmtu));
152 pc = buff;
153 } else {
154 pc = "Needed to fragment";
155 }
156 break;
157 case ICMP_UNREACH_SRCFAIL:
158 pc = "Source route failed";
159 break;
160 case ICMP_UNREACH_NET_UNKNOWN:
161 pc = "Unknown network";
162 break;
163 case ICMP_UNREACH_HOST_UNKNOWN:
164 pc = "Unknown host";
165 break;
166 case ICMP_UNREACH_ISOLATED:
167 pc = "Source host isolated";
168 break;
169 case ICMP_UNREACH_NET_PROHIB:
170 pc = "Net administratively prohibited";
171 break;
172 case ICMP_UNREACH_HOST_PROHIB:
173 pc = "Host administratively prohibited";
174 break;
175 case ICMP_UNREACH_TOSNET:
176 pc = "Net unreachable for this TOS";
177 break;
178 case ICMP_UNREACH_TOSHOST:
179 pc = "Host unreachable for this TOS";
180 break;
181 case ICMP_UNREACH_FILTER_PROHIB:
182 pc = "Communication administratively prohibited";
183 break;
184 case ICMP_UNREACH_HOST_PRECEDENCE:
185 pc = "Host precedence violation";
186 break;
187 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
188 pc = "Precedence cutoff in effect";
189 break;
190 default:
191 break;
192 }
193 break;
194 case ICMP_SOURCEQUENCH:
195 pt = "Packet lost, slow down";
196 break;
197 case ICMP_REDIRECT:
198 pt = "Redirect";
199 switch (icmp->icmp_code) {
200 case ICMP_REDIRECT_NET:
201 pc = "for network";
202 break;
203 case ICMP_REDIRECT_HOST:
204 pc = "for host";
205 break;
206 case ICMP_REDIRECT_TOSNET:
207 pc = "for tos and net";
208 break;
209 case ICMP_REDIRECT_TOSHOST:
210 pc = "for tos and host";
211 break;
212 default:
213 break;
214 }
215 (void) sprintf(buff, "%s %s to %s",
216 pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
217 addrtoname(AF_INET, &icmp->icmp_gwaddr));
218 pc = buff;
219 break;
220 case ICMP_ECHO:
221 pt = "Echo request";
222 (void) sprintf(buff, "ID: %d Sequence number: %d",
223 ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
224 pc = buff;
225 break;
226 case ICMP_ROUTERADVERT:
227
228 #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
229 #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
230 #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
231
232 pt = "Router advertisement";
233 (void) sprintf(buff, "Lifetime %ds [%d]:",
234 ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
235 if (icmp->icmp_wpa == 2) {
236 struct icmp_ra_addr *ra;
237 char ra_buf[MAXHOSTNAMELEN + 32];
238 char ra_ext_buf[50];
239 struct in_addr sin;
240 int icmp_ra_len;
241 int i;
242
243 /* Cannot trust anything from the network... */
244 num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
245 icmp->icmp_num_addrs);
246
247 ra = (struct icmp_ra_addr *)icmp->icmp_data;
248 for (i = 0; i < num_rtr_addrs; i++) {
249 sin.s_addr = ra->addr;
250 (void) snprintf(ra_buf, sizeof (ra_buf),
251 " {%s %u}",
252 addrtoname(AF_INET, &sin),
253 ntohl(ra->preference));
254 if (strlcat(buff, ra_buf, sizeof (buff)) >=
255 sizeof (buff)) {
256 buff[sizeof (buff) -
257 strlen("<Too Long>)")] = '\0';
258 (void) strlcat(buff, "<Too Long>",
259 sizeof (buff));
260 break;
261 }
262 ra++;
263 }
264
265 icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
266 sizeof (struct icmp_ra_addr);
267 if (ilen > icmp_ra_len) {
268 int curr_len = ilen - icmp_ra_len;
269 int ocurr_len;
270 exthdr_t *exthdr = (exthdr_t *)ra;
271
272 extbuff[0] = '\0';
273
274 while (curr_len > 0) {
275 /* Append Mobile-IP description */
276 (void) snprintf(ra_ext_buf,
277 sizeof (ra_ext_buf), ", %s",
278 get_mip_adv_desc(exthdr->type));
279 (void) strlcat(extbuff, ra_ext_buf,
280 sizeof (extbuff));
281
282 /* Special case for padding */
283 if (exthdr->type ==
284 ICMP_ADV_MSG_PADDING_EXT) {
285
286 curr_len--;
287 exthdr = (exthdr_t *)
288 ((char *)exthdr + 1);
289 continue;
290 }
291
292 /* else normal extension */
293 ocurr_len = curr_len;
294 curr_len -= sizeof (*exthdr) +
295 exthdr->length;
296 /* detect bad length */
297 if (ocurr_len < curr_len)
298 break;
299 exthdr = (exthdr_t *)
300 ((char *)exthdr +
301 sizeof (*exthdr) +
302 exthdr->length);
303 }
304 px = extbuff;
305 }
306 pc = buff;
307 }
308 break;
309 case ICMP_ROUTERSOLICIT:
310 pt = "Router solicitation";
311 break;
312 case ICMP_TIMXCEED:
313 pt = "Time exceeded";
314 switch (icmp->icmp_code) {
315 case ICMP_TIMXCEED_INTRANS:
316 pc = "in transit";
317 break;
318 case ICMP_TIMXCEED_REASS:
319 pc = "in reassembly";
320 break;
321 default:
322 break;
323 }
324 break;
325 case ICMP_PARAMPROB:
326 pt = "IP parameter problem";
327 switch (icmp->icmp_code) {
328 case ICMP_PARAMPROB_OPTABSENT:
329 pc = "Required option missing";
330 break;
331 case ICMP_PARAMPROB_BADLENGTH:
332 pc = "Bad length";
333 break;
334 case 0: /* Should this be the default? */
335 (void) sprintf(buff, "Problem at octet %d\n",
336 icmp->icmp_pptr);
337 pc = buff;
338 default:
339 break;
340 }
341 break;
342 case ICMP_TSTAMP:
343 pt = "Timestamp request";
344 break;
345 case ICMP_TSTAMPREPLY:
346 pt = "Timestamp reply";
347 break;
348 case ICMP_IREQ:
349 pt = "Information request";
350 break;
351 case ICMP_IREQREPLY:
352 pt = "Information reply";
353 break;
354 case ICMP_MASKREQ:
355 pt = "Address mask request";
356 break;
357 case ICMP_MASKREPLY:
358 pt = "Address mask reply";
359 (void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
360 pc = buff;
361 break;
362 default:
363 break;
364 }
365
366 if (flags & F_SUM) {
367 line = get_sum_line();
368 if (*pc) {
369 if (*px) {
370 (void) sprintf(line, "ICMP %s (%s)%s",
371 pt, pc, px);
372 } else {
373 (void) sprintf(line, "ICMP %s (%s)", pt, pc);
374 }
375 } else {
376 (void) sprintf(line, "ICMP %s", pt);
377 }
378 }
379
380 if (flags & F_DTAIL) {
381 show_header("ICMP: ", "ICMP Header", ilen);
382 show_space();
383 (void) sprintf(get_line(0, 0), "Type = %d (%s)",
384 icmp->icmp_type, pt);
385 if (*pc) {
386 (void) sprintf(get_line(0, 0), "Code = %d (%s)",
387 icmp->icmp_code, pc);
388 } else {
389 (void) sprintf(get_line(0, 0), "Code = %d",
390 icmp->icmp_code);
391 }
392 (void) sprintf(get_line(0, 0), "Checksum = %x",
393 ntohs(icmp->icmp_cksum));
394
395 if (icmp->icmp_type == ICMP_UNREACH ||
396 icmp->icmp_type == ICMP_REDIRECT) {
397 if (ilen > 28) {
398 show_space();
399 (void) sprintf(get_line(0, 0),
400 "[ subject header follows ]");
401 show_space();
402 prot_nest_prefix = "ICMP:";
403 (void) interpret_ip(flags,
404 (struct ip *)icmp->icmp_data, 28);
405 prot_nest_prefix = "";
406 }
407 } else if (icmp->icmp_type == ICMP_PARAMPROB) {
408 if (ilen > 28) {
409 show_space();
410 (void) sprintf(get_line(0, 0),
411 "[ subject header follows ]");
412 show_space();
413 prot_nest_prefix = "ICMP:";
414 (void) interpret_ip(flags,
415 (struct ip *)icmp->icmp_data, 28);
416 prot_nest_prefix = "";
417 }
418 } else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
419 if (icmp->icmp_wpa == 2) {
420 int icmp_ra_len;
421
422 show_space();
423 icmp_ra_len = ICMP_MINLEN +
424 num_rtr_addrs *
425 sizeof (struct icmp_ra_addr);
426 prot_nest_prefix = "";
427 if (ilen > icmp_ra_len) {
428 interpret_icmp_mip_ext(
429 (uchar_t *)icmp + icmp_ra_len,
430 ilen - icmp_ra_len);
431 }
432 }
433 }
434 show_space();
435 }
436 }
437
438 /*ARGSUSED*/
439 void
interpret_icmpv6(flags,icmp6,iplen,ilen)440 interpret_icmpv6(flags, icmp6, iplen, ilen)
441 int flags;
442 icmp6_t *icmp6;
443 int iplen, 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 static void
interpret_options(optc,ilen)777 interpret_options(optc, ilen)
778 char *optc;
779 int ilen;
780 {
781 #define PREFIX_OPTION_LENGTH 4
782 #define MTU_OPTION_LENGTH 1
783
784 #define PREFIX_INFINITY 0xffffffffUL
785
786 struct nd_opt_hdr *opt;
787
788 for (; ilen >= sizeof (*opt); ) {
789 opt = (struct nd_opt_hdr *)optc;
790 if (opt->nd_opt_len == 0)
791 return;
792 switch (opt->nd_opt_type) {
793 case ND_OPT_SOURCE_LINKADDR:
794 case ND_OPT_TARGET_LINKADDR:
795 {
796 struct nd_opt_lla *lopt;
797 char *buf, chbuf[128];
798 uint_t addr_len;
799 int i;
800
801 if (ilen < (int)opt->nd_opt_len * 8)
802 break;
803
804 buf = chbuf;
805
806 lopt = (struct nd_opt_lla *)opt;
807 if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
808 (void) sprintf(get_line(0, 0),
809 "+++ ICMPv6 Source LL Addr option +++");
810 } else {
811 (void) sprintf(get_line(0, 0),
812 "+++ ICMPv6 Target LL Addr option +++");
813 }
814
815 /*
816 * The option length is in 8 octet units, and
817 * includes the first two bytes (the type and
818 * lenght fields) of the option.
819 */
820 addr_len = lopt->nd_opt_lla_len * 8 - 2;
821 for (i = 0; i < addr_len; i++) {
822 snprintf(buf, sizeof (chbuf) - (buf - chbuf),
823 "%x:", lopt->nd_opt_lla_hdw_addr[i]);
824 buf += strlen(buf);
825 if (buf >= &chbuf[sizeof (chbuf)]) {
826 buf = NULL;
827 chbuf[sizeof (chbuf) -
828 strlen("<Too Long>)")] = '\0';
829 (void) strlcat(chbuf, "<Too Long>",
830 sizeof (chbuf));
831 break;
832 }
833 }
834 if (buf)
835 *(buf - 1) = '\0'; /* Erase last colon */
836 (void) sprintf(get_line(0, 0),
837 "Link Layer address: %s", chbuf);
838 show_space();
839 break;
840 }
841 case ND_OPT_MTU: {
842 struct nd_opt_mtu *mopt;
843 if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
844 ilen < sizeof (struct nd_opt_mtu))
845 break;
846 (void) sprintf(get_line(0, 0),
847 "+++ ICMPv6 MTU option +++");
848 mopt = (struct nd_opt_mtu *)opt;
849 (void) sprintf(get_line(0, 0),
850 "MTU = %u ", ntohl(mopt->nd_opt_mtu_mtu));
851 show_space();
852 break;
853 }
854 case ND_OPT_PREFIX_INFORMATION: {
855 struct nd_opt_prefix_info *popt;
856 char validstr[30];
857 char preferredstr[30];
858 char prefixstr[INET6_ADDRSTRLEN];
859
860 if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
861 ilen < sizeof (struct nd_opt_prefix_info))
862 break;
863 popt = (struct nd_opt_prefix_info *)opt;
864 (void) sprintf(get_line(0, 0),
865 "+++ ICMPv6 Prefix option +++");
866 (void) sprintf(get_line(0, 0),
867 "Prefix length = %d ", popt->nd_opt_pi_prefix_len);
868 (void) sprintf(get_line(0, 0),
869 "Onlink flag: %s, Autonomous addr conf flag: %s",
870 popt->nd_opt_pi_flags_reserved &
871 ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
872 popt->nd_opt_pi_flags_reserved &
873 ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
874
875 if (ntohl(popt->nd_opt_pi_valid_time) ==
876 PREFIX_INFINITY)
877 sprintf(validstr, "INFINITY");
878 else
879 sprintf(validstr, "%lu",
880 ntohl(popt->nd_opt_pi_valid_time));
881
882 if (ntohl(popt->nd_opt_pi_preferred_time) ==
883 PREFIX_INFINITY)
884 sprintf(preferredstr, "INFINITY");
885 else
886 sprintf(preferredstr, "%lu",
887 ntohl(popt->nd_opt_pi_preferred_time));
888
889 (void) sprintf(get_line(0, 0),
890 "Valid Lifetime %s, Preferred Lifetime %s",
891 validstr, preferredstr);
892 (void) sprintf(get_line(0, 0), "Prefix %s",
893 inet_ntop(AF_INET6,
894 (char *)&popt->nd_opt_pi_prefix, prefixstr,
895 INET6_ADDRSTRLEN));
896 show_space();
897 }
898 default:
899 break;
900 }
901 optc += opt->nd_opt_len * 8;
902 ilen -= opt->nd_opt_len * 8;
903 }
904 }
905
906 static void
interpret_mldv2qry(icmp6_t * icmp6,int ilen)907 interpret_mldv2qry(icmp6_t *icmp6, int ilen)
908 {
909 mld2q_t *qry;
910 in6_addr_t *src;
911 int rem = ilen;
912 int srccnt;
913 char addrstr[INET6_ADDRSTRLEN];
914
915 if (ilen < sizeof (*qry)) {
916 (void) snprintf(get_line(0, 0), get_line_remain(),
917 "Malformed MLD Query");
918 return;
919 }
920 qry = (mld2q_t *)icmp6;
921 rem -= sizeof (*qry);
922 srccnt = ntohs(qry->mld2q_numsrc);
923 (void) snprintf(get_line(0, 0), get_line_remain(),
924 "Multicast address= %s", inet_ntop(AF_INET6,
925 &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
926 (void) snprintf(get_line(0, 0), get_line_remain(),
927 "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
928
929 src = (in6_addr_t *)&qry[1];
930 while (srccnt > 0 && rem >= sizeof (*src)) {
931 rem -= sizeof (*src);
932
933 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
934 inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
935
936 srccnt--;
937 src++;
938 }
939 }
940
941 #define MAX_MLDV2_REPORT_TYPE 6
942
943 const char *mldv2rpt_types[] = {
944 "<unknown>",
945 "MODE_IS_INCLUDE",
946 "MODE_IS_EXCLUDE",
947 "CHANGE_TO_INCLUDE",
948 "CHANGE_TO_EXCLUDE",
949 "ALLOW_NEW_SOURCES",
950 "BLOCK_OLD_SOURCES",
951 };
952
953 static void
interpret_mldv2rpt(icmp6_t * icmp6,int ilen)954 interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
955 {
956 mld2r_t *rpt;
957 mld2mar_t *mar;
958 in6_addr_t *src;
959 int rem = ilen, auxlen;
960 uint16_t marcnt, srccnt;
961 char addrstr[INET6_ADDRSTRLEN];
962
963 if (ilen < sizeof (*rpt)) {
964 (void) snprintf(get_line(0, 0), get_line_remain(),
965 "Malformed MLDv2 Report");
966 return;
967 }
968 rpt = (mld2r_t *)icmp6;
969 mar = (mld2mar_t *)&rpt[1];
970 marcnt = ntohs(rpt->mld2r_nummar);
971 (void) snprintf(get_line(0, 0), get_line_remain(),
972 "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
973 rem -= sizeof (*rpt);
974 while (marcnt > 0 && rem >= sizeof (*mar)) {
975 rem -= sizeof (*mar);
976
977 (void) snprintf(get_line(0, 0), get_line_remain(),
978 "Multicast address= %s type = %s", inet_ntop(AF_INET6,
979 &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
980 (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
981 "<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
982 srccnt = ntohs(mar->mld2mar_numsrc);
983 (void) snprintf(get_line(0, 0), get_line_remain(),
984 "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
985
986 src = (in6_addr_t *)&mar[1];
987 while (srccnt > 0 && rem >= sizeof (*src)) {
988 rem -= sizeof (*src);
989
990 (void) snprintf(get_line(0, 0), get_line_remain(),
991 " %s", inet_ntop(AF_INET6, src, addrstr,
992 INET6_ADDRSTRLEN));
993
994 srccnt--;
995 src++;
996 }
997
998 marcnt--;
999 auxlen = mar->mld2mar_auxlen * 4;
1000 rem -= auxlen;
1001 mar = (mld2mar_t *)((uint8_t *)src + auxlen);
1002 }
1003 }
1004