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