xref: /openbsd/usr.sbin/tcpdump/print-icmp6.c (revision 91f110e0)
1 /*	$OpenBSD: print-icmp6.c,v 1.12 2014/01/11 04:41:08 lteo 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/param.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 
33 #include <net/if.h>
34 
35 #include <netinet/in.h>
36 #include <netinet/if_ether.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/ip.h>
39 #include <netinet/ip_icmp.h>
40 #include <netinet/ip_var.h>
41 #include <netinet/udp.h>
42 #include <netinet/udp_var.h>
43 #include <netinet/tcp.h>
44 
45 #include <arpa/inet.h>
46 
47 #include <stdio.h>
48 #include <string.h>
49 
50 #include <netinet/ip6.h>
51 #include <netinet/icmp6.h>
52 #include <netinet6/mld6.h>
53 
54 #include "interface.h"
55 #include "addrtoname.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 	register const struct icmp6_hdr *dp;
130 	register const struct ip6_hdr *ip;
131 	register const char *str;
132 	register const struct ip6_hdr *oip;
133 	register const struct udphdr *ouh;
134 	register int hlen, dport;
135 	register 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 != 0)
309 				printf(" ");
310 			printf("router_ltime=%d, ", ntohs(p->nd_ra_router_lifetime));
311 			printf("reachable_time=%u, ",
312 				(u_int32_t)ntohl(p->nd_ra_reachable));
313 			printf("retrans_time=%u)",
314 				(u_int32_t)ntohl(p->nd_ra_retransmit));
315 #define RTADVLEN 16
316 		        icmp6_opt_print((const u_char *)dp + RTADVLEN,
317 					icmp6len - RTADVLEN);
318 		}
319 		break;
320 	case ND_NEIGHBOR_SOLICIT:
321 	    {
322 		struct nd_neighbor_solicit *p;
323 		p = (struct nd_neighbor_solicit *)dp;
324 		TCHECK(p->nd_ns_target);
325 		printf("icmp6: neighbor sol: who has %s",
326 			ip6addr_string(&p->nd_ns_target));
327 		if (vflag) {
328 #define NDSOLLEN 24
329 		        icmp6_opt_print((const u_char *)dp + NDSOLLEN,
330 					icmp6len - NDSOLLEN);
331 		}
332 	    }
333 		break;
334 	case ND_NEIGHBOR_ADVERT:
335 	    {
336 		struct nd_neighbor_advert *p;
337 
338 		p = (struct nd_neighbor_advert *)dp;
339 		TCHECK(p->nd_na_target);
340 		printf("icmp6: neighbor adv: tgt is %s",
341 			ip6addr_string(&p->nd_na_target));
342                 if (vflag) {
343 #define ND_NA_FLAG_ALL	\
344 	(ND_NA_FLAG_ROUTER|ND_NA_FLAG_SOLICITED|ND_NA_FLAG_OVERRIDE)
345 			/* we don't need ntohl() here.  see advanced-api-04. */
346 			if (p->nd_na_flags_reserved &  ND_NA_FLAG_ALL) {
347 #undef ND_NA_FLAG_ALL
348 				u_int32_t flags;
349 
350 				flags = p->nd_na_flags_reserved;
351 				printf("(");
352 				if (flags & ND_NA_FLAG_ROUTER)
353 					printf("R");
354 				if (flags & ND_NA_FLAG_SOLICITED)
355 					printf("S");
356 				if (flags & ND_NA_FLAG_OVERRIDE)
357 					printf("O");
358 				printf(")");
359 			}
360 #define NDADVLEN 24
361 		        icmp6_opt_print((const u_char *)dp + NDADVLEN,
362 					icmp6len - NDADVLEN);
363 		}
364 	    }
365 		break;
366 	case ND_REDIRECT:
367 	{
368 #define RDR(i) ((struct nd_redirect *)(i))
369 		char tgtbuf[INET6_ADDRSTRLEN], dstbuf[INET6_ADDRSTRLEN];
370 
371 		TCHECK(RDR(dp)->nd_rd_dst);
372 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_target,
373 			  tgtbuf, INET6_ADDRSTRLEN);
374 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_dst,
375 			  dstbuf, INET6_ADDRSTRLEN);
376 		printf("icmp6: redirect %s to %s", dstbuf, tgtbuf);
377 #define REDIRECTLEN 40
378 		if (vflag) {
379 			icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
380 					icmp6len - REDIRECTLEN);
381 		}
382 		break;
383 	}
384 	case ICMP6_ROUTER_RENUMBERING:
385 		switch (dp->icmp6_code) {
386 		case ICMP6_ROUTER_RENUMBERING_COMMAND:
387 			printf("icmp6: router renum command");
388 			break;
389 		case ICMP6_ROUTER_RENUMBERING_RESULT:
390 			printf("icmp6: router renum result");
391 			break;
392 		default:
393 			printf("icmp6: router renum code-#%d", dp->icmp6_code);
394 			break;
395 		}
396 		break;
397 #ifdef ICMP6_WRUREQUEST
398 	case ICMP6_WRUREQUEST:	/*ICMP6_FQDN_QUERY*/
399 	    {
400 		int siz;
401 		siz = ep - (u_char *)(dp + 1);
402 		if (siz == 4)
403 			printf("icmp6: who-are-you request");
404 		else {
405 			printf("icmp6: FQDN request");
406 			if (vflag) {
407 				if (siz < 8)
408 					printf("?(icmp6_data %d bytes)", siz);
409 				else if (8 < siz)
410 					printf("?(extra %d bytes)", siz - 8);
411 			}
412 		}
413 		break;
414 	    }
415 #endif /*ICMP6_WRUREQUEST*/
416 #ifdef ICMP6_WRUREPLY
417 	case ICMP6_WRUREPLY:	/*ICMP6_FQDN_REPLY*/
418 	    {
419 		enum { UNKNOWN, WRU, FQDN } mode = UNKNOWN;
420 		u_char const *buf;
421 		u_char const *cp = NULL;
422 
423 		buf = (u_char *)(dp + 1);
424 
425 		/* fair guess */
426 		if (buf[12] == ep - buf - 13)
427 			mode = FQDN;
428 		else if (dp->icmp6_code == 1)
429 			mode = FQDN;
430 
431 		/* wild guess */
432 		if (mode == UNKNOWN) {
433 			cp = buf + 4;
434 			while (cp < ep) {
435 				if (!isprint(*cp++))
436 					mode = FQDN;
437 			}
438 		}
439 #ifndef abs
440 #define abs(a)	((0 < (a)) ? (a) : -(a))
441 #endif
442 		if (mode == UNKNOWN && 2 < abs(buf[12] - (ep - buf - 13)))
443 			mode = WRU;
444 		if (mode == UNKNOWN)
445 			mode = FQDN;
446 
447 		if (mode == WRU) {
448 			cp = buf + 4;
449 			printf("icmp6: who-are-you reply(\"");
450 		} else if (mode == FQDN) {
451 			cp = buf + 13;
452 			printf("icmp6: FQDN reply(\"");
453 		}
454 		for (; cp < ep; cp++)
455 			printf((isprint(*cp) ? "%c" : "\\%03o"), *cp);
456 		printf("\"");
457 		if (vflag) {
458 			printf(",%s", mode == FQDN ? "FQDN" : "WRU");
459 			if (mode == FQDN) {
460 				int ttl;
461 				ttl = (int)ntohl(*(u_int32_t *)&buf[8]);
462 				if (dp->icmp6_code == 1)
463 					printf(",TTL=unknown");
464 				else if (ttl < 0)
465 					printf(",TTL=%d:invalid", ttl);
466 				else
467 					printf(",TTL=%d", ttl);
468 				if (buf[12] != ep - buf - 13) {
469 					(void)printf(",invalid namelen:%d/%u",
470 						buf[12],
471 						(unsigned int)(ep - buf - 13));
472 				}
473 			}
474 		}
475 		printf(")");
476 		break;
477 	    }
478 #endif /*ICMP6_WRUREPLY*/
479 	case MLDV2_LISTENER_REPORT:
480 		printf("multicast listener report v2");
481 		mldv2_report_print((const u_char *) dp, length);
482 		break;
483 	default:
484 		printf("icmp6: type-#%d", dp->icmp6_type);
485 		break;
486 	}
487 	if (vflag) {
488 		u_int16_t sum;
489 		if (TTEST2(dp->icmp6_type, length)) {
490 			sum = icmp6_cksum(ip, dp, length);
491 			if (sum != 0)
492 				printf(" [bad icmp6 cksum %x!]", sum);
493 			else
494 				printf(" [icmp6 cksum ok]");
495 		}
496 	}
497 	return;
498 trunc:
499 	fputs("[|icmp6]", stdout);
500 #if 0
501 #undef TCHECK
502 #endif
503 }
504 
505 void
506 icmp6_opt_print(register const u_char *bp, int resid)
507 {
508 	register const struct nd_opt_hdr *op;
509 	register const struct nd_opt_hdr *opl;	/* why there's no struct? */
510 	register const struct nd_opt_prefix_info *opp;
511 	register const struct icmp6_opts_redirect *opr;
512 	register const struct nd_opt_mtu *opm;
513 	register const u_char *ep;
514 	int	opts_len;
515 #if 0
516 	register const struct ip6_hdr *ip;
517 	register const char *str;
518 	register const struct ip6_hdr *oip;
519 	register const struct udphdr *ouh;
520 	register int hlen, dport;
521 	char buf[256];
522 #endif
523 
524 #if 0
525 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
526 #endif
527 #define ECHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) return
528 
529 	op = (struct nd_opt_hdr *)bp;
530 #if 0
531 	ip = (struct ip6_hdr *)bp2;
532 	oip = &dp->icmp6_ip6;
533 	str = buf;
534 #endif
535 	/* 'ep' points to the end of avaible data. */
536 	ep = snapend;
537 
538 	ECHECK(op->nd_opt_len);
539 	if (resid <= 0)
540 		return;
541 	if (op->nd_opt_len == 0)
542 		goto trunc;
543 	if (bp + (op->nd_opt_len << 3) > ep)
544 		goto trunc;
545 	switch (op->nd_opt_type) {
546 	case ND_OPT_SOURCE_LINKADDR:
547 		opl = (struct nd_opt_hdr *)op;
548 #if 1
549 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
550 			goto trunc;
551 #else
552 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
553 #endif
554 		printf("(src lladdr: %s",
555 			etheraddr_string((u_char *)(opl + 1)));
556 		if (opl->nd_opt_len != 1)
557 			printf("!");
558 		printf(")");
559 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
560 				resid - (op->nd_opt_len << 3));
561 		break;
562 	case ND_OPT_TARGET_LINKADDR:
563 		opl = (struct nd_opt_hdr *)op;
564 #if 1
565 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
566 			goto trunc;
567 #else
568 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
569 #endif
570 		printf("(tgt lladdr: %s",
571 			etheraddr_string((u_char *)(opl + 1)));
572 		if (opl->nd_opt_len != 1)
573 			printf("!");
574 		printf(")");
575 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
576 				resid - (op->nd_opt_len << 3));
577 		break;
578 	case ND_OPT_PREFIX_INFORMATION:
579 		opp = (struct nd_opt_prefix_info *)op;
580 		TCHECK(opp->nd_opt_pi_prefix);
581 		printf("(prefix info: ");
582 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
583 		       printf("L");
584 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)
585 		       printf("A");
586 		if (opp->nd_opt_pi_flags_reserved)
587 			printf(" ");
588 		printf("valid_ltime=");
589 		if ((u_int32_t)ntohl(opp->nd_opt_pi_valid_time) == ~0U)
590 			printf("infinity");
591 		else {
592 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_valid_time));
593 		}
594 		printf(", ");
595 		printf("preferred_ltime=");
596 		if ((u_int32_t)ntohl(opp->nd_opt_pi_preferred_time) == ~0U)
597 			printf("infinity");
598 		else {
599 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_preferred_time));
600 		}
601 		printf(", ");
602 		printf("prefix=%s/%d", ip6addr_string(&opp->nd_opt_pi_prefix),
603 			opp->nd_opt_pi_prefix_len);
604 		if (opp->nd_opt_pi_len != 4)
605 			printf("!");
606 		printf(")");
607 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
608 				resid - (op->nd_opt_len << 3));
609 		break;
610 	case ND_OPT_REDIRECTED_HEADER:
611 		opr = (struct icmp6_opts_redirect *)op;
612 		printf("(redirect)");
613 		/* xxx */
614 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
615 				resid - (op->nd_opt_len << 3));
616 		break;
617 	case ND_OPT_MTU:
618 		opm = (struct nd_opt_mtu *)op;
619 		TCHECK(opm->nd_opt_mtu_mtu);
620 		printf("(mtu: ");
621 		printf("mtu=%u", (u_int32_t)ntohl(opm->nd_opt_mtu_mtu));
622 		if (opm->nd_opt_mtu_len != 1)
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 	default:
629 		opts_len = op->nd_opt_len;
630 		printf("(unknown opt_type=%d, opt_len=%d)",
631 		       op->nd_opt_type, opts_len);
632 		if (opts_len == 0)
633 			opts_len = 1; /* XXX */
634 		icmp6_opt_print((const u_char *)op + (opts_len << 3),
635 				resid - (opts_len << 3));
636 		break;
637 	}
638 	return;
639  trunc:
640 	fputs("[ndp opt]", stdout);
641 	return;
642 #if 0
643 #undef TCHECK
644 #endif
645 #undef ECHECK
646 }
647 
648 void
649 mld6_print(register const u_char *bp)
650 {
651 	register struct mld_hdr *mp = (struct mld_hdr *)bp;
652 	register const u_char *ep;
653 
654 	/* 'ep' points to the end of avaible data. */
655 	ep = snapend;
656 
657 	if ((u_char *)mp + sizeof(*mp) > ep)
658 		return;
659 
660 	printf("max resp delay: %d ", ntohs(mp->mld_maxdelay));
661 	printf("addr: %s", ip6addr_string(&mp->mld_addr));
662 
663 	return;
664 }
665 
666 void
667 mldv2_report_print(const u_char *bp, u_int len)
668 {
669 	struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
670 	u_int group, nsrcs, ngroups;
671 	u_int i, j;
672 
673 	if (len < MLDV2_REPORT_MINLEN) {
674 		printf(" [invalid len %d]", len);
675 		return;
676 	}
677 
678 	TCHECK(icp->icmp6_data16[1]);
679 	ngroups = ntohs(icp->icmp6_data16[1]);
680 	printf(", %d group record(s)", ngroups);
681 	if (vflag > 0) {
682 		/* Print the group records */
683 		group = MLDV2_REPORT_GROUP0;
684 		for (i = 0; i < ngroups; i++) {
685 			/* type(1) + auxlen(1) + numsrc(2) + grp(16) */
686 			if (len < group + MLDV2_REPORT_MINGRPLEN) {
687 				printf(" [invalid number of groups]");
688 				return;
689 			}
690 			TCHECK2(bp[group + MLDV2_RGROUP_MADDR],
691 			    sizeof(struct in6_addr));
692 			printf(" [gaddr %s",
693 			    ip6addr_string(&bp[group + MLDV2_RGROUP_MADDR]));
694 			printf(" %s", tok2str(mldv2report2str,
695 			    " [v2-report-#%d]", bp[group]));
696 			nsrcs = (bp[group + MLDV2_RGROUP_NSRCS] << 8) +
697 			    bp[group + MLDV2_RGROUP_NSRCS + 1];
698 			/* Check the number of sources and print them */
699 			if (len < group + MLDV2_REPORT_MINGRPLEN +
700 				    (nsrcs * sizeof(struct in6_addr))) {
701 				printf(" [invalid number of sources %d]", nsrcs);
702 				return;
703 			}
704 			if (vflag == 1)
705 				printf(", %d source(s)", nsrcs);
706 			else {
707 				/* Print the sources */
708 				(void)printf(" {");
709 				for (j = 0; j < nsrcs; j++) {
710 					TCHECK2(bp[group +
711 					    MLDV2_REPORT_MINGRPLEN +
712 					    j * sizeof(struct in6_addr)],
713 					    sizeof(struct in6_addr));
714 					printf(" %s", ip6addr_string(&bp[group +
715 					    MLDV2_REPORT_MINGRPLEN + j *
716 					    sizeof(struct in6_addr)]));
717 				}
718 				(void)printf(" }");
719 			}
720 			/* Next group record */
721 			group += MLDV2_REPORT_MINGRPLEN + nsrcs *
722 			    sizeof(struct in6_addr);
723 			printf("]");
724 		}
725 	}
726 	return;
727 trunc:
728 	(void)printf("[|icmp6]");
729 	return;
730 }
731 
732 void
733 mldv2_query_print(const u_char *bp, u_int len)
734 {
735 	struct icmp6_hdr *icp = (struct icmp6_hdr *) bp;
736 	u_int mrc, qqic;
737 	int mrd, qqi;
738 	int mant, exp;
739 	u_int nsrcs;
740 	u_int i;
741 
742 	if (len < MLD_V2_QUERY_MINLEN) {
743 		printf(" [invalid len %d]", len);
744 		return;
745 	}
746 	TCHECK(icp->icmp6_data16[0]);
747 	mrc = ntohs(icp->icmp6_data16[0]);
748 	if (mrc & MLDV2_MRC_FLOAT) {
749 		mant = MLD_MRC_MANT(mrc);
750 		exp = MLD_MRC_EXP(mrc);
751 		mrd = MLDV2_MRD(mant, exp);
752 	} else {
753 		mrd = mrc;
754 	}
755 	if (vflag) {
756 		(void)printf(" [max resp delay=%d]", mrd);
757 	}
758 	TCHECK2(bp[8], sizeof(struct in6_addr));
759 	printf(" [gaddr %s", ip6addr_string(&bp[8]));
760 
761 	if (vflag) {
762 		TCHECK(bp[MLDV2_QUERY_QQIC]);
763 		if (bp[MLDV2_QUERY_QRV] & MLDV2_QUERY_QRV_SFLAG) {
764 			printf(" sflag");
765 		}
766 		if (MLD_QRV(bp[MLDV2_QUERY_QRV])) {
767 			printf(" robustness=%d", MLD_QRV(bp[MLDV2_QUERY_QRV]));
768 		}
769 		qqic = bp[MLDV2_QUERY_QQIC];
770 		if (qqic & MLDV2_QQIC_FLOAT) {
771 			mant = MLD_QQIC_MANT(qqic);
772 			exp = MLD_QQIC_EXP(qqic);
773 			qqi = MLDV2_QQI(mant, exp);
774 		} else {
775 			qqi = bp[MLDV2_QUERY_QQIC];
776 		}
777 		printf(" qqi=%d", qqi);
778 	}
779 
780 	TCHECK2(bp[MLDV2_QUERY_NSRCS], 2);
781 	nsrcs = ntohs(*(u_short *)&bp[MLDV2_QUERY_NSRCS]);
782 	if (nsrcs > 0) {
783 		if (len < MLD_V2_QUERY_MINLEN + nsrcs * sizeof(struct in6_addr))
784 			printf(" [invalid number of sources]");
785 		else if (vflag > 1) {
786 			printf(" {");
787 			for (i = 0; i < nsrcs; i++) {
788 				TCHECK2(bp[MLDV2_QUERY_SRC0 + i *
789 				    sizeof(struct in6_addr)],
790 				    sizeof(struct in6_addr));
791 				printf(" %s",
792 				    ip6addr_string(&bp[MLDV2_QUERY_SRC0 + i *
793 				    sizeof(struct in6_addr)]));
794 			}
795 			printf(" }");
796 		} else
797 			printf(", %d source(s)", nsrcs);
798 	}
799 	printf("]");
800 	return;
801 trunc:
802 	(void)printf("[|icmp6]");
803 	return;
804 }
805 
806 
807 #endif /* INET6 */
808