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