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