1 /* $OpenBSD: print-icmp6.c,v 1.25 2022/12/28 21:30:19 jmc Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that: (1) source code distributions
9 * retain the above copyright notice and this paragraph in its entirety, (2)
10 * distributions including binary code include the above copyright notice and
11 * this paragraph in its entirety in the documentation or other materials
12 * provided with the distribution, and (3) all advertising materials mentioning
13 * features or use of this software display the following acknowledgement:
14 * ``This product includes software developed by the University of California,
15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16 * the University nor the names of its contributors may be used to endorse
17 * or promote products derived from this software without specific prior
18 * written permission.
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 */
23
24 #include <ctype.h>
25
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29
30 #include <net/if.h>
31
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <netinet/ip.h>
35 #include <netinet/ip_icmp.h>
36 #include <netinet/ip_var.h>
37 #include <netinet/udp.h>
38 #include <netinet/udp_var.h>
39 #include <netinet/tcp.h>
40
41 #include <arpa/inet.h>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <netinet/ip6.h>
48 #include <netinet/icmp6.h>
49 #include <netinet6/mld6.h>
50
51 #include "interface.h"
52 #include "addrtoname.h"
53 #include "extract.h"
54
55 void icmp6_opt_print(const u_char *, int);
56 void mld6_print(const u_char *);
57 void mldv2_query_print(const u_char *, u_int);
58 void mldv2_report_print(const u_char *, u_int);
59
60 /* mldv2 report types */
61 static struct tok mldv2report2str[] = {
62 { 1, "is_in" },
63 { 2, "is_ex" },
64 { 3, "to_in" },
65 { 4, "to_ex" },
66 { 5, "allow" },
67 { 6, "block" },
68 { 0, NULL }
69 };
70
71 #define MLDV2_QUERY_QRV 24
72 #define MLDV2_QUERY_QQIC 25
73 #define MLDV2_QUERY_NSRCS 26
74 #define MLDV2_QUERY_SRC0 28
75
76 #define MLDV2_QUERY_QRV_SFLAG (1 << 3)
77
78 #define MLD_V1_QUERY_MINLEN 24
79
80 #define MLDV2_REPORT_GROUP0 8
81
82 #define MLDV2_REPORT_MINLEN 8
83 #define MLDV2_REPORT_MINGRPLEN 20
84
85 #define MLDV2_RGROUP_NSRCS 2
86 #define MLDV2_RGROUP_MADDR 4
87
88 #define MLDV2_MRC_FLOAT (1 << 15)
89 #define MLDV2_MRD(mant, exp) ((mant | 0x1000) << (exp + 3))
90
91 #define MLDV2_QQIC_FLOAT (1 << 7)
92 #define MLDV2_QQI(mant, exp) ((mant | 0x10) << (exp + 3))
93
94 static int
icmp6_cksum(const struct ip6_hdr * ip6,const struct icmp6_hdr * icmp6,u_int len)95 icmp6_cksum(const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6,
96 u_int len)
97 {
98 union {
99 struct {
100 struct in6_addr ph_src;
101 struct in6_addr ph_dst;
102 u_int32_t ph_len;
103 u_int8_t ph_zero[3];
104 u_int8_t ph_nxt;
105 } ph;
106 u_int16_t pa[20];
107 } phu;
108 size_t i;
109 u_int32_t sum = 0;
110
111 /* pseudo-header */
112 memset(&phu, 0, sizeof(phu));
113 phu.ph.ph_src = ip6->ip6_src;
114 phu.ph.ph_dst = ip6->ip6_dst;
115 phu.ph.ph_len = htonl(len);
116 phu.ph.ph_nxt = IPPROTO_ICMPV6;
117
118 for (i = 0; i < sizeof(phu.pa) / sizeof(phu.pa[0]); i++)
119 sum += phu.pa[i];
120
121 return in_cksum((u_short *)icmp6, len, sum);
122 }
123
124 void
icmp6_print(const u_char * bp,u_int length,const u_char * bp2)125 icmp6_print(const u_char *bp, u_int length, const u_char *bp2)
126 {
127 const struct icmp6_hdr *dp;
128 const struct ip6_hdr *ip;
129 const struct ip6_hdr *oip;
130 const struct udphdr *ouh;
131 int hlen, dport;
132 const u_char *ep;
133 int icmp6len;
134
135 #if 0
136 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
137 #endif
138
139 dp = (struct icmp6_hdr *)bp;
140 ip = (struct ip6_hdr *)bp2;
141 oip = (struct ip6_hdr *)(dp + 1);
142 /* 'ep' points to the end of available data. */
143 ep = snapend;
144 if (ip->ip6_plen)
145 icmp6len = (ntohs(ip->ip6_plen) + sizeof(struct ip6_hdr) -
146 (bp - bp2));
147 else /* XXX: jumbo payload case... */
148 icmp6len = snapend - bp;
149
150 #if 0
151 printf("%s > %s: ",
152 ip6addr_string(&ip->ip6_src),
153 ip6addr_string(&ip->ip6_dst));
154 #endif
155
156 TCHECK(dp->icmp6_code);
157 switch (dp->icmp6_type) {
158 case ICMP6_DST_UNREACH:
159 TCHECK(oip->ip6_dst);
160 switch (dp->icmp6_code) {
161 case ICMP6_DST_UNREACH_NOROUTE:
162 printf("icmp6: %s unreachable route",
163 ip6addr_string(&oip->ip6_dst));
164 break;
165 case ICMP6_DST_UNREACH_ADMIN:
166 printf("icmp6: %s unreachable prohibited",
167 ip6addr_string(&oip->ip6_dst));
168 break;
169 #ifdef ICMP6_DST_UNREACH_BEYONDSCOPE
170 case ICMP6_DST_UNREACH_BEYONDSCOPE:
171 #else
172 case ICMP6_DST_UNREACH_NOTNEIGHBOR:
173 #endif
174 printf("icmp6: %s beyond scope of source address %s",
175 ip6addr_string(&oip->ip6_dst),
176 ip6addr_string(&oip->ip6_src));
177 break;
178 case ICMP6_DST_UNREACH_ADDR:
179 printf("icmp6: %s unreachable address",
180 ip6addr_string(&oip->ip6_dst));
181 break;
182 case ICMP6_DST_UNREACH_NOPORT:
183 TCHECK(oip->ip6_nxt);
184 hlen = sizeof(struct ip6_hdr);
185 ouh = (struct udphdr *)(((u_char *)oip) + hlen);
186 TCHECK(ouh->uh_dport);
187 dport = ntohs(ouh->uh_dport);
188 switch (oip->ip6_nxt) {
189 case IPPROTO_TCP:
190 printf("icmp6: %s tcp port %s unreachable",
191 ip6addr_string(&oip->ip6_dst),
192 tcpport_string(dport));
193 break;
194 case IPPROTO_UDP:
195 printf("icmp6: %s udp port %s unreachable",
196 ip6addr_string(&oip->ip6_dst),
197 udpport_string(dport));
198 break;
199 default:
200 printf("icmp6: %s protocol %d port %d unreachable",
201 ip6addr_string(&oip->ip6_dst),
202 oip->ip6_nxt, dport);
203 break;
204 }
205 break;
206 default:
207 printf("icmp6: %s unreachable code-#%d",
208 ip6addr_string(&oip->ip6_dst),
209 dp->icmp6_code);
210 break;
211 }
212 break;
213 case ICMP6_PACKET_TOO_BIG:
214 TCHECK(dp->icmp6_mtu);
215 printf("icmp6: too big %u", (u_int32_t)ntohl(dp->icmp6_mtu));
216 break;
217 case ICMP6_TIME_EXCEEDED:
218 TCHECK(oip->ip6_dst);
219 switch (dp->icmp6_code) {
220 case ICMP6_TIME_EXCEED_TRANSIT:
221 printf("icmp6: time exceeded in-transit for %s",
222 ip6addr_string(&oip->ip6_dst));
223 break;
224 case ICMP6_TIME_EXCEED_REASSEMBLY:
225 printf("icmp6: ip6 reassembly time exceeded");
226 break;
227 default:
228 printf("icmp6: time exceeded code-#%d",
229 dp->icmp6_code);
230 break;
231 }
232 break;
233 case ICMP6_PARAM_PROB:
234 TCHECK(oip->ip6_dst);
235 switch (dp->icmp6_code) {
236 case ICMP6_PARAMPROB_HEADER:
237 printf("icmp6: parameter problem erroneous - octet %u",
238 (u_int32_t)ntohl(dp->icmp6_pptr));
239 break;
240 case ICMP6_PARAMPROB_NEXTHEADER:
241 printf("icmp6: parameter problem next header - octet %u",
242 (u_int32_t)ntohl(dp->icmp6_pptr));
243 break;
244 case ICMP6_PARAMPROB_OPTION:
245 printf("icmp6: parameter problem option - octet %u",
246 (u_int32_t)ntohl(dp->icmp6_pptr));
247 break;
248 default:
249 printf("icmp6: parameter problem code-#%d",
250 dp->icmp6_code);
251 break;
252 }
253 break;
254 case ICMP6_ECHO_REQUEST:
255 case ICMP6_ECHO_REPLY:
256 printf("icmp6: echo %s", dp->icmp6_type == ICMP6_ECHO_REQUEST ?
257 "request" : "reply");
258 if (vflag) {
259 TCHECK(dp->icmp6_seq);
260 printf(" (id:%04x seq:%u)",
261 ntohs(dp->icmp6_id), ntohs(dp->icmp6_seq));
262 }
263 break;
264 case ICMP6_MEMBERSHIP_QUERY:
265 printf("icmp6: multicast listener query ");
266 if (length == MLD_V1_QUERY_MINLEN) {
267 mld6_print((const u_char *)dp);
268 } else if (length >= MLD_V2_QUERY_MINLEN) {
269 printf("v2 ");
270 mldv2_query_print((const u_char *)dp, length);
271 } else {
272 printf("unknown-version (len %u) ", length);
273 }
274 break;
275 case ICMP6_MEMBERSHIP_REPORT:
276 printf("icmp6: multicast listener report ");
277 mld6_print((const u_char *)dp);
278 break;
279 case ICMP6_MEMBERSHIP_REDUCTION:
280 printf("icmp6: multicast listener done ");
281 mld6_print((const u_char *)dp);
282 break;
283 case ND_ROUTER_SOLICIT:
284 printf("icmp6: router solicitation ");
285 if (vflag) {
286 #define RTSOLLEN 8
287 icmp6_opt_print((const u_char *)dp + RTSOLLEN,
288 icmp6len - RTSOLLEN);
289 }
290 break;
291 case ND_ROUTER_ADVERT:
292 printf("icmp6: router advertisement");
293 if (vflag) {
294 struct nd_router_advert *p;
295
296 p = (struct nd_router_advert *)dp;
297 TCHECK(p->nd_ra_retransmit);
298 printf("(chlim=%d, ", (int)p->nd_ra_curhoplimit);
299 if (p->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
300 printf("M");
301 if (p->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
302 printf("O");
303 if (p->nd_ra_flags_reserved &
304 (ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER))
305 printf(", ");
306 switch (p->nd_ra_flags_reserved
307 & ND_RA_FLAG_RTPREF_MASK) {
308 case ND_RA_FLAG_RTPREF_HIGH:
309 printf("pref=high, ");
310 break;
311 case ND_RA_FLAG_RTPREF_MEDIUM:
312 printf("pref=medium, ");
313 break;
314 case ND_RA_FLAG_RTPREF_LOW:
315 printf("pref=low, ");
316 break;
317 case ND_RA_FLAG_RTPREF_RSV:
318 printf("pref=rsv, ");
319 break;
320 }
321 printf("router_ltime=%d, ",
322 ntohs(p->nd_ra_router_lifetime));
323 printf("reachable_time=%u, ",
324 (u_int32_t)ntohl(p->nd_ra_reachable));
325 printf("retrans_time=%u)",
326 (u_int32_t)ntohl(p->nd_ra_retransmit));
327 #define RTADVLEN 16
328 icmp6_opt_print((const u_char *)dp + RTADVLEN,
329 icmp6len - RTADVLEN);
330 }
331 break;
332 case ND_NEIGHBOR_SOLICIT:
333 {
334 struct nd_neighbor_solicit *p;
335 p = (struct nd_neighbor_solicit *)dp;
336 TCHECK(p->nd_ns_target);
337 printf("icmp6: neighbor sol: who has %s",
338 ip6addr_string(&p->nd_ns_target));
339 if (vflag) {
340 #define NDSOLLEN 24
341 icmp6_opt_print((const u_char *)dp + NDSOLLEN,
342 icmp6len - NDSOLLEN);
343 }
344 }
345 break;
346 case ND_NEIGHBOR_ADVERT:
347 {
348 struct nd_neighbor_advert *p;
349
350 p = (struct nd_neighbor_advert *)dp;
351 TCHECK(p->nd_na_target);
352 printf("icmp6: neighbor adv: tgt is %s",
353 ip6addr_string(&p->nd_na_target));
354 if (vflag) {
355 #define ND_NA_FLAG_ALL \
356 (ND_NA_FLAG_ROUTER|ND_NA_FLAG_SOLICITED|ND_NA_FLAG_OVERRIDE)
357 /* we don't need ntohl() here. see advanced-api-04. */
358 if (p->nd_na_flags_reserved & ND_NA_FLAG_ALL) {
359 #undef ND_NA_FLAG_ALL
360 u_int32_t flags;
361
362 flags = p->nd_na_flags_reserved;
363 printf("(");
364 if (flags & ND_NA_FLAG_ROUTER)
365 printf("R");
366 if (flags & ND_NA_FLAG_SOLICITED)
367 printf("S");
368 if (flags & ND_NA_FLAG_OVERRIDE)
369 printf("O");
370 printf(")");
371 }
372 #define NDADVLEN 24
373 icmp6_opt_print((const u_char *)dp + NDADVLEN,
374 icmp6len - NDADVLEN);
375 }
376 }
377 break;
378 case ND_REDIRECT:
379 {
380 #define RDR(i) ((struct nd_redirect *)(i))
381 char tgtbuf[INET6_ADDRSTRLEN], dstbuf[INET6_ADDRSTRLEN];
382
383 TCHECK(RDR(dp)->nd_rd_dst);
384 inet_ntop(AF_INET6, &RDR(dp)->nd_rd_target,
385 tgtbuf, INET6_ADDRSTRLEN);
386 inet_ntop(AF_INET6, &RDR(dp)->nd_rd_dst,
387 dstbuf, INET6_ADDRSTRLEN);
388 printf("icmp6: redirect %s to %s", dstbuf, tgtbuf);
389 #define REDIRECTLEN 40
390 if (vflag) {
391 icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
392 icmp6len - REDIRECTLEN);
393 }
394 break;
395 }
396 case ICMP6_ROUTER_RENUMBERING:
397 switch (dp->icmp6_code) {
398 case ICMP6_ROUTER_RENUMBERING_COMMAND:
399 printf("icmp6: router renum command");
400 break;
401 case ICMP6_ROUTER_RENUMBERING_RESULT:
402 printf("icmp6: router renum result");
403 break;
404 default:
405 printf("icmp6: router renum code-#%d", dp->icmp6_code);
406 break;
407 }
408 break;
409 #ifdef ICMP6_WRUREQUEST
410 case ICMP6_WRUREQUEST: /*ICMP6_FQDN_QUERY*/
411 {
412 int siz;
413 siz = ep - (u_char *)(dp + 1);
414 if (siz == 4)
415 printf("icmp6: who-are-you request");
416 else {
417 printf("icmp6: FQDN request");
418 if (vflag) {
419 if (siz < 8)
420 printf("?(icmp6_data %d bytes)", siz);
421 else if (8 < siz)
422 printf("?(extra %d bytes)", siz - 8);
423 }
424 }
425 break;
426 }
427 #endif /*ICMP6_WRUREQUEST*/
428 #ifdef ICMP6_WRUREPLY
429 case ICMP6_WRUREPLY: /*ICMP6_FQDN_REPLY*/
430 {
431 enum { UNKNOWN, WRU, FQDN } mode = UNKNOWN;
432 u_char const *buf;
433 u_char const *cp = NULL;
434
435 buf = (u_char *)(dp + 1);
436
437 /* fair guess */
438 if (buf[12] == ep - buf - 13)
439 mode = FQDN;
440 else if (dp->icmp6_code == 1)
441 mode = FQDN;
442
443 /* wild guess */
444 if (mode == UNKNOWN) {
445 cp = buf + 4;
446 while (cp < ep) {
447 if (!isprint(*cp++))
448 mode = FQDN;
449 }
450 }
451 if (mode == UNKNOWN && 2 < labs(buf[12] - (ep - buf - 13)))
452 mode = WRU;
453 if (mode == UNKNOWN)
454 mode = FQDN;
455
456 if (mode == WRU) {
457 cp = buf + 4;
458 printf("icmp6: who-are-you reply(\"");
459 } else if (mode == FQDN) {
460 cp = buf + 13;
461 printf("icmp6: FQDN reply(\"");
462 }
463 for (; cp < ep; cp++)
464 printf((isprint(*cp) ? "%c" : "\\%03o"), *cp);
465 printf("\"");
466 if (vflag) {
467 printf(",%s", mode == FQDN ? "FQDN" : "WRU");
468 if (mode == FQDN) {
469 int ttl;
470 ttl = (int)ntohl(*(u_int32_t *)&buf[8]);
471 if (dp->icmp6_code == 1)
472 printf(",TTL=unknown");
473 else if (ttl < 0)
474 printf(",TTL=%d:invalid", ttl);
475 else
476 printf(",TTL=%d", ttl);
477 if (buf[12] != ep - buf - 13) {
478 printf(",invalid namelen:%d/%u",
479 buf[12],
480 (unsigned int)(ep - buf - 13));
481 }
482 }
483 }
484 printf(")");
485 break;
486 }
487 #endif /*ICMP6_WRUREPLY*/
488 case MLDV2_LISTENER_REPORT:
489 printf("multicast listener report v2");
490 mldv2_report_print((const u_char *) dp, length);
491 break;
492 default:
493 printf("icmp6: type-#%d", dp->icmp6_type);
494 break;
495 }
496 if (vflag) {
497 if (TTEST2(dp->icmp6_type, length)) {
498 u_int16_t sum, icmp6_sum;
499 sum = icmp6_cksum(ip, dp, length);
500 if (sum != 0) {
501 icmp6_sum = EXTRACT_16BITS(&dp->icmp6_cksum);
502 printf(" [bad icmp6 cksum %x! -> %x]", icmp6_sum,
503 in_cksum_shouldbe(icmp6_sum, sum));
504 } else
505 printf(" [icmp6 cksum ok]");
506 }
507 }
508 return;
509 trunc:
510 printf("[|icmp6]");
511 #if 0
512 #undef TCHECK
513 #endif
514 }
515
516 void
icmp6_opt_print(const u_char * bp,int resid)517 icmp6_opt_print(const u_char *bp, int resid)
518 {
519 const struct nd_opt_hdr *op;
520 const struct nd_opt_hdr *opl; /* why there's no struct? */
521 const struct nd_opt_prefix_info *opp;
522 const struct nd_opt_mtu *opm;
523 const struct nd_opt_rdnss *oprd;
524 const struct nd_opt_route_info *opri;
525 const u_char *ep;
526 const struct in6_addr *in6p;
527 struct in6_addr in6;
528 int i, opts_len;
529 #if 0
530 const struct ip6_hdr *ip;
531 const char *str;
532 const struct ip6_hdr *oip;
533 const struct udphdr *ouh;
534 int hlen, dport;
535 char buf[256];
536 #endif
537
538 #if 0
539 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
540 #endif
541 #define ECHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) return
542
543 op = (struct nd_opt_hdr *)bp;
544 #if 0
545 ip = (struct ip6_hdr *)bp2;
546 oip = &dp->icmp6_ip6;
547 #endif
548 /* 'ep' points to the end of available data. */
549 ep = snapend;
550
551 ECHECK(op->nd_opt_len);
552 if (resid <= 0)
553 return;
554 if (op->nd_opt_len == 0)
555 goto trunc;
556 if (bp + (op->nd_opt_len << 3) > ep)
557 goto trunc;
558 switch (op->nd_opt_type) {
559 case ND_OPT_SOURCE_LINKADDR:
560 opl = (struct nd_opt_hdr *)op;
561 #if 1
562 if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
563 goto trunc;
564 #else
565 TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
566 #endif
567 printf("(src lladdr: %s",
568 etheraddr_string((u_char *)(opl + 1)));
569 if (opl->nd_opt_len != 1)
570 printf("!");
571 printf(")");
572 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
573 resid - (op->nd_opt_len << 3));
574 break;
575 case ND_OPT_TARGET_LINKADDR:
576 opl = (struct nd_opt_hdr *)op;
577 #if 1
578 if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
579 goto trunc;
580 #else
581 TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
582 #endif
583 printf("(tgt lladdr: %s",
584 etheraddr_string((u_char *)(opl + 1)));
585 if (opl->nd_opt_len != 1)
586 printf("!");
587 printf(")");
588 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
589 resid - (op->nd_opt_len << 3));
590 break;
591 case ND_OPT_PREFIX_INFORMATION:
592 opp = (struct nd_opt_prefix_info *)op;
593 TCHECK(opp->nd_opt_pi_prefix);
594 printf("(prefix info: ");
595 if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
596 printf("L");
597 if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)
598 printf("A");
599 if (opp->nd_opt_pi_flags_reserved)
600 printf(" ");
601 printf("valid_ltime=");
602 if ((u_int32_t)ntohl(opp->nd_opt_pi_valid_time) == ~0U)
603 printf("infinity");
604 else {
605 printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_valid_time));
606 }
607 printf(", ");
608 printf("preferred_ltime=");
609 if ((u_int32_t)ntohl(opp->nd_opt_pi_preferred_time) == ~0U)
610 printf("infinity");
611 else {
612 printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_preferred_time));
613 }
614 printf(", ");
615 printf("prefix=%s/%d", ip6addr_string(&opp->nd_opt_pi_prefix),
616 opp->nd_opt_pi_prefix_len);
617 if (opp->nd_opt_pi_len != 4)
618 printf("!");
619 printf(")");
620 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
621 resid - (op->nd_opt_len << 3));
622 break;
623 case ND_OPT_REDIRECTED_HEADER:
624 printf("(redirect)");
625 /* xxx */
626 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
627 resid - (op->nd_opt_len << 3));
628 break;
629 case ND_OPT_MTU:
630 opm = (struct nd_opt_mtu *)op;
631 TCHECK(opm->nd_opt_mtu_mtu);
632 printf("(mtu: ");
633 printf("mtu=%u", (u_int32_t)ntohl(opm->nd_opt_mtu_mtu));
634 if (opm->nd_opt_mtu_len != 1)
635 printf("!");
636 printf(")");
637 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
638 resid - (op->nd_opt_len << 3));
639 break;
640 case ND_OPT_ROUTE_INFO:
641 opri = (struct nd_opt_route_info *)op;
642 TCHECK(opri->nd_opt_rti_lifetime);
643 printf("(route-info: ");
644 memset(&in6, 0, sizeof(in6));
645 in6p = (const struct in6_addr *)(opri + 1);
646 switch (op->nd_opt_len) {
647 case 1:
648 break;
649 case 2:
650 TCHECK2(*in6p, 8);
651 memcpy(&in6, opri + 1, 8);
652 break;
653 case 3:
654 TCHECK(*in6p);
655 memcpy(&in6, opri + 1, sizeof(in6));
656 break;
657 default:
658 goto trunc;
659 }
660 printf("%s/%u, ", ip6addr_string(&in6),
661 opri->nd_opt_rti_prefixlen);
662 switch (opri->nd_opt_rti_flags & ND_RA_FLAG_RTPREF_MASK) {
663 case ND_RA_FLAG_RTPREF_HIGH:
664 printf("pref=high, ");
665 break;
666 case ND_RA_FLAG_RTPREF_MEDIUM:
667 printf("pref=medium, ");
668 break;
669 case ND_RA_FLAG_RTPREF_LOW:
670 printf("pref=low, ");
671 break;
672 case ND_RA_FLAG_RTPREF_RSV:
673 printf("pref=rsv, ");
674 break;
675 }
676 printf("lifetime=%us)",
677 (u_int32_t)ntohl(opri->nd_opt_rti_lifetime));
678 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
679 resid - (op->nd_opt_len << 3));
680 break;
681 case ND_OPT_RDNSS:
682 oprd = (const struct nd_opt_rdnss *)op;
683 printf("(rdnss: ");
684 TCHECK(oprd->nd_opt_rdnss_lifetime);
685 printf("lifetime=%us",
686 (u_int32_t)ntohl(oprd->nd_opt_rdnss_lifetime));
687 if (oprd->nd_opt_rdnss_len < 3) {
688 printf("!");
689 } else for (i = 0; i < ((oprd->nd_opt_rdnss_len - 1) / 2); i++) {
690 struct in6_addr *addr = (struct in6_addr *)(oprd + 1) + i;
691 TCHECK2(*addr, sizeof(struct in6_addr));
692 printf(", addr=%s", ip6addr_string(addr));
693 }
694 printf(")");
695 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
696 resid - (op->nd_opt_len << 3));
697 break;
698 case ND_OPT_DNSSL:
699 printf("(dnssl: opt_len=%d)", op->nd_opt_len);
700 /* XXX */
701 icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
702 resid - (op->nd_opt_len << 3));
703 break;
704 default:
705 opts_len = op->nd_opt_len;
706 printf("(unknown opt_type=%d, opt_len=%d)",
707 op->nd_opt_type, opts_len);
708 if (opts_len == 0)
709 opts_len = 1; /* XXX */
710 icmp6_opt_print((const u_char *)op + (opts_len << 3),
711 resid - (opts_len << 3));
712 break;
713 }
714 return;
715 trunc:
716 printf("[ndp opt]");
717 return;
718 #if 0
719 #undef TCHECK
720 #endif
721 #undef ECHECK
722 }
723
724 void
mld6_print(const u_char * bp)725 mld6_print(const u_char *bp)
726 {
727 struct mld_hdr *mp = (struct mld_hdr *)bp;
728 const u_char *ep;
729
730 /* 'ep' points to the end of available data. */
731 ep = snapend;
732
733 if ((u_char *)mp + sizeof(*mp) > ep)
734 return;
735
736 printf("max resp delay: %d ", ntohs(mp->mld_maxdelay));
737 printf("addr: %s", ip6addr_string(&mp->mld_addr));
738
739 return;
740 }
741
742 void
mldv2_report_print(const u_char * bp,u_int len)743 mldv2_report_print(const u_char *bp, u_int len)
744 {
745 struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
746 u_int group, nsrcs, ngroups;
747 u_int i, j;
748
749 if (len < MLDV2_REPORT_MINLEN) {
750 printf(" [invalid len %d]", len);
751 return;
752 }
753
754 TCHECK(icp->icmp6_data16[1]);
755 ngroups = ntohs(icp->icmp6_data16[1]);
756 printf(", %d group record(s)", ngroups);
757 if (vflag > 0) {
758 /* Print the group records */
759 group = MLDV2_REPORT_GROUP0;
760 for (i = 0; i < ngroups; i++) {
761 /* type(1) + auxlen(1) + numsrc(2) + grp(16) */
762 if (len < group + MLDV2_REPORT_MINGRPLEN) {
763 printf(" [invalid number of groups]");
764 return;
765 }
766 TCHECK2(bp[group + MLDV2_RGROUP_MADDR],
767 sizeof(struct in6_addr));
768 printf(" [gaddr %s",
769 ip6addr_string(&bp[group + MLDV2_RGROUP_MADDR]));
770 printf(" %s", tok2str(mldv2report2str,
771 " [v2-report-#%d]", bp[group]));
772 nsrcs = (bp[group + MLDV2_RGROUP_NSRCS] << 8) +
773 bp[group + MLDV2_RGROUP_NSRCS + 1];
774 /* Check the number of sources and print them */
775 if (len < group + MLDV2_REPORT_MINGRPLEN +
776 (nsrcs * sizeof(struct in6_addr))) {
777 printf(" [invalid number of sources %d]", nsrcs);
778 return;
779 }
780 if (vflag == 1)
781 printf(", %d source(s)", nsrcs);
782 else {
783 /* Print the sources */
784 printf(" {");
785 for (j = 0; j < nsrcs; j++) {
786 TCHECK2(bp[group +
787 MLDV2_REPORT_MINGRPLEN +
788 j * sizeof(struct in6_addr)],
789 sizeof(struct in6_addr));
790 printf(" %s", ip6addr_string(&bp[group +
791 MLDV2_REPORT_MINGRPLEN + j *
792 sizeof(struct in6_addr)]));
793 }
794 printf(" }");
795 }
796 /* Next group record */
797 group += MLDV2_REPORT_MINGRPLEN + nsrcs *
798 sizeof(struct in6_addr);
799 printf("]");
800 }
801 }
802 return;
803 trunc:
804 printf("[|icmp6]");
805 return;
806 }
807
808 void
mldv2_query_print(const u_char * bp,u_int len)809 mldv2_query_print(const u_char *bp, u_int len)
810 {
811 struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
812 u_int mrc, qqic;
813 int mrd, qqi;
814 int mant, exp;
815 u_int nsrcs;
816 u_int i;
817
818 if (len < MLD_V2_QUERY_MINLEN) {
819 printf(" [invalid len %d]", len);
820 return;
821 }
822 TCHECK(icp->icmp6_data16[0]);
823 mrc = ntohs(icp->icmp6_data16[0]);
824 if (mrc & MLDV2_MRC_FLOAT) {
825 mant = MLD_MRC_MANT(mrc);
826 exp = MLD_MRC_EXP(mrc);
827 mrd = MLDV2_MRD(mant, exp);
828 } else {
829 mrd = mrc;
830 }
831 if (vflag) {
832 printf(" [max resp delay=%d]", mrd);
833 }
834 TCHECK2(bp[8], sizeof(struct in6_addr));
835 printf(" [gaddr %s", ip6addr_string(&bp[8]));
836
837 if (vflag) {
838 TCHECK(bp[MLDV2_QUERY_QQIC]);
839 if (bp[MLDV2_QUERY_QRV] & MLDV2_QUERY_QRV_SFLAG) {
840 printf(" sflag");
841 }
842 if (MLD_QRV(bp[MLDV2_QUERY_QRV])) {
843 printf(" robustness=%d", MLD_QRV(bp[MLDV2_QUERY_QRV]));
844 }
845 qqic = bp[MLDV2_QUERY_QQIC];
846 if (qqic & MLDV2_QQIC_FLOAT) {
847 mant = MLD_QQIC_MANT(qqic);
848 exp = MLD_QQIC_EXP(qqic);
849 qqi = MLDV2_QQI(mant, exp);
850 } else {
851 qqi = bp[MLDV2_QUERY_QQIC];
852 }
853 printf(" qqi=%d", qqi);
854 }
855
856 TCHECK2(bp[MLDV2_QUERY_NSRCS], 2);
857 nsrcs = ntohs(*(u_short *)&bp[MLDV2_QUERY_NSRCS]);
858 if (nsrcs > 0) {
859 if (len < MLD_V2_QUERY_MINLEN + nsrcs * sizeof(struct in6_addr))
860 printf(" [invalid number of sources]");
861 else if (vflag > 1) {
862 printf(" {");
863 for (i = 0; i < nsrcs; i++) {
864 TCHECK2(bp[MLDV2_QUERY_SRC0 + i *
865 sizeof(struct in6_addr)],
866 sizeof(struct in6_addr));
867 printf(" %s",
868 ip6addr_string(&bp[MLDV2_QUERY_SRC0 + i *
869 sizeof(struct in6_addr)]));
870 }
871 printf(" }");
872 } else
873 printf(", %d source(s)", nsrcs);
874 }
875 printf("]");
876 return;
877 trunc:
878 printf("[|icmp6]");
879 return;
880 }
881