print-icmp6.c revision 127668
1/*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#ifndef lint
23static const char rcsid[] _U_ =
24    "@(#) $Header: /tcpdump/master/tcpdump/print-icmp6.c,v 1.72.2.4 2004/03/24 00:14:09 guy Exp $";
25#endif
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#ifdef INET6
32
33#include <tcpdump-stdinc.h>
34
35#include <stdio.h>
36#include <string.h>
37
38#include "ip6.h"
39#include "icmp6.h"
40#include "ipproto.h"
41
42#include "interface.h"
43#include "addrtoname.h"
44#include "extract.h"
45
46#include "udp.h"
47#include "ah.h"
48
49static const char *get_rtpref(u_int);
50static const char *get_lifetime(u_int32_t);
51static void print_lladdr(const u_char *, size_t);
52static void icmp6_opt_print(const u_char *, int);
53static void mld6_print(const u_char *);
54static struct udphdr *get_upperlayer(u_char *, u_int *);
55static void dnsname_print(const u_char *, const u_char *);
56static void icmp6_nodeinfo_print(u_int, const u_char *, const u_char *);
57static void icmp6_rrenum_print(const u_char *, const u_char *);
58
59#ifndef abs
60#define abs(a)	((0 < (a)) ? (a) : -(a))
61#endif
62
63static const char *
64get_rtpref(u_int v)
65{
66	static const char *rtpref_str[] = {
67		"medium",		/* 00 */
68		"high",			/* 01 */
69		"rsv",			/* 10 */
70		"low"			/* 11 */
71	};
72
73	return rtpref_str[((v & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff];
74}
75
76static const char *
77get_lifetime(u_int32_t v)
78{
79	static char buf[20];
80
81	if (v == (u_int32_t)~0UL)
82		return "infinity";
83	else {
84		snprintf(buf, sizeof(buf), "%u", v);
85		return buf;
86	}
87}
88
89static void
90print_lladdr(const u_int8_t *p, size_t l)
91{
92	const u_int8_t *ep, *q;
93
94	q = p;
95	ep = p + l;
96	while (l > 0 && q < ep) {
97		if (q > p)
98			printf(":");
99		printf("%02x", *q++);
100		l--;
101	}
102}
103
104static int icmp6_cksum(const struct ip6_hdr *ip6, const struct icmp6_hdr *icp,
105	u_int len)
106{
107	size_t i;
108	register const u_int16_t *sp;
109	u_int32_t sum;
110	union {
111		struct {
112			struct in6_addr ph_src;
113			struct in6_addr ph_dst;
114			u_int32_t	ph_len;
115			u_int8_t	ph_zero[3];
116			u_int8_t	ph_nxt;
117		} ph;
118		u_int16_t pa[20];
119	} phu;
120
121	/* pseudo-header */
122	memset(&phu, 0, sizeof(phu));
123	phu.ph.ph_src = ip6->ip6_src;
124	phu.ph.ph_dst = ip6->ip6_dst;
125	phu.ph.ph_len = htonl(len);
126	phu.ph.ph_nxt = IPPROTO_ICMPV6;
127
128	sum = 0;
129	for (i = 0; i < sizeof(phu.pa) / sizeof(phu.pa[0]); i++)
130		sum += phu.pa[i];
131
132	sp = (const u_int16_t *)icp;
133
134	for (i = 0; i < (len & ~1); i += 2)
135		sum += *sp++;
136
137	if (len & 1)
138		sum += htons((*(const u_int8_t *)sp) << 8);
139
140	while (sum > 0xffff)
141		sum = (sum & 0xffff) + (sum >> 16);
142	sum = ~sum & 0xffff;
143
144	return (sum);
145}
146
147void
148icmp6_print(const u_char *bp, u_int length, const u_char *bp2, int fragmented)
149{
150	const struct icmp6_hdr *dp;
151	const struct ip6_hdr *ip;
152	const char *str;
153	const struct ip6_hdr *oip;
154	const struct udphdr *ouh;
155	int dport;
156	const u_char *ep;
157	char buf[256];
158	u_int prot;
159
160	dp = (struct icmp6_hdr *)bp;
161	ip = (struct ip6_hdr *)bp2;
162	oip = (struct ip6_hdr *)(dp + 1);
163	str = buf;
164	/* 'ep' points to the end of available data. */
165	ep = snapend;
166
167	TCHECK(dp->icmp6_cksum);
168
169	if (vflag && !fragmented) {
170		int sum = dp->icmp6_cksum;
171
172		if (TTEST2(bp[0], length)) {
173			sum = icmp6_cksum(ip, dp, length);
174			if (sum != 0)
175				(void)printf("[bad icmp6 cksum %x!] ", sum);
176			else
177				(void)printf("[icmp6 sum ok] ");
178		}
179	}
180
181	switch (dp->icmp6_type) {
182	case ICMP6_DST_UNREACH:
183		TCHECK(oip->ip6_dst);
184		switch (dp->icmp6_code) {
185		case ICMP6_DST_UNREACH_NOROUTE:
186			printf("icmp6: %s unreachable route",
187			       ip6addr_string(&oip->ip6_dst));
188			break;
189		case ICMP6_DST_UNREACH_ADMIN:
190			printf("icmp6: %s unreachable prohibited",
191			       ip6addr_string(&oip->ip6_dst));
192			break;
193		case ICMP6_DST_UNREACH_BEYONDSCOPE:
194			printf("icmp6: %s beyond scope of source address %s",
195			       ip6addr_string(&oip->ip6_dst),
196			       ip6addr_string(&oip->ip6_src));
197			break;
198		case ICMP6_DST_UNREACH_ADDR:
199			printf("icmp6: %s unreachable address",
200			       ip6addr_string(&oip->ip6_dst));
201			break;
202		case ICMP6_DST_UNREACH_NOPORT:
203			if ((ouh = get_upperlayer((u_char *)oip, &prot))
204			    == NULL)
205				goto trunc;
206
207			dport = EXTRACT_16BITS(&ouh->uh_dport);
208			switch (prot) {
209			case IPPROTO_TCP:
210				printf("icmp6: %s tcp port %s unreachable",
211					ip6addr_string(&oip->ip6_dst),
212					tcpport_string(dport));
213				break;
214			case IPPROTO_UDP:
215				printf("icmp6: %s udp port %s unreachable",
216					ip6addr_string(&oip->ip6_dst),
217					udpport_string(dport));
218				break;
219			default:
220				printf("icmp6: %s protocol %d port %d unreachable",
221					ip6addr_string(&oip->ip6_dst),
222					oip->ip6_nxt, dport);
223				break;
224			}
225			break;
226		default:
227			printf("icmp6: %s unreachable code-#%d",
228				ip6addr_string(&oip->ip6_dst),
229				dp->icmp6_code);
230			break;
231		}
232		break;
233	case ICMP6_PACKET_TOO_BIG:
234		TCHECK(dp->icmp6_mtu);
235		printf("icmp6: too big %u", EXTRACT_32BITS(&dp->icmp6_mtu));
236		break;
237	case ICMP6_TIME_EXCEEDED:
238		TCHECK(oip->ip6_dst);
239		switch (dp->icmp6_code) {
240		case ICMP6_TIME_EXCEED_TRANSIT:
241			printf("icmp6: time exceeded in-transit for %s",
242				ip6addr_string(&oip->ip6_dst));
243			break;
244		case ICMP6_TIME_EXCEED_REASSEMBLY:
245			printf("icmp6: ip6 reassembly time exceeded");
246			break;
247		default:
248			printf("icmp6: time exceeded code-#%d",
249				dp->icmp6_code);
250			break;
251		}
252		break;
253	case ICMP6_PARAM_PROB:
254		TCHECK(oip->ip6_dst);
255		switch (dp->icmp6_code) {
256		case ICMP6_PARAMPROB_HEADER:
257			printf("icmp6: parameter problem errorneous - octet %u",
258				EXTRACT_32BITS(&dp->icmp6_pptr));
259			break;
260		case ICMP6_PARAMPROB_NEXTHEADER:
261			printf("icmp6: parameter problem next header - octet %u",
262				EXTRACT_32BITS(&dp->icmp6_pptr));
263			break;
264		case ICMP6_PARAMPROB_OPTION:
265			printf("icmp6: parameter problem option - octet %u",
266				EXTRACT_32BITS(&dp->icmp6_pptr));
267			break;
268		default:
269			printf("icmp6: parameter problem code-#%d",
270			       dp->icmp6_code);
271			break;
272		}
273		break;
274	case ICMP6_ECHO_REQUEST:
275	case ICMP6_ECHO_REPLY:
276		TCHECK(dp->icmp6_seq);
277		printf("icmp6: echo %s seq %u",
278			dp->icmp6_type == ICMP6_ECHO_REQUEST ?
279			"request" : "reply",
280			EXTRACT_16BITS(&dp->icmp6_seq));
281		break;
282	case ICMP6_MEMBERSHIP_QUERY:
283		printf("icmp6: multicast listener query ");
284		mld6_print((const u_char *)dp);
285		break;
286	case ICMP6_MEMBERSHIP_REPORT:
287		printf("icmp6: multicast listener report ");
288		mld6_print((const u_char *)dp);
289		break;
290	case ICMP6_MEMBERSHIP_REDUCTION:
291		printf("icmp6: multicast listener done ");
292		mld6_print((const u_char *)dp);
293		break;
294	case ND_ROUTER_SOLICIT:
295		printf("icmp6: router solicitation ");
296		if (vflag) {
297#define RTSOLLEN 8
298			icmp6_opt_print((const u_char *)dp + RTSOLLEN,
299					length - RTSOLLEN);
300		}
301		break;
302	case ND_ROUTER_ADVERT:
303		printf("icmp6: router advertisement");
304		if (vflag) {
305			struct nd_router_advert *p;
306
307			p = (struct nd_router_advert *)dp;
308			TCHECK(p->nd_ra_retransmit);
309			printf("(chlim=%d, ", (int)p->nd_ra_curhoplimit);
310			if (p->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
311				printf("M");
312			if (p->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
313				printf("O");
314			if (p->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT)
315				printf("H");
316
317			if ((p->nd_ra_flags_reserved & ~ND_RA_FLAG_RTPREF_MASK)
318			    != 0)
319				printf(" ");
320
321			printf("pref=%s, ",
322			    get_rtpref(p->nd_ra_flags_reserved));
323
324			printf("router_ltime=%d, ", EXTRACT_16BITS(&p->nd_ra_router_lifetime));
325			printf("reachable_time=%u, ",
326				EXTRACT_32BITS(&p->nd_ra_reachable));
327			printf("retrans_time=%u)",
328				EXTRACT_32BITS(&p->nd_ra_retransmit));
329#define RTADVLEN 16
330			icmp6_opt_print((const u_char *)dp + RTADVLEN,
331					length - RTADVLEN);
332		}
333		break;
334	case ND_NEIGHBOR_SOLICIT:
335	    {
336		struct nd_neighbor_solicit *p;
337		p = (struct nd_neighbor_solicit *)dp;
338		TCHECK(p->nd_ns_target);
339		printf("icmp6: neighbor sol: who has %s",
340			ip6addr_string(&p->nd_ns_target));
341		if (vflag) {
342#define NDSOLLEN 24
343			icmp6_opt_print((const u_char *)dp + NDSOLLEN,
344					length - NDSOLLEN);
345		}
346	    }
347		break;
348	case ND_NEIGHBOR_ADVERT:
349	    {
350		struct nd_neighbor_advert *p;
351
352		p = (struct nd_neighbor_advert *)dp;
353		TCHECK(p->nd_na_target);
354		printf("icmp6: neighbor adv: tgt is %s",
355			ip6addr_string(&p->nd_na_target));
356		if (vflag) {
357#define ND_NA_FLAG_ALL	\
358	(ND_NA_FLAG_ROUTER|ND_NA_FLAG_SOLICITED|ND_NA_FLAG_OVERRIDE)
359			/* we don't need ntohl() here.  see advanced-api-04. */
360			if (p->nd_na_flags_reserved &  ND_NA_FLAG_ALL) {
361#undef ND_NA_FLAG_ALL
362				u_int32_t flags;
363
364				flags = p->nd_na_flags_reserved;
365				printf("(");
366				if (flags & ND_NA_FLAG_ROUTER)
367					printf("R");
368				if (flags & ND_NA_FLAG_SOLICITED)
369					printf("S");
370				if (flags & ND_NA_FLAG_OVERRIDE)
371					printf("O");
372				printf(")");
373			}
374#define NDADVLEN 24
375			icmp6_opt_print((const u_char *)dp + NDADVLEN,
376					length - NDADVLEN);
377#undef NDADVLEN
378		}
379	    }
380		break;
381	case ND_REDIRECT:
382#define RDR(i) ((struct nd_redirect *)(i))
383		TCHECK(RDR(dp)->nd_rd_dst);
384		printf("icmp6: redirect %s",
385		    getname6((const u_char *)&RDR(dp)->nd_rd_dst));
386		TCHECK(RDR(dp)->nd_rd_target);
387		printf(" to %s",
388		    getname6((const u_char*)&RDR(dp)->nd_rd_target));
389#define REDIRECTLEN 40
390		if (vflag) {
391			icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
392					length - REDIRECTLEN);
393		}
394		break;
395#undef REDIRECTLEN
396#undef RDR
397	case ICMP6_ROUTER_RENUMBERING:
398		icmp6_rrenum_print(bp, ep);
399		break;
400	case ICMP6_NI_QUERY:
401	case ICMP6_NI_REPLY:
402		icmp6_nodeinfo_print(length, bp, ep);
403		break;
404	case ICMP6_HADISCOV_REQUEST:
405		printf("icmp6: ha discovery request");
406		if (vflag) {
407			TCHECK(dp->icmp6_data16[0]);
408			printf("(id=%d)", EXTRACT_16BITS(&dp->icmp6_data16[0]));
409		}
410		break;
411	case ICMP6_HADISCOV_REPLY:
412		printf("icmp6: ha discovery reply");
413		if (vflag) {
414			struct in6_addr *in6;
415			u_char *cp;
416
417			TCHECK(dp->icmp6_data16[0]);
418			printf("(id=%d", EXTRACT_16BITS(&dp->icmp6_data16[0]));
419			cp = (u_char *)dp + length;
420			in6 = (struct in6_addr *)(dp + 1);
421			for (; (u_char *)in6 < cp; in6++) {
422				TCHECK(*in6);
423				printf(", %s", ip6addr_string(in6));
424			}
425			printf(")");
426		}
427		break;
428	case ICMP6_MOBILEPREFIX_SOLICIT:
429		printf("icmp6: mobile router solicitation");
430		if (vflag) {
431			TCHECK(dp->icmp6_data16[0]);
432			printf("(id=%d)", EXTRACT_16BITS(&dp->icmp6_data16[0]));
433		}
434		break;
435	case ICMP6_MOBILEPREFIX_ADVERT:
436		printf("icmp6: mobile router advertisement");
437		if (vflag) {
438			TCHECK(dp->icmp6_data16[0]);
439			printf("(id=%d", EXTRACT_16BITS(&dp->icmp6_data16[0]));
440			if (dp->icmp6_data16[1] & 0xc0)
441				printf(" ");
442			if (dp->icmp6_data16[1] & 0x80)
443				printf("M");
444			if (dp->icmp6_data16[1] & 0x40)
445				printf("O");
446			printf(")");
447#define MPADVLEN 8
448			icmp6_opt_print((const u_char *)dp + MPADVLEN,
449					length - MPADVLEN);
450		}
451		break;
452	default:
453		printf("icmp6: type-#%d", dp->icmp6_type);
454		break;
455	}
456	return;
457trunc:
458	fputs("[|icmp6]", stdout);
459}
460
461static struct udphdr *
462get_upperlayer(u_char *bp, u_int *prot)
463{
464	const u_char *ep;
465	struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
466	struct udphdr *uh;
467	struct ip6_hbh *hbh;
468	struct ip6_frag *fragh;
469	struct ah *ah;
470	u_int nh;
471	int hlen;
472
473	/* 'ep' points to the end of available data. */
474	ep = snapend;
475
476	if (!TTEST(ip6->ip6_nxt))
477		return NULL;
478
479	nh = ip6->ip6_nxt;
480	hlen = sizeof(struct ip6_hdr);
481
482	while (bp < snapend) {
483		bp += hlen;
484
485		switch(nh) {
486		case IPPROTO_UDP:
487		case IPPROTO_TCP:
488			uh = (struct udphdr *)bp;
489			if (TTEST(uh->uh_dport)) {
490				*prot = nh;
491				return(uh);
492			}
493			else
494				return(NULL);
495			/* NOTREACHED */
496
497		case IPPROTO_HOPOPTS:
498		case IPPROTO_DSTOPTS:
499		case IPPROTO_ROUTING:
500			hbh = (struct ip6_hbh *)bp;
501			if (!TTEST(hbh->ip6h_len))
502				return(NULL);
503			nh = hbh->ip6h_nxt;
504			hlen = (hbh->ip6h_len + 1) << 3;
505			break;
506
507		case IPPROTO_FRAGMENT: /* this should be odd, but try anyway */
508			fragh = (struct ip6_frag *)bp;
509			if (!TTEST(fragh->ip6f_offlg))
510				return(NULL);
511			/* fragments with non-zero offset are meaningless */
512			if ((EXTRACT_16BITS(&fragh->ip6f_offlg) & IP6F_OFF_MASK) != 0)
513				return(NULL);
514			nh = fragh->ip6f_nxt;
515			hlen = sizeof(struct ip6_frag);
516			break;
517
518		case IPPROTO_AH:
519			ah = (struct ah *)bp;
520			if (!TTEST(ah->ah_len))
521				return(NULL);
522			nh = ah->ah_nxt;
523			hlen = (ah->ah_len + 2) << 2;
524			break;
525
526		default:	/* unknown or undecodable header */
527			*prot = nh; /* meaningless, but set here anyway */
528			return(NULL);
529		}
530	}
531
532	return(NULL);		/* should be notreached, though */
533}
534
535static void
536icmp6_opt_print(const u_char *bp, int resid)
537{
538	const struct nd_opt_hdr *op;
539	const struct nd_opt_hdr *opl;	/* why there's no struct? */
540	const struct nd_opt_prefix_info *opp;
541	const struct icmp6_opts_redirect *opr;
542	const struct nd_opt_mtu *opm;
543	const struct nd_opt_advinterval *opa;
544	const struct nd_opt_homeagent_info *oph;
545	const struct nd_opt_route_info *opri;
546	const u_char *cp, *ep;
547	struct in6_addr in6, *in6p;
548	size_t l;
549
550#define ECHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) return
551
552	cp = bp;
553	/* 'ep' points to the end of available data. */
554	ep = snapend;
555
556	while (cp < ep) {
557		op = (struct nd_opt_hdr *)cp;
558
559		ECHECK(op->nd_opt_len);
560		if (resid <= 0)
561			return;
562		if (op->nd_opt_len == 0)
563			goto trunc;
564		if (cp + (op->nd_opt_len << 3) > ep)
565			goto trunc;
566
567		switch (op->nd_opt_type) {
568		case ND_OPT_SOURCE_LINKADDR:
569			opl = (struct nd_opt_hdr *)op;
570			printf("(src lladdr: ");
571			l = (op->nd_opt_len << 3) - 2;
572			print_lladdr(cp + 2, l);
573			/*(*/
574			printf(")");
575			break;
576		case ND_OPT_TARGET_LINKADDR:
577			opl = (struct nd_opt_hdr *)op;
578			printf("(tgt lladdr: ");
579			l = (op->nd_opt_len << 3) - 2;
580			print_lladdr(cp + 2, l);
581			/*(*/
582			printf(")");
583			break;
584		case ND_OPT_PREFIX_INFORMATION:
585			opp = (struct nd_opt_prefix_info *)op;
586			TCHECK(opp->nd_opt_pi_prefix);
587			printf("(prefix info: ");	/*)*/
588			if (op->nd_opt_len != 4) {
589				printf("badlen");
590				/*(*/
591				printf(")");
592				break;
593			}
594			if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
595				printf("L");
596			if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)
597				printf("A");
598			if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ROUTER)
599				printf("R");
600			if (opp->nd_opt_pi_flags_reserved)
601				printf(" ");
602			printf("valid_ltime=%s,",
603			    get_lifetime(EXTRACT_32BITS(&opp->nd_opt_pi_valid_time)));
604			printf("preferred_ltime=%s,",
605			    get_lifetime(EXTRACT_32BITS(&opp->nd_opt_pi_preferred_time)));
606			printf("prefix=%s/%d",
607			    ip6addr_string(&opp->nd_opt_pi_prefix),
608			    opp->nd_opt_pi_prefix_len);
609			if (opp->nd_opt_pi_len != 4)
610				printf("!");
611			/*(*/
612			printf(")");
613			break;
614		case ND_OPT_REDIRECTED_HEADER:
615			opr = (struct icmp6_opts_redirect *)op;
616			printf("(redirect)");
617			/* xxx */
618			break;
619		case ND_OPT_MTU:
620			opm = (struct nd_opt_mtu *)op;
621			TCHECK(opm->nd_opt_mtu_mtu);
622			printf("(mtu:");	/*)*/
623			if (op->nd_opt_len != 1) {
624				printf("badlen");
625				/*(*/
626				printf(")");
627				break;
628			}
629			printf(" mtu=%u", EXTRACT_32BITS(&opm->nd_opt_mtu_mtu));
630			if (opm->nd_opt_mtu_len != 1)
631				printf("!");
632			printf(")");
633			break;
634		case ND_OPT_ADVINTERVAL:
635			opa = (struct nd_opt_advinterval *)op;
636			TCHECK(opa->nd_opt_adv_interval);
637			printf("(advint:");	/*)*/
638			printf(" advint=%u",
639			    EXTRACT_32BITS(&opa->nd_opt_adv_interval));
640			/*(*/
641			printf(")");
642			break;
643		case ND_OPT_HOMEAGENT_INFO:
644			oph = (struct nd_opt_homeagent_info *)op;
645			TCHECK(oph->nd_opt_hai_lifetime);
646			printf("(ha info:");	/*)*/
647			printf(" pref=%d", EXTRACT_16BITS(&oph->nd_opt_hai_preference));
648			printf(", lifetime=%u", EXTRACT_16BITS(&oph->nd_opt_hai_lifetime));
649			printf(")");
650			break;
651		case ND_OPT_ROUTE_INFO:
652			opri = (struct nd_opt_route_info *)op;
653			TCHECK(opri->nd_opt_rti_lifetime);
654			memset(&in6, 0, sizeof(in6));
655			in6p = (struct in6_addr *)(opri + 1);
656			switch (op->nd_opt_len) {
657			case 1:
658				break;
659			case 2:
660				TCHECK2(*in6p, 8);
661				memcpy(&in6, opri + 1, 8);
662				break;
663			case 3:
664				TCHECK(*in6p);
665				memcpy(&in6, opri + 1, sizeof(in6));
666				break;
667			default:
668				goto trunc;
669			}
670			printf("(rtinfo:");	/*)*/
671			printf(" %s/%u", ip6addr_string(&in6),
672			    opri->nd_opt_rti_prefixlen);
673			printf(", pref=%s", get_rtpref(opri->nd_opt_rti_flags));
674			printf(", lifetime=%s",
675			    get_lifetime(EXTRACT_32BITS(&opri->nd_opt_rti_lifetime)));
676			/*(*/
677			printf(")");
678			break;
679		default:
680			printf("(unknown opt_type=%d, opt_len=%d)",
681			       op->nd_opt_type, op->nd_opt_len);
682			break;
683		}
684
685		cp += op->nd_opt_len << 3;
686		resid -= op->nd_opt_len << 3;
687	}
688	return;
689
690 trunc:
691	fputs("[ndp opt]", stdout);
692	return;
693#undef ECHECK
694}
695
696static void
697mld6_print(const u_char *bp)
698{
699	struct mld6_hdr *mp = (struct mld6_hdr *)bp;
700	const u_char *ep;
701
702	/* 'ep' points to the end of available data. */
703	ep = snapend;
704
705	if ((u_char *)mp + sizeof(*mp) > ep)
706		return;
707
708	printf("max resp delay: %d ", EXTRACT_16BITS(&mp->mld6_maxdelay));
709	printf("addr: %s", ip6addr_string(&mp->mld6_addr));
710}
711
712static void
713dnsname_print(const u_char *cp, const u_char *ep)
714{
715	int i;
716
717	/* DNS name decoding - no decompression */
718	printf(", \"");
719	while (cp < ep) {
720		i = *cp++;
721		if (i) {
722			if (i > ep - cp) {
723				printf("???");
724				break;
725			}
726			while (i-- && cp < ep) {
727				safeputchar(*cp);
728				cp++;
729			}
730			if (cp + 1 < ep && *cp)
731				printf(".");
732		} else {
733			if (cp == ep) {
734				/* FQDN */
735				printf(".");
736			} else if (cp + 1 == ep && *cp == '\0') {
737				/* truncated */
738			} else {
739				/* invalid */
740				printf("???");
741			}
742			break;
743		}
744	}
745	printf("\"");
746}
747
748static void
749icmp6_nodeinfo_print(u_int icmp6len, const u_char *bp, const u_char *ep)
750{
751	struct icmp6_nodeinfo *ni6;
752	struct icmp6_hdr *dp;
753	const u_char *cp;
754	size_t siz, i;
755	int needcomma;
756
757	if (ep < bp)
758		return;
759	dp = (struct icmp6_hdr *)bp;
760	ni6 = (struct icmp6_nodeinfo *)bp;
761	siz = ep - bp;
762
763	switch (ni6->ni_type) {
764	case ICMP6_NI_QUERY:
765		if (siz == sizeof(*dp) + 4) {
766			/* KAME who-are-you */
767			printf("icmp6: who-are-you request");
768			break;
769		}
770		printf("icmp6: node information query");
771
772		TCHECK2(*dp, sizeof(*ni6));
773		ni6 = (struct icmp6_nodeinfo *)dp;
774		printf(" (");	/*)*/
775		switch (EXTRACT_16BITS(&ni6->ni_qtype)) {
776		case NI_QTYPE_NOOP:
777			printf("noop");
778			break;
779		case NI_QTYPE_SUPTYPES:
780			printf("supported qtypes");
781			i = EXTRACT_16BITS(&ni6->ni_flags);
782			if (i)
783				printf(" [%s]", (i & 0x01) ? "C" : "");
784			break;
785			break;
786		case NI_QTYPE_FQDN:
787			printf("DNS name");
788			break;
789		case NI_QTYPE_NODEADDR:
790			printf("node addresses");
791			i = ni6->ni_flags;
792			if (!i)
793				break;
794			/* NI_NODEADDR_FLAG_TRUNCATE undefined for query */
795			printf(" [%s%s%s%s%s%s]",
796			    (i & NI_NODEADDR_FLAG_ANYCAST) ? "a" : "",
797			    (i & NI_NODEADDR_FLAG_GLOBAL) ? "G" : "",
798			    (i & NI_NODEADDR_FLAG_SITELOCAL) ? "S" : "",
799			    (i & NI_NODEADDR_FLAG_LINKLOCAL) ? "L" : "",
800			    (i & NI_NODEADDR_FLAG_COMPAT) ? "C" : "",
801			    (i & NI_NODEADDR_FLAG_ALL) ? "A" : "");
802			break;
803		default:
804			printf("unknown");
805			break;
806		}
807
808		if (ni6->ni_qtype == NI_QTYPE_NOOP ||
809		    ni6->ni_qtype == NI_QTYPE_SUPTYPES) {
810			if (siz != sizeof(*ni6))
811				if (vflag)
812					printf(", invalid len");
813			/*(*/
814			printf(")");
815			break;
816		}
817
818
819		/* XXX backward compat, icmp-name-lookup-03 */
820		if (siz == sizeof(*ni6)) {
821			printf(", 03 draft");
822			/*(*/
823			printf(")");
824			break;
825		}
826
827		switch (ni6->ni_code) {
828		case ICMP6_NI_SUBJ_IPV6:
829			if (!TTEST2(*dp,
830			    sizeof(*ni6) + sizeof(struct in6_addr)))
831				break;
832			if (siz != sizeof(*ni6) + sizeof(struct in6_addr)) {
833				if (vflag)
834					printf(", invalid subject len");
835				break;
836			}
837			printf(", subject=%s",
838			    getname6((const u_char *)(ni6 + 1)));
839			break;
840		case ICMP6_NI_SUBJ_FQDN:
841			printf(", subject=DNS name");
842			cp = (const u_char *)(ni6 + 1);
843			if (cp[0] == ep - cp - 1) {
844				/* icmp-name-lookup-03, pascal string */
845				if (vflag)
846					printf(", 03 draft");
847				cp++;
848				printf(", \"");
849				while (cp < ep) {
850					safeputchar(*cp);
851					cp++;
852				}
853				printf("\"");
854			} else
855				dnsname_print(cp, ep);
856			break;
857		case ICMP6_NI_SUBJ_IPV4:
858			if (!TTEST2(*dp, sizeof(*ni6) + sizeof(struct in_addr)))
859				break;
860			if (siz != sizeof(*ni6) + sizeof(struct in_addr)) {
861				if (vflag)
862					printf(", invalid subject len");
863				break;
864			}
865			printf(", subject=%s",
866			    getname((const u_char *)(ni6 + 1)));
867			break;
868		default:
869			printf(", unknown subject");
870			break;
871		}
872
873		/*(*/
874		printf(")");
875		break;
876
877	case ICMP6_NI_REPLY:
878		if (icmp6len > siz) {
879			printf("[|icmp6: node information reply]");
880			break;
881		}
882
883		needcomma = 0;
884
885		ni6 = (struct icmp6_nodeinfo *)dp;
886		printf("icmp6: node information reply");
887		printf(" (");	/*)*/
888		switch (ni6->ni_code) {
889		case ICMP6_NI_SUCCESS:
890			if (vflag) {
891				printf("success");
892				needcomma++;
893			}
894			break;
895		case ICMP6_NI_REFUSED:
896			printf("refused");
897			needcomma++;
898			if (siz != sizeof(*ni6))
899				if (vflag)
900					printf(", invalid length");
901			break;
902		case ICMP6_NI_UNKNOWN:
903			printf("unknown");
904			needcomma++;
905			if (siz != sizeof(*ni6))
906				if (vflag)
907					printf(", invalid length");
908			break;
909		}
910
911		if (ni6->ni_code != ICMP6_NI_SUCCESS) {
912			/*(*/
913			printf(")");
914			break;
915		}
916
917		switch (EXTRACT_16BITS(&ni6->ni_qtype)) {
918		case NI_QTYPE_NOOP:
919			if (needcomma)
920				printf(", ");
921			printf("noop");
922			if (siz != sizeof(*ni6))
923				if (vflag)
924					printf(", invalid length");
925			break;
926		case NI_QTYPE_SUPTYPES:
927			if (needcomma)
928				printf(", ");
929			printf("supported qtypes");
930			i = EXTRACT_16BITS(&ni6->ni_flags);
931			if (i)
932				printf(" [%s]", (i & 0x01) ? "C" : "");
933			break;
934		case NI_QTYPE_FQDN:
935			if (needcomma)
936				printf(", ");
937			printf("DNS name");
938			cp = (const u_char *)(ni6 + 1) + 4;
939			if (cp[0] == ep - cp - 1) {
940				/* icmp-name-lookup-03, pascal string */
941				if (vflag)
942					printf(", 03 draft");
943				cp++;
944				printf(", \"");
945				while (cp < ep) {
946					safeputchar(*cp);
947					cp++;
948				}
949				printf("\"");
950			} else
951				dnsname_print(cp, ep);
952			if ((EXTRACT_16BITS(&ni6->ni_flags) & 0x01) != 0)
953				printf(" [TTL=%u]", *(u_int32_t *)(ni6 + 1));
954			break;
955		case NI_QTYPE_NODEADDR:
956			if (needcomma)
957				printf(", ");
958			printf("node addresses");
959			i = sizeof(*ni6);
960			while (i < siz) {
961				if (i + sizeof(struct in6_addr) + sizeof(int32_t) > siz)
962					break;
963				printf(" %s", getname6(bp + i));
964				i += sizeof(struct in6_addr);
965				printf("(%d)", (int32_t)EXTRACT_32BITS(bp + i));
966				i += sizeof(int32_t);
967			}
968			i = ni6->ni_flags;
969			if (!i)
970				break;
971			printf(" [%s%s%s%s%s%s%s]",
972			    (i & NI_NODEADDR_FLAG_ANYCAST) ? "a" : "",
973			    (i & NI_NODEADDR_FLAG_GLOBAL) ? "G" : "",
974			    (i & NI_NODEADDR_FLAG_SITELOCAL) ? "S" : "",
975			    (i & NI_NODEADDR_FLAG_LINKLOCAL) ? "L" : "",
976			    (i & NI_NODEADDR_FLAG_COMPAT) ? "C" : "",
977			    (i & NI_NODEADDR_FLAG_ALL) ? "A" : "",
978			    (i & NI_NODEADDR_FLAG_TRUNCATE) ? "T" : "");
979			break;
980		default:
981			if (needcomma)
982				printf(", ");
983			printf("unknown");
984			break;
985		}
986
987		/*(*/
988		printf(")");
989		break;
990	}
991	return;
992
993trunc:
994	fputs("[|icmp6]", stdout);
995}
996
997static void
998icmp6_rrenum_print(const u_char *bp, const u_char *ep)
999{
1000	struct icmp6_router_renum *rr6;
1001	struct icmp6_hdr *dp;
1002	size_t siz;
1003	const char *cp;
1004	struct rr_pco_match *match;
1005	struct rr_pco_use *use;
1006	char hbuf[NI_MAXHOST];
1007	int n;
1008
1009	if (ep < bp)
1010		return;
1011	dp = (struct icmp6_hdr *)bp;
1012	rr6 = (struct icmp6_router_renum *)bp;
1013	siz = ep - bp;
1014	cp = (const char *)(rr6 + 1);
1015
1016	TCHECK(rr6->rr_reserved);
1017	switch (rr6->rr_code) {
1018	case ICMP6_ROUTER_RENUMBERING_COMMAND:
1019		printf("router renum: command");
1020		break;
1021	case ICMP6_ROUTER_RENUMBERING_RESULT:
1022		printf("router renum: result");
1023		break;
1024	case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
1025		printf("router renum: sequence number reset");
1026		break;
1027	default:
1028		printf("router renum: code-#%d", rr6->rr_code);
1029		break;
1030	}
1031
1032	printf(", seq=%u", EXTRACT_32BITS(&rr6->rr_seqnum));
1033
1034	if (vflag) {
1035#define F(x, y)	((rr6->rr_flags) & (x) ? (y) : "")
1036		printf("[");	/*]*/
1037		if (rr6->rr_flags) {
1038			printf("%s%s%s%s%s,", F(ICMP6_RR_FLAGS_TEST, "T"),
1039			    F(ICMP6_RR_FLAGS_REQRESULT, "R"),
1040			    F(ICMP6_RR_FLAGS_FORCEAPPLY, "A"),
1041			    F(ICMP6_RR_FLAGS_SPECSITE, "S"),
1042			    F(ICMP6_RR_FLAGS_PREVDONE, "P"));
1043		}
1044		printf("seg=%u,", rr6->rr_segnum);
1045		printf("maxdelay=%u", rr6->rr_maxdelay);
1046		if (rr6->rr_reserved)
1047			printf("rsvd=0x%x", EXTRACT_16BITS(&rr6->rr_reserved));
1048		/*[*/
1049		printf("]");
1050#undef F
1051	}
1052
1053	if (rr6->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
1054		match = (struct rr_pco_match *)cp;
1055		cp = (const char *)(match + 1);
1056
1057		TCHECK(match->rpm_prefix);
1058
1059		if (vflag > 1)
1060			printf("\n\t");
1061		else
1062			printf(" ");
1063		printf("match(");	/*)*/
1064		switch (match->rpm_code) {
1065		case RPM_PCO_ADD:	printf("add"); break;
1066		case RPM_PCO_CHANGE:	printf("change"); break;
1067		case RPM_PCO_SETGLOBAL:	printf("setglobal"); break;
1068		default:		printf("#%u", match->rpm_code); break;
1069		}
1070
1071		if (vflag) {
1072			printf(",ord=%u", match->rpm_ordinal);
1073			printf(",min=%u", match->rpm_minlen);
1074			printf(",max=%u", match->rpm_maxlen);
1075		}
1076		if (inet_ntop(AF_INET6, &match->rpm_prefix, hbuf, sizeof(hbuf)))
1077			printf(",%s/%u", hbuf, match->rpm_matchlen);
1078		else
1079			printf(",?/%u", match->rpm_matchlen);
1080		/*(*/
1081		printf(")");
1082
1083		n = match->rpm_len - 3;
1084		if (n % 4)
1085			goto trunc;
1086		n /= 4;
1087		while (n-- > 0) {
1088			use = (struct rr_pco_use *)cp;
1089			cp = (const char *)(use + 1);
1090
1091			TCHECK(use->rpu_prefix);
1092
1093			if (vflag > 1)
1094				printf("\n\t");
1095			else
1096				printf(" ");
1097			printf("use(");	/*)*/
1098			if (use->rpu_flags) {
1099#define F(x, y)	((use->rpu_flags) & (x) ? (y) : "")
1100				printf("%s%s,",
1101				    F(ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, "V"),
1102				    F(ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, "P"));
1103#undef F
1104			}
1105			if (vflag) {
1106				printf("mask=0x%x,", use->rpu_ramask);
1107				printf("raflags=0x%x,", use->rpu_raflags);
1108				if (~use->rpu_vltime == 0)
1109					printf("vltime=infty,");
1110				else
1111					printf("vltime=%u,",
1112					    EXTRACT_32BITS(&use->rpu_vltime));
1113				if (~use->rpu_pltime == 0)
1114					printf("pltime=infty,");
1115				else
1116					printf("pltime=%u,",
1117					    EXTRACT_32BITS(&use->rpu_pltime));
1118			}
1119			if (inet_ntop(AF_INET6, &use->rpu_prefix, hbuf,
1120			    sizeof(hbuf)))
1121				printf("%s/%u/%u", hbuf, use->rpu_uselen,
1122				    use->rpu_keeplen);
1123			else
1124				printf("?/%u/%u", use->rpu_uselen,
1125				    use->rpu_keeplen);
1126			/*(*/
1127			printf(")");
1128		}
1129	}
1130
1131	return;
1132
1133trunc:
1134	fputs("[|icmp6]", stdout);
1135}
1136
1137#endif /* INET6 */
1138