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