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