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