xref: /freebsd/contrib/tcpdump/print-icmp6.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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
23 static const char rcsid[] =
24     "@(#) $Header: /tcpdump/master/tcpdump/print-icmp6.c,v 1.2.2.1 2000/01/11 06:58:24 fenner Exp $";
25 #endif
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #ifdef INET6
32 
33 #include <ctype.h>
34 
35 #include <sys/param.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 
40 #include <net/if.h>
41 
42 #include <netinet/in.h>
43 #include <netinet/if_ether.h>
44 #include <netinet/in_systm.h>
45 #include <netinet/ip.h>
46 #include <netinet/ip_icmp.h>
47 #include <netinet/ip_var.h>
48 #include <netinet/udp.h>
49 #include <netinet/udp_var.h>
50 #include <netinet/tcp.h>
51 
52 #include <arpa/inet.h>
53 
54 #include <stdio.h>
55 
56 #include <netinet/ip6.h>
57 #include <netinet/icmp6.h>
58 
59 #include "interface.h"
60 #include "addrtoname.h"
61 
62 void icmp6_opt_print(const u_char *, int);
63 void mld6_print(const u_char *);
64 
65 void
66 icmp6_print(register const u_char *bp, register const u_char *bp2)
67 {
68 	register const struct icmp6_hdr *dp;
69 	register const struct ip6_hdr *ip;
70 	register const char *str;
71 	register const struct ip6_hdr *oip;
72 	register const struct udphdr *ouh;
73 	register int hlen, dport;
74 	register const u_char *ep;
75 	char buf[256];
76 	int icmp6len;
77 
78 #if 0
79 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
80 #endif
81 
82 	dp = (struct icmp6_hdr *)bp;
83 	ip = (struct ip6_hdr *)bp2;
84 	oip = (struct ip6_hdr *)(dp + 1);
85 	str = buf;
86 	/* 'ep' points to the end of avaible data. */
87 	ep = snapend;
88 	if (ip->ip6_plen)
89 		icmp6len = (ntohs(ip->ip6_plen) + sizeof(struct ip6_hdr) -
90 			    (bp - bp2));
91 	else			/* XXX: jumbo payload case... */
92 		icmp6len = snapend - bp;
93 
94 #if 0
95         (void)printf("%s > %s: ",
96 		ip6addr_string(&ip->ip6_src),
97 		ip6addr_string(&ip->ip6_dst));
98 #endif
99 
100 	TCHECK(dp->icmp6_code);
101 	switch(dp->icmp6_type) {
102 	case ICMP6_DST_UNREACH:
103 		TCHECK(oip->ip6_dst);
104 		switch (dp->icmp6_code) {
105 		case ICMP6_DST_UNREACH_NOROUTE:
106 			printf("icmp6: %s unreachable route",
107 			       ip6addr_string(&oip->ip6_dst));
108 			break;
109 		case ICMP6_DST_UNREACH_ADMIN:
110 			printf("icmp6: %s unreachable prohibited",
111 			       ip6addr_string(&oip->ip6_dst));
112 			break;
113 #ifdef ICMP6_DST_UNREACH_BEYONDSCOPE
114 		case ICMP6_DST_UNREACH_BEYONDSCOPE:
115 #else
116 		case ICMP6_DST_UNREACH_NOTNEIGHBOR:
117 #endif
118 			printf("icmp6: %s beyond scope of source address %s",
119 			       ip6addr_string(&oip->ip6_dst),
120 			       ip6addr_string(&oip->ip6_src));
121 			break;
122 		case ICMP6_DST_UNREACH_ADDR:
123 			printf("icmp6: %s unreachable address",
124 			       ip6addr_string(&oip->ip6_dst));
125 			break;
126 		case ICMP6_DST_UNREACH_NOPORT:
127 			TCHECK(oip->ip6_nxt);
128 			hlen = sizeof(struct ip6_hdr);
129 			ouh = (struct udphdr *)(((u_char *)oip) + hlen);
130 			dport = ntohs(ouh->uh_dport);
131 			switch (oip->ip6_nxt) {
132 			case IPPROTO_TCP:
133 				printf("icmp6: %s tcp port %s unreachable",
134 					ip6addr_string(&oip->ip6_dst),
135 					tcpport_string(dport));
136 				break;
137 			case IPPROTO_UDP:
138 				printf("icmp6: %s udp port %s unreachable",
139 					ip6addr_string(&oip->ip6_dst),
140 					udpport_string(dport));
141 				break;
142 			default:
143 				printf("icmp6: %s protocol %d port %d unreachable",
144 					ip6addr_string(&oip->ip6_dst),
145 					oip->ip6_nxt, dport);
146 				break;
147 			}
148 			break;
149 		default:
150 			printf("icmp6: %s unreachable code-#%d",
151 				ip6addr_string(&oip->ip6_dst),
152 				dp->icmp6_code);
153 			break;
154 		}
155 		break;
156 	case ICMP6_PACKET_TOO_BIG:
157 		TCHECK(dp->icmp6_mtu);
158 		printf("icmp6: too big %u\n", (u_int32_t)ntohl(dp->icmp6_mtu));
159 		break;
160 	case ICMP6_TIME_EXCEEDED:
161 		TCHECK(oip->ip6_dst);
162 		switch (dp->icmp6_code) {
163 		case ICMP6_TIME_EXCEED_TRANSIT:
164 			printf("icmp6: time exceeded in-transit for %s",
165 				ip6addr_string(&oip->ip6_dst));
166 			break;
167 		case ICMP6_TIME_EXCEED_REASSEMBLY:
168 			printf("icmp6: ip6 reassembly time exceeded");
169 			break;
170 		default:
171 			printf("icmp6: time exceeded code-#%d",
172 				dp->icmp6_code);
173 			break;
174 		}
175 		break;
176 	case ICMP6_PARAM_PROB:
177 		TCHECK(oip->ip6_dst);
178 		switch (dp->icmp6_code) {
179 		case ICMP6_PARAMPROB_HEADER:
180 			printf("icmp6: parameter problem errorneous - octet %u\n",
181 				(u_int32_t)ntohl(dp->icmp6_pptr));
182 			break;
183 		case ICMP6_PARAMPROB_NEXTHEADER:
184 			printf("icmp6: parameter problem next header - octet %u\n",
185 				(u_int32_t)ntohl(dp->icmp6_pptr));
186 			break;
187 		case ICMP6_PARAMPROB_OPTION:
188 			printf("icmp6: parameter problem option - octet %u\n",
189 				(u_int32_t)ntohl(dp->icmp6_pptr));
190 			break;
191 		default:
192 			printf("icmp6: parameter problem code-#%d",
193 			       dp->icmp6_code);
194 			break;
195 		}
196 		break;
197 	case ICMP6_ECHO_REQUEST:
198 		printf("icmp6: echo request");
199 		break;
200 	case ICMP6_ECHO_REPLY:
201 		printf("icmp6: echo reply");
202 		break;
203 	case ICMP6_MEMBERSHIP_QUERY:
204 		printf("icmp6: multicast listener query ");
205 		mld6_print((const u_char *)dp);
206 		break;
207 	case ICMP6_MEMBERSHIP_REPORT:
208 		printf("icmp6: multicast listener report ");
209 		mld6_print((const u_char *)dp);
210 		break;
211 	case ICMP6_MEMBERSHIP_REDUCTION:
212 		printf("icmp6: multicast listener done ");
213 		mld6_print((const u_char *)dp);
214 		break;
215 	case ND_ROUTER_SOLICIT:
216 		printf("icmp6: router solicitation ");
217 		if (vflag) {
218 #define RTSOLLEN 8
219 		        icmp6_opt_print((const u_char *)dp + RTSOLLEN,
220 					icmp6len - RTSOLLEN);
221 		}
222 		break;
223 	case ND_ROUTER_ADVERT:
224 		printf("icmp6: router advertisement");
225 		if (vflag) {
226 			struct nd_router_advert *p;
227 
228 			p = (struct nd_router_advert *)dp;
229 			TCHECK(p->nd_ra_retransmit);
230 			printf("(chlim=%d, ", (int)p->nd_ra_curhoplimit);
231 			if (p->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
232 				printf("M");
233 			if (p->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
234 				printf("O");
235 			if (p->nd_ra_flags_reserved != 0)
236 				printf(" ");
237 			printf("router_ltime=%d, ", ntohs(p->nd_ra_router_lifetime));
238 			printf("reachable_time=%u, ",
239 				(u_int32_t)ntohl(p->nd_ra_reachable));
240 			printf("retrans_time=%u)",
241 				(u_int32_t)ntohl(p->nd_ra_retransmit));
242 #define RTADVLEN 16
243 		        icmp6_opt_print((const u_char *)dp + RTADVLEN,
244 					icmp6len - RTADVLEN);
245 		}
246 		break;
247 	case ND_NEIGHBOR_SOLICIT:
248 	    {
249 		struct nd_neighbor_solicit *p;
250 		p = (struct nd_neighbor_solicit *)dp;
251 		TCHECK(p->nd_ns_target);
252 		printf("icmp6: neighbor sol: who has %s",
253 			ip6addr_string(&p->nd_ns_target));
254 		if (vflag) {
255 #define NDSOLLEN 24
256 		        icmp6_opt_print((const u_char *)dp + NDSOLLEN,
257 					icmp6len - NDSOLLEN);
258 		}
259 	    }
260 		break;
261 	case ND_NEIGHBOR_ADVERT:
262 	    {
263 		struct nd_neighbor_advert *p;
264 
265 		p = (struct nd_neighbor_advert *)dp;
266 		TCHECK(p->nd_na_target);
267 		printf("icmp6: neighbor adv: tgt is %s",
268 			ip6addr_string(&p->nd_na_target));
269                 if (vflag) {
270 #define ND_NA_FLAG_ALL	\
271 	(ND_NA_FLAG_ROUTER|ND_NA_FLAG_SOLICITED|ND_NA_FLAG_OVERRIDE)
272 			/* we don't need ntohl() here.  see advanced-api-04. */
273 			if (p->nd_na_flags_reserved &  ND_NA_FLAG_ALL) {
274 #undef ND_NA_FLAG_ALL
275 				u_int32_t flags;
276 
277 				flags = p->nd_na_flags_reserved;
278 				printf("(");
279 				if (flags & ND_NA_FLAG_ROUTER)
280 					printf("R");
281 				if (flags & ND_NA_FLAG_SOLICITED)
282 					printf("S");
283 				if (flags & ND_NA_FLAG_OVERRIDE)
284 					printf("O");
285 				printf(")");
286 			}
287 #define NDADVLEN 24
288 		        icmp6_opt_print((const u_char *)dp + NDADVLEN,
289 					icmp6len - NDADVLEN);
290 		}
291 	    }
292 		break;
293 	case ND_REDIRECT:
294 	{
295 #define RDR(i) ((struct nd_redirect *)(i))
296 		char tgtbuf[INET6_ADDRSTRLEN], dstbuf[INET6_ADDRSTRLEN];
297 
298 		TCHECK(RDR(dp)->nd_rd_dst);
299 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_target,
300 			  tgtbuf, INET6_ADDRSTRLEN);
301 		inet_ntop(AF_INET6, &RDR(dp)->nd_rd_dst,
302 			  dstbuf, INET6_ADDRSTRLEN);
303 		printf("icmp6: redirect %s to %s", dstbuf, tgtbuf);
304 #define REDIRECTLEN 40
305 		if (vflag) {
306 			icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
307 					icmp6len - REDIRECTLEN);
308 		}
309 		break;
310 	}
311 	case ICMP6_ROUTER_RENUMBERING:
312 		switch (dp->icmp6_code) {
313 		case ICMP6_ROUTER_RENUMBERING_COMMAND:
314 			printf("icmp6: router renum command");
315 			break;
316 		case ICMP6_ROUTER_RENUMBERING_RESULT:
317 			printf("icmp6: router renum result");
318 			break;
319 		default:
320 			printf("icmp6: router renum code-#%d", dp->icmp6_code);
321 			break;
322 		}
323 		break;
324 #ifdef ICMP6_WRUREQUEST
325 	case ICMP6_WRUREQUEST:	/*ICMP6_FQDN_QUERY*/
326 	    {
327 		int siz;
328 		siz = ep - (u_char *)(dp + 1);
329 		if (siz == 4)
330 			printf("icmp6: who-are-you request");
331 		else {
332 			printf("icmp6: FQDN request");
333 			if (vflag) {
334 				if (siz < 8)
335 					printf("?(icmp6_data %d bytes)", siz);
336 				else if (8 < siz)
337 					printf("?(extra %d bytes)", siz - 8);
338 			}
339 		}
340 		break;
341 	    }
342 #endif /*ICMP6_WRUREQUEST*/
343 #ifdef ICMP6_WRUREPLY
344 	case ICMP6_WRUREPLY:	/*ICMP6_FQDN_REPLY*/
345 	    {
346 		enum { UNKNOWN, WRU, FQDN } mode = UNKNOWN;
347 		u_char const *buf;
348 		u_char const *cp = NULL;
349 
350 		buf = (u_char *)(dp + 1);
351 
352 		/* fair guess */
353 		if (buf[12] == ep - buf - 13)
354 			mode = FQDN;
355 		else if (dp->icmp6_code == 1)
356 			mode = FQDN;
357 
358 		/* wild guess */
359 		if (mode == UNKNOWN) {
360 			cp = buf + 4;
361 			while (cp < ep) {
362 				if (!isprint(*cp++))
363 					mode = FQDN;
364 			}
365 		}
366 #ifndef abs
367 #define abs(a)	((0 < (a)) ? (a) : -(a))
368 #endif
369 		if (mode == UNKNOWN && 2 < abs(buf[12] - (ep - buf - 13)))
370 			mode = WRU;
371 		if (mode == UNKNOWN)
372 			mode = FQDN;
373 
374 		if (mode == WRU) {
375 			cp = buf + 4;
376 			printf("icmp6: who-are-you reply(\"");
377 		} else if (mode == FQDN) {
378 			cp = buf + 13;
379 			printf("icmp6: FQDN reply(\"");
380 		}
381 		for (; cp < ep; cp++)
382 			printf((isprint(*cp) ? "%c" : "\\%03o"), *cp);
383 		printf("\"");
384 		if (vflag) {
385 			printf(",%s", mode == FQDN ? "FQDN" : "WRU");
386 			if (mode == FQDN) {
387 				long ttl;
388 				ttl = (long)ntohl(*(u_long *)&buf[8]);
389 				if (dp->icmp6_code == 1)
390 					printf(",TTL=unknown");
391 				else if (ttl < 0)
392 					printf(",TTL=%ld:invalid", ttl);
393 				else
394 					printf(",TTL=%ld", ttl);
395 				if (buf[12] != ep - buf - 13) {
396 					(void)printf(",invalid namelen:%d/%u",
397 						buf[12],
398 						(unsigned int)(ep - buf - 13));
399 				}
400 			}
401 		}
402 		printf(")");
403 		break;
404 	    }
405 #endif /*ICMP6_WRUREPLY*/
406 	default:
407 		printf("icmp6: type-#%d", dp->icmp6_type);
408 		break;
409 	}
410 	return;
411 trunc:
412 	fputs("[|icmp6]", stdout);
413 #if 0
414 #undef TCHECK
415 #endif
416 }
417 
418 void
419 icmp6_opt_print(register const u_char *bp, int resid)
420 {
421 	register const struct nd_opt_hdr *op;
422 	register const struct nd_opt_hdr *opl;	/* why there's no struct? */
423 	register const struct nd_opt_prefix_info *opp;
424 	register const struct icmp6_opts_redirect *opr;
425 	register const struct nd_opt_mtu *opm;
426 	register const u_char *ep;
427 	int	opts_len;
428 #if 0
429 	register const struct ip6_hdr *ip;
430 	register const char *str;
431 	register const struct ip6_hdr *oip;
432 	register const struct udphdr *ouh;
433 	register int hlen, dport;
434 	char buf[256];
435 #endif
436 
437 #if 0
438 #define TCHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) goto trunc
439 #endif
440 #define ECHECK(var) if ((u_char *)&(var) > ep - sizeof(var)) return
441 
442 	op = (struct nd_opt_hdr *)bp;
443 #if 0
444 	ip = (struct ip6_hdr *)bp2;
445 	oip = &dp->icmp6_ip6;
446 	str = buf;
447 #endif
448 	/* 'ep' points to the end of avaible data. */
449 	ep = snapend;
450 
451 	ECHECK(op->nd_opt_len);
452 	if (resid <= 0)
453 		return;
454 	switch(op->nd_opt_type) {
455 	case ND_OPT_SOURCE_LINKADDR:
456 		opl = (struct nd_opt_hdr *)op;
457 #if 1
458 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
459 			goto trunc;
460 #else
461 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
462 #endif
463 		printf("(src lladdr: %s",
464 			etheraddr_string((u_char *)(opl + 1)));
465 		if (opl->nd_opt_len != 1)
466 			printf("!");
467 		printf(")");
468 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
469 				resid - (op->nd_opt_len << 3));
470 		break;
471 	case ND_OPT_TARGET_LINKADDR:
472 		opl = (struct nd_opt_hdr *)op;
473 #if 1
474 		if ((u_char *)opl + (opl->nd_opt_len << 3) > ep)
475 			goto trunc;
476 #else
477 		TCHECK((u_char *)opl + (opl->nd_opt_len << 3) - 1);
478 #endif
479 		printf("(tgt lladdr: %s",
480 			etheraddr_string((u_char *)(opl + 1)));
481 		if (opl->nd_opt_len != 1)
482 			printf("!");
483 		printf(")");
484 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
485 				resid - (op->nd_opt_len << 3));
486 		break;
487 	case ND_OPT_PREFIX_INFORMATION:
488 		opp = (struct nd_opt_prefix_info *)op;
489 		TCHECK(opp->nd_opt_pi_prefix);
490 		printf("(prefix info: ");
491 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
492 		       printf("L");
493 		if (opp->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)
494 		       printf("A");
495 		if (opp->nd_opt_pi_flags_reserved)
496 			printf(" ");
497 		printf("valid_ltime=");
498 		if ((u_int32_t)ntohl(opp->nd_opt_pi_valid_time) == ~0U)
499 			printf("infinity");
500 		else {
501 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_valid_time));
502 		}
503 		printf(", ");
504 		printf("preffered_ltime=");
505 		if ((u_int32_t)ntohl(opp->nd_opt_pi_preferred_time) == ~0U)
506 			printf("infinity");
507 		else {
508 			printf("%u", (u_int32_t)ntohl(opp->nd_opt_pi_preferred_time));
509 		}
510 		printf(", ");
511 		printf("prefix=%s/%d", ip6addr_string(&opp->nd_opt_pi_prefix),
512 			opp->nd_opt_pi_prefix_len);
513 		if (opp->nd_opt_pi_len != 4)
514 			printf("!");
515 		printf(")");
516 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
517 				resid - (op->nd_opt_len << 3));
518 		break;
519 	case ND_OPT_REDIRECTED_HEADER:
520 		opr = (struct icmp6_opts_redirect *)op;
521 		printf("(redirect)");
522 		/* xxx */
523 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
524 				resid - (op->nd_opt_len << 3));
525 		break;
526 	case ND_OPT_MTU:
527 		opm = (struct nd_opt_mtu *)op;
528 		TCHECK(opm->nd_opt_mtu_mtu);
529 		printf("(mtu: ");
530 		printf("mtu=%u", (u_int32_t)ntohl(opm->nd_opt_mtu_mtu));
531 		if (opm->nd_opt_mtu_len != 1)
532 			printf("!");
533 		printf(")");
534 		icmp6_opt_print((const u_char *)op + (op->nd_opt_len << 3),
535 				resid - (op->nd_opt_len << 3));
536 		break;
537 	default:
538 		opts_len = op->nd_opt_len;
539 		printf("(unknwon opt_type=%d, opt_len=%d)",
540 		       op->nd_opt_type, opts_len);
541 		if (opts_len == 0)
542 			opts_len = 1; /* XXX */
543 		icmp6_opt_print((const u_char *)op + (opts_len << 3),
544 				resid - (opts_len << 3));
545 		break;
546 	}
547 	return;
548  trunc:
549 	fputs("[ndp opt]", stdout);
550 	return;
551 #if 0
552 #undef TCHECK
553 #endif
554 #undef ECHECK
555 }
556 
557 void
558 mld6_print(register const u_char *bp)
559 {
560 	register struct mld6_hdr *mp = (struct mld6_hdr *)bp;
561 	register const u_char *ep;
562 
563 	/* 'ep' points to the end of avaible data. */
564 	ep = snapend;
565 
566 	if ((u_char *)mp + sizeof(*mp) > ep)
567 		return;
568 
569 	printf("max resp delay: %d ", ntohs(mp->mld6_maxdelay));
570 	printf("addr: %s", ip6addr_string(&mp->mld6_addr));
571 
572 	return;
573 }
574 #endif /* INET6 */
575