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