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