xref: /openbsd-src/usr.sbin/tcpdump/print-icmp6.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /*	$OpenBSD: print-icmp6.c,v 1.22 2018/10/22 16:12:45 kn 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         (void)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, ", ntohs(p->nd_ra_router_lifetime));
325 			printf("reachable_time=%u, ",
326 				(u_int32_t)ntohl(p->nd_ra_reachable));
327 			printf("retrans_time=%u)",
328 				(u_int32_t)ntohl(p->nd_ra_retransmit));
329 #define RTADVLEN 16
330 		        icmp6_opt_print((const u_char *)dp + RTADVLEN,
331 					icmp6len - RTADVLEN);
332 		}
333 		break;
334 	case ND_NEIGHBOR_SOLICIT:
335 	    {
336 		struct nd_neighbor_solicit *p;
337 		p = (struct nd_neighbor_solicit *)dp;
338 		TCHECK(p->nd_ns_target);
339 		printf("icmp6: neighbor sol: who has %s",
340 			ip6addr_string(&p->nd_ns_target));
341 		if (vflag) {
342 #define NDSOLLEN 24
343 		        icmp6_opt_print((const u_char *)dp + NDSOLLEN,
344 					icmp6len - NDSOLLEN);
345 		}
346 	    }
347 		break;
348 	case ND_NEIGHBOR_ADVERT:
349 	    {
350 		struct nd_neighbor_advert *p;
351 
352 		p = (struct nd_neighbor_advert *)dp;
353 		TCHECK(p->nd_na_target);
354 		printf("icmp6: neighbor adv: tgt is %s",
355 			ip6addr_string(&p->nd_na_target));
356                 if (vflag) {
357 #define ND_NA_FLAG_ALL	\
358 	(ND_NA_FLAG_ROUTER|ND_NA_FLAG_SOLICITED|ND_NA_FLAG_OVERRIDE)
359 			/* we don't need ntohl() here.  see advanced-api-04. */
360 			if (p->nd_na_flags_reserved &  ND_NA_FLAG_ALL) {
361 #undef ND_NA_FLAG_ALL
362 				u_int32_t flags;
363 
364 				flags = p->nd_na_flags_reserved;
365 				printf("(");
366 				if (flags & ND_NA_FLAG_ROUTER)
367 					printf("R");
368 				if (flags & ND_NA_FLAG_SOLICITED)
369 					printf("S");
370 				if (flags & ND_NA_FLAG_OVERRIDE)
371 					printf("O");
372 				printf(")");
373 			}
374 #define NDADVLEN 24
375 		        icmp6_opt_print((const u_char *)dp + NDADVLEN,
376 					icmp6len - NDADVLEN);
377 		}
378 	    }
379 		break;
380 	case ND_REDIRECT:
381 	{
382 #define RDR(i) ((struct nd_redirect *)(i))
383 		char tgtbuf[INET6_ADDRSTRLEN], dstbuf[INET6_ADDRSTRLEN];
384 
385 		TCHECK(RDR(dp)->nd_rd_dst);
386 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_target,
387 			  tgtbuf, INET6_ADDRSTRLEN);
388 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_dst,
389 			  dstbuf, INET6_ADDRSTRLEN);
390 		printf("icmp6: redirect %s to %s", dstbuf, tgtbuf);
391 #define REDIRECTLEN 40
392 		if (vflag) {
393 			icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
394 					icmp6len - REDIRECTLEN);
395 		}
396 		break;
397 	}
398 	case ICMP6_ROUTER_RENUMBERING:
399 		switch (dp->icmp6_code) {
400 		case ICMP6_ROUTER_RENUMBERING_COMMAND:
401 			printf("icmp6: router renum command");
402 			break;
403 		case ICMP6_ROUTER_RENUMBERING_RESULT:
404 			printf("icmp6: router renum result");
405 			break;
406 		default:
407 			printf("icmp6: router renum code-#%d", dp->icmp6_code);
408 			break;
409 		}
410 		break;
411 #ifdef ICMP6_WRUREQUEST
412 	case ICMP6_WRUREQUEST:	/*ICMP6_FQDN_QUERY*/
413 	    {
414 		int siz;
415 		siz = ep - (u_char *)(dp + 1);
416 		if (siz == 4)
417 			printf("icmp6: who-are-you request");
418 		else {
419 			printf("icmp6: FQDN request");
420 			if (vflag) {
421 				if (siz < 8)
422 					printf("?(icmp6_data %d bytes)", siz);
423 				else if (8 < siz)
424 					printf("?(extra %d bytes)", siz - 8);
425 			}
426 		}
427 		break;
428 	    }
429 #endif /*ICMP6_WRUREQUEST*/
430 #ifdef ICMP6_WRUREPLY
431 	case ICMP6_WRUREPLY:	/*ICMP6_FQDN_REPLY*/
432 	    {
433 		enum { UNKNOWN, WRU, FQDN } mode = UNKNOWN;
434 		u_char const *buf;
435 		u_char const *cp = NULL;
436 
437 		buf = (u_char *)(dp + 1);
438 
439 		/* fair guess */
440 		if (buf[12] == ep - buf - 13)
441 			mode = FQDN;
442 		else if (dp->icmp6_code == 1)
443 			mode = FQDN;
444 
445 		/* wild guess */
446 		if (mode == UNKNOWN) {
447 			cp = buf + 4;
448 			while (cp < ep) {
449 				if (!isprint(*cp++))
450 					mode = FQDN;
451 			}
452 		}
453 		if (mode == UNKNOWN && 2 < labs(buf[12] - (ep - buf - 13)))
454 			mode = WRU;
455 		if (mode == UNKNOWN)
456 			mode = FQDN;
457 
458 		if (mode == WRU) {
459 			cp = buf + 4;
460 			printf("icmp6: who-are-you reply(\"");
461 		} else if (mode == FQDN) {
462 			cp = buf + 13;
463 			printf("icmp6: FQDN reply(\"");
464 		}
465 		for (; cp < ep; cp++)
466 			printf((isprint(*cp) ? "%c" : "\\%03o"), *cp);
467 		printf("\"");
468 		if (vflag) {
469 			printf(",%s", mode == FQDN ? "FQDN" : "WRU");
470 			if (mode == FQDN) {
471 				int ttl;
472 				ttl = (int)ntohl(*(u_int32_t *)&buf[8]);
473 				if (dp->icmp6_code == 1)
474 					printf(",TTL=unknown");
475 				else if (ttl < 0)
476 					printf(",TTL=%d:invalid", ttl);
477 				else
478 					printf(",TTL=%d", ttl);
479 				if (buf[12] != ep - buf - 13) {
480 					(void)printf(",invalid namelen:%d/%u",
481 						buf[12],
482 						(unsigned int)(ep - buf - 13));
483 				}
484 			}
485 		}
486 		printf(")");
487 		break;
488 	    }
489 #endif /*ICMP6_WRUREPLY*/
490 	case MLDV2_LISTENER_REPORT:
491 		printf("multicast listener report v2");
492 		mldv2_report_print((const u_char *) dp, length);
493 		break;
494 	default:
495 		printf("icmp6: type-#%d", dp->icmp6_type);
496 		break;
497 	}
498 	if (vflag) {
499 		if (TTEST2(dp->icmp6_type, length)) {
500 			u_int16_t sum, icmp6_sum;
501 			sum = icmp6_cksum(ip, dp, length);
502 			if (sum != 0) {
503 				icmp6_sum = EXTRACT_16BITS(&dp->icmp6_cksum);
504 				printf(" [bad icmp6 cksum %x! -> %x]", icmp6_sum,
505 				    in_cksum_shouldbe(icmp6_sum, sum));
506 			} else
507 				printf(" [icmp6 cksum ok]");
508 		}
509 	}
510 	return;
511 trunc:
512 	fputs("[|icmp6]", stdout);
513 #if 0
514 #undef TCHECK
515 #endif
516 }
517 
518 void
519 icmp6_opt_print(const u_char *bp, int resid)
520 {
521 	const struct nd_opt_hdr *op;
522 	const struct nd_opt_hdr *opl;	/* why there's no struct? */
523 	const struct nd_opt_prefix_info *opp;
524 	const struct icmp6_opts_redirect *opr;
525 	const struct nd_opt_mtu *opm;
526 	const struct nd_opt_rdnss *oprd;
527 	const struct nd_opt_route_info *opri;
528 	const u_char *ep;
529 	const struct in6_addr *in6p;
530 	struct in6_addr in6;
531 	int	i, opts_len;
532 #if 0
533 	const struct ip6_hdr *ip;
534 	const char *str;
535 	const struct ip6_hdr *oip;
536 	const struct udphdr *ouh;
537 	int hlen, dport;
538 	char buf[256];
539 #endif
540 
541 #if 0
542 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
543 #endif
544 #define ECHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) return
545 
546 	op = (struct nd_opt_hdr *)bp;
547 #if 0
548 	ip = (struct ip6_hdr *)bp2;
549 	oip = &dp->icmp6_ip6;
550 	str = buf;
551 #endif
552 	/* 'ep' points to the end of avaible data. */
553 	ep = snapend;
554 
555 	ECHECK(op->nd_opt_len);
556 	if (resid <= 0)
557 		return;
558 	if (op->nd_opt_len == 0)
559 		goto trunc;
560 	if (bp + (op->nd_opt_len << 3) > ep)
561 		goto trunc;
562 	switch (op->nd_opt_type) {
563 	case ND_OPT_SOURCE_LINKADDR:
564 		opl = (struct nd_opt_hdr *)op;
565 #if 1
566 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
567 			goto trunc;
568 #else
569 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
570 #endif
571 		printf("(src lladdr: %s",
572 			etheraddr_string((u_char *)(opl + 1)));
573 		if (opl->nd_opt_len != 1)
574 			printf("!");
575 		printf(")");
576 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
577 				resid - (op->nd_opt_len << 3));
578 		break;
579 	case ND_OPT_TARGET_LINKADDR:
580 		opl = (struct nd_opt_hdr *)op;
581 #if 1
582 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
583 			goto trunc;
584 #else
585 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
586 #endif
587 		printf("(tgt lladdr: %s",
588 			etheraddr_string((u_char *)(opl + 1)));
589 		if (opl->nd_opt_len != 1)
590 			printf("!");
591 		printf(")");
592 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
593 				resid - (op->nd_opt_len << 3));
594 		break;
595 	case ND_OPT_PREFIX_INFORMATION:
596 		opp = (struct nd_opt_prefix_info *)op;
597 		TCHECK(opp->nd_opt_pi_prefix);
598 		printf("(prefix info: ");
599 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
600 		       printf("L");
601 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)
602 		       printf("A");
603 		if (opp->nd_opt_pi_flags_reserved)
604 			printf(" ");
605 		printf("valid_ltime=");
606 		if ((u_int32_t)ntohl(opp->nd_opt_pi_valid_time) == ~0U)
607 			printf("infinity");
608 		else {
609 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_valid_time));
610 		}
611 		printf(", ");
612 		printf("preferred_ltime=");
613 		if ((u_int32_t)ntohl(opp->nd_opt_pi_preferred_time) == ~0U)
614 			printf("infinity");
615 		else {
616 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_preferred_time));
617 		}
618 		printf(", ");
619 		printf("prefix=%s/%d", ip6addr_string(&opp->nd_opt_pi_prefix),
620 			opp->nd_opt_pi_prefix_len);
621 		if (opp->nd_opt_pi_len != 4)
622 			printf("!");
623 		printf(")");
624 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
625 				resid - (op->nd_opt_len << 3));
626 		break;
627 	case ND_OPT_REDIRECTED_HEADER:
628 		opr = (struct icmp6_opts_redirect *)op;
629 		printf("(redirect)");
630 		/* xxx */
631 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
632 				resid - (op->nd_opt_len << 3));
633 		break;
634 	case ND_OPT_MTU:
635 		opm = (struct nd_opt_mtu *)op;
636 		TCHECK(opm->nd_opt_mtu_mtu);
637 		printf("(mtu: ");
638 		printf("mtu=%u", (u_int32_t)ntohl(opm->nd_opt_mtu_mtu));
639 		if (opm->nd_opt_mtu_len != 1)
640 			printf("!");
641 		printf(")");
642 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
643 				resid - (op->nd_opt_len << 3));
644 		break;
645 	case ND_OPT_ROUTE_INFO:
646 		opri = (struct nd_opt_route_info *)op;
647 		TCHECK(opri->nd_opt_rti_lifetime);
648 		printf("(route-info: ");
649 		memset(&in6, 0, sizeof(in6));
650 		in6p = (const struct in6_addr *)(opri + 1);
651 		switch (op->nd_opt_len) {
652 		case 1:
653 			break;
654 		case 2:
655 			TCHECK2(*in6p, 8);
656 			memcpy(&in6, opri + 1, 8);
657 			break;
658 		case 3:
659 			TCHECK(*in6p);
660 			memcpy(&in6, opri + 1, sizeof(in6));
661 			break;
662 		default:
663 			goto trunc;
664 		}
665 		printf("%s/%u, ", ip6addr_string(&in6),
666 		    opri->nd_opt_rti_prefixlen);
667 		switch (opri->nd_opt_rti_flags & ND_RA_FLAG_RTPREF_MASK) {
668 		case ND_RA_FLAG_RTPREF_HIGH:
669 			printf("pref=high, ");
670 			break;
671 		case ND_RA_FLAG_RTPREF_MEDIUM:
672 			printf("pref=medium, ");
673 			break;
674 		case ND_RA_FLAG_RTPREF_LOW:
675 			printf("pref=low, ");
676 			break;
677 		case ND_RA_FLAG_RTPREF_RSV:
678 			printf("pref=rsv, ");
679 			break;
680 		}
681 		printf("lifetime=%us)",
682 		    (u_int32_t)ntohl(opri->nd_opt_rti_lifetime));
683 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
684 				resid - (op->nd_opt_len << 3));
685 		break;
686 	case ND_OPT_RDNSS:
687 		oprd = (const struct nd_opt_rdnss *)op;
688 		printf("(rdnss: ");
689 		TCHECK(oprd->nd_opt_rdnss_lifetime);
690 		printf("lifetime=%us",
691 		    (u_int32_t)ntohl(oprd->nd_opt_rdnss_lifetime));
692 		if (oprd->nd_opt_rdnss_len < 3) {
693 			printf("!");
694 		} else for (i = 0; i < ((oprd->nd_opt_rdnss_len - 1) / 2); i++) {
695 			struct in6_addr *addr = (struct in6_addr *)(oprd + 1) + i;
696 			TCHECK2(*addr, sizeof(struct in6_addr));
697 			printf(", addr=%s", ip6addr_string(addr));
698 		}
699 		printf(")");
700 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
701 				resid - (op->nd_opt_len << 3));
702 		break;
703 	case ND_OPT_DNSSL:
704 		printf("(dnssl: opt_len=%d)", op->nd_opt_len);
705 		/* XXX */
706 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
707 				resid - (op->nd_opt_len << 3));
708 		break;
709 	default:
710 		opts_len = op->nd_opt_len;
711 		printf("(unknown opt_type=%d, opt_len=%d)",
712 		       op->nd_opt_type, opts_len);
713 		if (opts_len == 0)
714 			opts_len = 1; /* XXX */
715 		icmp6_opt_print((const u_char *)op + (opts_len << 3),
716 				resid - (opts_len << 3));
717 		break;
718 	}
719 	return;
720  trunc:
721 	fputs("[ndp opt]", stdout);
722 	return;
723 #if 0
724 #undef TCHECK
725 #endif
726 #undef ECHECK
727 }
728 
729 void
730 mld6_print(const u_char *bp)
731 {
732 	struct mld_hdr *mp = (struct mld_hdr *)bp;
733 	const u_char *ep;
734 
735 	/* 'ep' points to the end of avaible data. */
736 	ep = snapend;
737 
738 	if ((u_char *)mp + sizeof(*mp) > ep)
739 		return;
740 
741 	printf("max resp delay: %d ", ntohs(mp->mld_maxdelay));
742 	printf("addr: %s", ip6addr_string(&mp->mld_addr));
743 
744 	return;
745 }
746 
747 void
748 mldv2_report_print(const u_char *bp, u_int len)
749 {
750 	struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
751 	u_int group, nsrcs, ngroups;
752 	u_int i, j;
753 
754 	if (len < MLDV2_REPORT_MINLEN) {
755 		printf(" [invalid len %d]", len);
756 		return;
757 	}
758 
759 	TCHECK(icp->icmp6_data16[1]);
760 	ngroups = ntohs(icp->icmp6_data16[1]);
761 	printf(", %d group record(s)", ngroups);
762 	if (vflag > 0) {
763 		/* Print the group records */
764 		group = MLDV2_REPORT_GROUP0;
765 		for (i = 0; i < ngroups; i++) {
766 			/* type(1) + auxlen(1) + numsrc(2) + grp(16) */
767 			if (len < group + MLDV2_REPORT_MINGRPLEN) {
768 				printf(" [invalid number of groups]");
769 				return;
770 			}
771 			TCHECK2(bp[group + MLDV2_RGROUP_MADDR],
772 			    sizeof(struct in6_addr));
773 			printf(" [gaddr %s",
774 			    ip6addr_string(&bp[group + MLDV2_RGROUP_MADDR]));
775 			printf(" %s", tok2str(mldv2report2str,
776 			    " [v2-report-#%d]", bp[group]));
777 			nsrcs = (bp[group + MLDV2_RGROUP_NSRCS] << 8) +
778 			    bp[group + MLDV2_RGROUP_NSRCS + 1];
779 			/* Check the number of sources and print them */
780 			if (len < group + MLDV2_REPORT_MINGRPLEN +
781 				    (nsrcs * sizeof(struct in6_addr))) {
782 				printf(" [invalid number of sources %d]", nsrcs);
783 				return;
784 			}
785 			if (vflag == 1)
786 				printf(", %d source(s)", nsrcs);
787 			else {
788 				/* Print the sources */
789 				(void)printf(" {");
790 				for (j = 0; j < nsrcs; j++) {
791 					TCHECK2(bp[group +
792 					    MLDV2_REPORT_MINGRPLEN +
793 					    j * sizeof(struct in6_addr)],
794 					    sizeof(struct in6_addr));
795 					printf(" %s", ip6addr_string(&bp[group +
796 					    MLDV2_REPORT_MINGRPLEN + j *
797 					    sizeof(struct in6_addr)]));
798 				}
799 				(void)printf(" }");
800 			}
801 			/* Next group record */
802 			group += MLDV2_REPORT_MINGRPLEN + nsrcs *
803 			    sizeof(struct in6_addr);
804 			printf("]");
805 		}
806 	}
807 	return;
808 trunc:
809 	(void)printf("[|icmp6]");
810 	return;
811 }
812 
813 void
814 mldv2_query_print(const u_char *bp, u_int len)
815 {
816 	struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
817 	u_int mrc, qqic;
818 	int mrd, qqi;
819 	int mant, exp;
820 	u_int nsrcs;
821 	u_int i;
822 
823 	if (len < MLD_V2_QUERY_MINLEN) {
824 		printf(" [invalid len %d]", len);
825 		return;
826 	}
827 	TCHECK(icp->icmp6_data16[0]);
828 	mrc = ntohs(icp->icmp6_data16[0]);
829 	if (mrc & MLDV2_MRC_FLOAT) {
830 		mant = MLD_MRC_MANT(mrc);
831 		exp = MLD_MRC_EXP(mrc);
832 		mrd = MLDV2_MRD(mant, exp);
833 	} else {
834 		mrd = mrc;
835 	}
836 	if (vflag) {
837 		(void)printf(" [max resp delay=%d]", mrd);
838 	}
839 	TCHECK2(bp[8], sizeof(struct in6_addr));
840 	printf(" [gaddr %s", ip6addr_string(&bp[8]));
841 
842 	if (vflag) {
843 		TCHECK(bp[MLDV2_QUERY_QQIC]);
844 		if (bp[MLDV2_QUERY_QRV] & MLDV2_QUERY_QRV_SFLAG) {
845 			printf(" sflag");
846 		}
847 		if (MLD_QRV(bp[MLDV2_QUERY_QRV])) {
848 			printf(" robustness=%d", MLD_QRV(bp[MLDV2_QUERY_QRV]));
849 		}
850 		qqic = bp[MLDV2_QUERY_QQIC];
851 		if (qqic & MLDV2_QQIC_FLOAT) {
852 			mant = MLD_QQIC_MANT(qqic);
853 			exp = MLD_QQIC_EXP(qqic);
854 			qqi = MLDV2_QQI(mant, exp);
855 		} else {
856 			qqi = bp[MLDV2_QUERY_QQIC];
857 		}
858 		printf(" qqi=%d", qqi);
859 	}
860 
861 	TCHECK2(bp[MLDV2_QUERY_NSRCS], 2);
862 	nsrcs = ntohs(*(u_short *)&bp[MLDV2_QUERY_NSRCS]);
863 	if (nsrcs > 0) {
864 		if (len < MLD_V2_QUERY_MINLEN + nsrcs * sizeof(struct in6_addr))
865 			printf(" [invalid number of sources]");
866 		else if (vflag > 1) {
867 			printf(" {");
868 			for (i = 0; i < nsrcs; i++) {
869 				TCHECK2(bp[MLDV2_QUERY_SRC0 + i *
870 				    sizeof(struct in6_addr)],
871 				    sizeof(struct in6_addr));
872 				printf(" %s",
873 				    ip6addr_string(&bp[MLDV2_QUERY_SRC0 + i *
874 				    sizeof(struct in6_addr)]));
875 			}
876 			printf(" }");
877 		} else
878 			printf(", %d source(s)", nsrcs);
879 	}
880 	printf("]");
881 	return;
882 trunc:
883 	(void)printf("[|icmp6]");
884 	return;
885 }
886