xref: /netbsd-src/external/bsd/tcpdump/dist/print-icmp.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
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 #include <sys/cdefs.h>
23 #ifndef lint
24 __RCSID("$NetBSD: print-icmp.c,v 1.13 2023/08/17 20:19:40 christos Exp $");
25 #endif
26 
27 /* \summary: Internet Control Message Protocol (ICMP) printer */
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 
33 #include "netdissect-stdinc.h"
34 
35 #include <stdio.h>
36 #include <string.h>
37 
38 #include "netdissect.h"
39 #include "addrtoname.h"
40 #include "extract.h"
41 
42 #include "ip.h"
43 #include "udp.h"
44 #include "ipproto.h"
45 #include "mpls.h"
46 
47 /*
48  * Interface Control Message Protocol Definitions.
49  * Per RFC 792, September 1981.
50  */
51 
52 /*
53  * Structure of an icmp header.
54  */
55 struct icmp {
56 	nd_uint8_t  icmp_type;		/* type of message, see below */
57 	nd_uint8_t  icmp_code;		/* type sub code */
58 	nd_uint16_t icmp_cksum;		/* ones complement cksum of struct */
59 	union {
60 		nd_uint8_t ih_pptr;	/* ICMP_PARAMPROB */
61 		nd_ipv4 ih_gwaddr;	/* ICMP_REDIRECT */
62 		struct ih_idseq {
63 			nd_uint16_t icd_id;
64 			nd_uint16_t icd_seq;
65 		} ih_idseq;
66 		nd_uint32_t ih_void;
67 	} icmp_hun;
68 #define	icmp_pptr	icmp_hun.ih_pptr
69 #define	icmp_gwaddr	icmp_hun.ih_gwaddr
70 #define	icmp_id		icmp_hun.ih_idseq.icd_id
71 #define	icmp_seq	icmp_hun.ih_idseq.icd_seq
72 #define	icmp_void	icmp_hun.ih_void
73 	union {
74 		struct id_ts {
75 			nd_uint32_t its_otime;
76 			nd_uint32_t its_rtime;
77 			nd_uint32_t its_ttime;
78 		} id_ts;
79 		struct id_ip  {
80 			struct ip idi_ip;
81 			/* options and then 64 bits of data */
82 		} id_ip;
83 		nd_uint32_t id_mask;
84 		nd_byte id_data[1];
85 	} icmp_dun;
86 #define	icmp_otime	icmp_dun.id_ts.its_otime
87 #define	icmp_rtime	icmp_dun.id_ts.its_rtime
88 #define	icmp_ttime	icmp_dun.id_ts.its_ttime
89 #define	icmp_ip		icmp_dun.id_ip.idi_ip
90 #define	icmp_mask	icmp_dun.id_mask
91 #define	icmp_data	icmp_dun.id_data
92 } UNALIGNED;
93 
94 /*
95  * Lower bounds on packet lengths for various types.
96  * For the error advice packets must first insure that the
97  * packet is large enough to contain the returned ip header.
98  * Only then can we do the check to see if 64 bits of packet
99  * data have been returned, since we need to check the returned
100  * ip header length.
101  */
102 #define	ICMP_MINLEN	8				/* abs minimum */
103 #define ICMP_EXTD_MINLEN (156 - sizeof (struct ip))     /* draft-bonica-internet-icmp-08 */
104 #define	ICMP_TSLEN	(8 + 3 * sizeof (uint32_t))	/* timestamp */
105 #define	ICMP_MASKLEN	12				/* address mask */
106 #define	ICMP_ADVLENMIN	(8 + sizeof (struct ip) + 8)	/* min */
107 #define	ICMP_ADVLEN(p)	(8 + (IP_HL(&(p)->icmp_ip) << 2) + 8)
108 	/* N.B.: must separately check that ip_hl >= 5 */
109 
110 /*
111  * Definition of type and code field values.
112  */
113 #define	ICMP_ECHOREPLY		0		/* echo reply */
114 #define	ICMP_UNREACH		3		/* dest unreachable, codes: */
115 #define		ICMP_UNREACH_NET	0		/* bad net */
116 #define		ICMP_UNREACH_HOST	1		/* bad host */
117 #define		ICMP_UNREACH_PROTOCOL	2		/* bad protocol */
118 #define		ICMP_UNREACH_PORT	3		/* bad port */
119 #define		ICMP_UNREACH_NEEDFRAG	4		/* IP_DF caused drop */
120 #define		ICMP_UNREACH_SRCFAIL	5		/* src route failed */
121 #define		ICMP_UNREACH_NET_UNKNOWN 6		/* unknown net */
122 #define		ICMP_UNREACH_HOST_UNKNOWN 7		/* unknown host */
123 #define		ICMP_UNREACH_ISOLATED	8		/* src host isolated */
124 #define		ICMP_UNREACH_NET_PROHIB	9		/* prohibited access */
125 #define		ICMP_UNREACH_HOST_PROHIB 10		/* ditto */
126 #define		ICMP_UNREACH_TOSNET	11		/* bad tos for net */
127 #define		ICMP_UNREACH_TOSHOST	12		/* bad tos for host */
128 #define	ICMP_SOURCEQUENCH	4		/* packet lost, slow down */
129 #define	ICMP_REDIRECT		5		/* shorter route, codes: */
130 #define		ICMP_REDIRECT_NET	0		/* for network */
131 #define		ICMP_REDIRECT_HOST	1		/* for host */
132 #define		ICMP_REDIRECT_TOSNET	2		/* for tos and net */
133 #define		ICMP_REDIRECT_TOSHOST	3		/* for tos and host */
134 #define	ICMP_ECHO		8		/* echo service */
135 #define	ICMP_ROUTERADVERT	9		/* router advertisement */
136 #define	ICMP_ROUTERSOLICIT	10		/* router solicitation */
137 #define	ICMP_TIMXCEED		11		/* time exceeded, code: */
138 #define		ICMP_TIMXCEED_INTRANS	0		/* ttl==0 in transit */
139 #define		ICMP_TIMXCEED_REASS	1		/* ttl==0 in reass */
140 #define	ICMP_PARAMPROB		12		/* ip header bad */
141 #define		ICMP_PARAMPROB_OPTABSENT 1		/* req. opt. absent */
142 #define	ICMP_TSTAMP		13		/* timestamp request */
143 #define	ICMP_TSTAMPREPLY	14		/* timestamp reply */
144 #define	ICMP_IREQ		15		/* information request */
145 #define	ICMP_IREQREPLY		16		/* information reply */
146 #define	ICMP_MASKREQ		17		/* address mask request */
147 #define	ICMP_MASKREPLY		18		/* address mask reply */
148 
149 #define	ICMP_MAXTYPE		18
150 
151 #define ICMP_ERRTYPE(type) \
152 	((type) == ICMP_UNREACH || (type) == ICMP_SOURCEQUENCH || \
153 	(type) == ICMP_REDIRECT || (type) == ICMP_TIMXCEED || \
154 	(type) == ICMP_PARAMPROB)
155 #define	ICMP_MULTIPART_EXT_TYPE(type) \
156 	((type) == ICMP_UNREACH || \
157          (type) == ICMP_TIMXCEED || \
158          (type) == ICMP_PARAMPROB)
159 /* rfc1700 */
160 #ifndef ICMP_UNREACH_NET_UNKNOWN
161 #define ICMP_UNREACH_NET_UNKNOWN	6	/* destination net unknown */
162 #endif
163 #ifndef ICMP_UNREACH_HOST_UNKNOWN
164 #define ICMP_UNREACH_HOST_UNKNOWN	7	/* destination host unknown */
165 #endif
166 #ifndef ICMP_UNREACH_ISOLATED
167 #define ICMP_UNREACH_ISOLATED		8	/* source host isolated */
168 #endif
169 #ifndef ICMP_UNREACH_NET_PROHIB
170 #define ICMP_UNREACH_NET_PROHIB		9	/* admin prohibited net */
171 #endif
172 #ifndef ICMP_UNREACH_HOST_PROHIB
173 #define ICMP_UNREACH_HOST_PROHIB	10	/* admin prohibited host */
174 #endif
175 #ifndef ICMP_UNREACH_TOSNET
176 #define ICMP_UNREACH_TOSNET		11	/* tos prohibited net */
177 #endif
178 #ifndef ICMP_UNREACH_TOSHOST
179 #define ICMP_UNREACH_TOSHOST		12	/* tos prohibited host */
180 #endif
181 
182 /* rfc1716 */
183 #ifndef ICMP_UNREACH_FILTER_PROHIB
184 #define ICMP_UNREACH_FILTER_PROHIB	13	/* admin prohibited filter */
185 #endif
186 #ifndef ICMP_UNREACH_HOST_PRECEDENCE
187 #define ICMP_UNREACH_HOST_PRECEDENCE	14	/* host precedence violation */
188 #endif
189 #ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
190 #define ICMP_UNREACH_PRECEDENCE_CUTOFF	15	/* precedence cutoff */
191 #endif
192 
193 /* Most of the icmp types */
194 static const struct tok icmp2str[] = {
195 	{ ICMP_ECHOREPLY,		"echo reply" },
196 	{ ICMP_SOURCEQUENCH,		"source quench" },
197 	{ ICMP_ECHO,			"echo request" },
198 	{ ICMP_ROUTERSOLICIT,		"router solicitation" },
199 	{ ICMP_TSTAMP,			"time stamp request" },
200 	{ ICMP_TSTAMPREPLY,		"time stamp reply" },
201 	{ ICMP_IREQ,			"information request" },
202 	{ ICMP_IREQREPLY,		"information reply" },
203 	{ ICMP_MASKREQ,			"address mask request" },
204 	{ 0,				NULL }
205 };
206 
207 /* rfc1191 */
208 struct mtu_discovery {
209 	nd_uint16_t unused;
210 	nd_uint16_t nexthopmtu;
211 };
212 
213 /* rfc1256 */
214 struct ih_rdiscovery {
215 	nd_uint8_t ird_addrnum;
216 	nd_uint8_t ird_addrsiz;
217 	nd_uint16_t ird_lifetime;
218 };
219 
220 struct id_rdiscovery {
221 	nd_uint32_t ird_addr;
222 	nd_uint32_t ird_pref;
223 };
224 
225 /*
226  * RFC 4884 - Extended ICMP to Support Multi-Part Messages
227  *
228  * This is a general extension mechanism, based on the mechanism
229  * in draft-bonica-icmp-mpls-02 ICMP Extensions for MultiProtocol
230  * Label Switching.
231  *
232  * The Destination Unreachable, Time Exceeded
233  * and Parameter Problem messages are slightly changed as per
234  * the above RFC. A new Length field gets added to give
235  * the caller an idea about the length of the piggybacked
236  * IP packet before the extension header starts.
237  *
238  * The Length field represents length of the padded "original datagram"
239  * field  measured in 32-bit words.
240  *
241  * 0                   1                   2                   3
242  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
243  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
244  * |     Type      |     Code      |          Checksum             |
245  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
246  * |     unused    |    Length     |          unused               |
247  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248  * |      Internet Header + leading octets of original datagram    |
249  * |                                                               |
250  * |                           //                                  |
251  * |                                                               |
252  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
253  */
254 
255 struct icmp_ext_t {
256     nd_uint8_t  icmp_type;
257     nd_uint8_t  icmp_code;
258     nd_uint16_t icmp_checksum;
259     nd_byte     icmp_reserved;
260     nd_uint8_t  icmp_length;
261     nd_byte     icmp_reserved2[2];
262     nd_byte     icmp_ext_legacy_header[128]; /* extension header starts 128 bytes after ICMP header */
263     nd_byte     icmp_ext_version_res[2];
264     nd_uint16_t icmp_ext_checksum;
265     nd_byte     icmp_ext_data[1];
266 };
267 
268 /*
269  * Extract version from the first octet of icmp_ext_version_res.
270  */
271 #define ICMP_EXT_EXTRACT_VERSION(x) (((x)&0xf0)>>4)
272 
273 /*
274  * Current version.
275  */
276 #define ICMP_EXT_VERSION 2
277 
278 /*
279  * Extension object class numbers.
280  *
281  * Class 1 dates back to draft-bonica-icmp-mpls-02.
282  */
283 
284 /* rfc4950  */
285 #define MPLS_STACK_ENTRY_OBJECT_CLASS            1
286 
287 struct icmp_multipart_ext_object_header_t {
288     nd_uint16_t length;
289     nd_uint8_t  class_num;
290     nd_uint8_t  ctype;
291 };
292 
293 static const struct tok icmp_multipart_ext_obj_values[] = {
294     { 1, "MPLS Stack Entry" },
295     { 2, "Interface Identification" },
296     { 0, NULL}
297 };
298 
299 /* prototypes */
300 const char *icmp_tstamp_print(u_int);
301 
302 /* print the milliseconds since midnight UTC */
303 const char *
304 icmp_tstamp_print(u_int tstamp)
305 {
306     u_int msec,sec,min,hrs;
307 
308     static char buf[64];
309 
310     msec = tstamp % 1000;
311     sec = tstamp / 1000;
312     min = sec / 60; sec -= min * 60;
313     hrs = min / 60; min -= hrs * 60;
314     snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%03u",hrs,min,sec,msec);
315     return buf;
316 }
317 
318 UNALIGNED_OK
319 void
320 icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *bp2,
321            int fragmented)
322 {
323 	char *cp;
324 	const struct icmp *dp;
325 	uint8_t icmp_type, icmp_code;
326         const struct icmp_ext_t *ext_dp;
327 	const struct ip *ip;
328 	const char *str;
329 	const struct ip *oip;
330 	uint8_t ip_proto;
331 	const struct udphdr *ouh;
332         const uint8_t *obj_tptr;
333         uint32_t raw_label;
334 	const struct icmp_multipart_ext_object_header_t *icmp_multipart_ext_object_header;
335 	u_int hlen, mtu, obj_tlen, obj_class_num, obj_ctype;
336 	uint16_t dport;
337 	char buf[MAXHOSTNAMELEN + 100];
338 	struct cksum_vec vec[1];
339 
340 	ndo->ndo_protocol = "icmp";
341 	dp = (const struct icmp *)bp;
342         ext_dp = (const struct icmp_ext_t *)bp;
343 	ip = (const struct ip *)bp2;
344 	str = buf;
345 
346 	icmp_type = GET_U_1(dp->icmp_type);
347 	icmp_code = GET_U_1(dp->icmp_code);
348 	switch (icmp_type) {
349 
350 	case ICMP_ECHO:
351 	case ICMP_ECHOREPLY:
352 		(void)snprintf(buf, sizeof(buf), "echo %s, id %u, seq %u",
353                                icmp_type == ICMP_ECHO ?
354                                "request" : "reply",
355                                GET_BE_U_2(dp->icmp_id),
356                                GET_BE_U_2(dp->icmp_seq));
357 		break;
358 
359 	case ICMP_UNREACH:
360 		switch (icmp_code) {
361 
362 		case ICMP_UNREACH_NET:
363 			(void)snprintf(buf, sizeof(buf),
364 			    "net %s unreachable",
365 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
366 			break;
367 
368 		case ICMP_UNREACH_HOST:
369 			(void)snprintf(buf, sizeof(buf),
370 			    "host %s unreachable",
371 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
372 			break;
373 
374 		case ICMP_UNREACH_PROTOCOL:
375 			(void)snprintf(buf, sizeof(buf),
376 			    "%s protocol %u unreachable",
377 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
378 			    GET_U_1(dp->icmp_ip.ip_p));
379 			break;
380 
381 		case ICMP_UNREACH_PORT:
382 			ND_TCHECK_1(dp->icmp_ip.ip_p);
383 			oip = &dp->icmp_ip;
384 			hlen = IP_HL(oip) * 4;
385 			ouh = (const struct udphdr *)(((const u_char *)oip) + hlen);
386 			dport = GET_BE_U_2(ouh->uh_dport);
387 			ip_proto = GET_U_1(oip->ip_p);
388 			switch (ip_proto) {
389 
390 			case IPPROTO_TCP:
391 				(void)snprintf(buf, sizeof(buf),
392 					"%s tcp port %s unreachable",
393 					GET_IPADDR_STRING(oip->ip_dst),
394 					tcpport_string(ndo, dport));
395 				break;
396 
397 			case IPPROTO_UDP:
398 				(void)snprintf(buf, sizeof(buf),
399 					"%s udp port %s unreachable",
400 					GET_IPADDR_STRING(oip->ip_dst),
401 					udpport_string(ndo, dport));
402 				break;
403 
404 			default:
405 				(void)snprintf(buf, sizeof(buf),
406 					"%s protocol %u port %u unreachable",
407 					GET_IPADDR_STRING(oip->ip_dst),
408 					ip_proto, dport);
409 				break;
410 			}
411 			break;
412 
413 		case ICMP_UNREACH_NEEDFRAG:
414 		    {
415 			const struct mtu_discovery *mp;
416 			mp = (const struct mtu_discovery *)(const u_char *)&dp->icmp_void;
417 			mtu = GET_BE_U_2(mp->nexthopmtu);
418 			if (mtu) {
419 				(void)snprintf(buf, sizeof(buf),
420 				    "%s unreachable - need to frag (mtu %u)",
421 				    GET_IPADDR_STRING(dp->icmp_ip.ip_dst), mtu);
422 			} else {
423 				(void)snprintf(buf, sizeof(buf),
424 				    "%s unreachable - need to frag",
425 				    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
426 			}
427 		    }
428 			break;
429 
430 		case ICMP_UNREACH_SRCFAIL:
431 			(void)snprintf(buf, sizeof(buf),
432 			    "%s unreachable - source route failed",
433 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
434 			break;
435 
436 		case ICMP_UNREACH_NET_UNKNOWN:
437 			(void)snprintf(buf, sizeof(buf),
438 			    "net %s unreachable - unknown",
439 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
440 			break;
441 
442 		case ICMP_UNREACH_HOST_UNKNOWN:
443 			(void)snprintf(buf, sizeof(buf),
444 			    "host %s unreachable - unknown",
445 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
446 			break;
447 
448 		case ICMP_UNREACH_ISOLATED:
449 			(void)snprintf(buf, sizeof(buf),
450 			    "%s unreachable - source host isolated",
451 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
452 			break;
453 
454 		case ICMP_UNREACH_NET_PROHIB:
455 			(void)snprintf(buf, sizeof(buf),
456 			    "net %s unreachable - admin prohibited",
457 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
458 			break;
459 
460 		case ICMP_UNREACH_HOST_PROHIB:
461 			(void)snprintf(buf, sizeof(buf),
462 			    "host %s unreachable - admin prohibited",
463 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
464 			break;
465 
466 		case ICMP_UNREACH_TOSNET:
467 			(void)snprintf(buf, sizeof(buf),
468 			    "net %s unreachable - tos prohibited",
469 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
470 			break;
471 
472 		case ICMP_UNREACH_TOSHOST:
473 			(void)snprintf(buf, sizeof(buf),
474 			    "host %s unreachable - tos prohibited",
475 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
476 			break;
477 
478 		case ICMP_UNREACH_FILTER_PROHIB:
479 			(void)snprintf(buf, sizeof(buf),
480 			    "host %s unreachable - admin prohibited filter",
481 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
482 			break;
483 
484 		case ICMP_UNREACH_HOST_PRECEDENCE:
485 			(void)snprintf(buf, sizeof(buf),
486 			    "host %s unreachable - host precedence violation",
487 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
488 			break;
489 
490 		case ICMP_UNREACH_PRECEDENCE_CUTOFF:
491 			(void)snprintf(buf, sizeof(buf),
492 			    "host %s unreachable - precedence cutoff",
493 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
494 			break;
495 
496 		default:
497 			(void)snprintf(buf, sizeof(buf),
498 			    "%s unreachable - #%u",
499 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
500 			    icmp_code);
501 			break;
502 		}
503 		break;
504 
505 	case ICMP_REDIRECT:
506 		switch (icmp_code) {
507 
508 		case ICMP_REDIRECT_NET:
509 			(void)snprintf(buf, sizeof(buf),
510 			    "redirect %s to net %s",
511 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
512 			    GET_IPADDR_STRING(dp->icmp_gwaddr));
513 			break;
514 
515 		case ICMP_REDIRECT_HOST:
516 			(void)snprintf(buf, sizeof(buf),
517 			    "redirect %s to host %s",
518 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
519 			    GET_IPADDR_STRING(dp->icmp_gwaddr));
520 			break;
521 
522 		case ICMP_REDIRECT_TOSNET:
523 			(void)snprintf(buf, sizeof(buf),
524 			    "redirect-tos %s to net %s",
525 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
526 			    GET_IPADDR_STRING(dp->icmp_gwaddr));
527 			break;
528 
529 		case ICMP_REDIRECT_TOSHOST:
530 			(void)snprintf(buf, sizeof(buf),
531 			    "redirect-tos %s to host %s",
532 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
533 			    GET_IPADDR_STRING(dp->icmp_gwaddr));
534 			break;
535 
536 		default:
537 			(void)snprintf(buf, sizeof(buf),
538 			    "redirect-#%u %s to %s", icmp_code,
539 			    GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
540 			    GET_IPADDR_STRING(dp->icmp_gwaddr));
541 			break;
542 		}
543 		break;
544 
545 	case ICMP_ROUTERADVERT:
546 	    {
547 		const struct ih_rdiscovery *ihp;
548 		const struct id_rdiscovery *idp;
549 		u_int lifetime, num, size;
550 
551 		(void)snprintf(buf, sizeof(buf), "router advertisement");
552 		cp = buf + strlen(buf);
553 
554 		ihp = (const struct ih_rdiscovery *)&dp->icmp_void;
555 		ND_TCHECK_SIZE(ihp);
556 		(void)strncpy(cp, " lifetime ", sizeof(buf) - (cp - buf));
557 		cp = buf + strlen(buf);
558 		lifetime = GET_BE_U_2(ihp->ird_lifetime);
559 		if (lifetime < 60) {
560 			(void)snprintf(cp, sizeof(buf) - (cp - buf), "%u",
561 			    lifetime);
562 		} else if (lifetime < 60 * 60) {
563 			(void)snprintf(cp, sizeof(buf) - (cp - buf), "%u:%02u",
564 			    lifetime / 60, lifetime % 60);
565 		} else {
566 			(void)snprintf(cp, sizeof(buf) - (cp - buf),
567 			    "%u:%02u:%02u",
568 			    lifetime / 3600,
569 			    (lifetime % 3600) / 60,
570 			    lifetime % 60);
571 		}
572 		cp = buf + strlen(buf);
573 
574 		num = GET_U_1(ihp->ird_addrnum);
575 		(void)snprintf(cp, sizeof(buf) - (cp - buf), " %u:", num);
576 		cp = buf + strlen(buf);
577 
578 		size = GET_U_1(ihp->ird_addrsiz);
579 		if (size != 2) {
580 			(void)snprintf(cp, sizeof(buf) - (cp - buf),
581 			    " [size %u]", size);
582 			break;
583 		}
584 		idp = (const struct id_rdiscovery *)&dp->icmp_data;
585 		while (num > 0) {
586 			ND_TCHECK_SIZE(idp);
587 			(void)snprintf(cp, sizeof(buf) - (cp - buf), " {%s %u}",
588 			    GET_IPADDR_STRING(idp->ird_addr),
589 			    GET_BE_U_4(idp->ird_pref));
590 			cp = buf + strlen(buf);
591 			++idp;
592 		num--;
593 		}
594 	    }
595 		break;
596 
597 	case ICMP_TIMXCEED:
598 		ND_TCHECK_4(dp->icmp_ip.ip_dst);
599 		switch (icmp_code) {
600 
601 		case ICMP_TIMXCEED_INTRANS:
602 			str = "time exceeded in-transit";
603 			break;
604 
605 		case ICMP_TIMXCEED_REASS:
606 			str = "ip reassembly time exceeded";
607 			break;
608 
609 		default:
610 			(void)snprintf(buf, sizeof(buf), "time exceeded-#%u",
611 			    icmp_code);
612 			break;
613 		}
614 		break;
615 
616 	case ICMP_PARAMPROB:
617 		if (icmp_code)
618 			(void)snprintf(buf, sizeof(buf),
619 			    "parameter problem - code %u", icmp_code);
620 		else {
621 			(void)snprintf(buf, sizeof(buf),
622 			    "parameter problem - octet %u",
623 			    GET_U_1(dp->icmp_pptr));
624 		}
625 		break;
626 
627 	case ICMP_MASKREPLY:
628 		(void)snprintf(buf, sizeof(buf), "address mask is 0x%08x",
629 		    GET_BE_U_4(dp->icmp_mask));
630 		break;
631 
632 	case ICMP_TSTAMP:
633 		(void)snprintf(buf, sizeof(buf),
634 		    "time stamp query id %u seq %u",
635 		    GET_BE_U_2(dp->icmp_id),
636 		    GET_BE_U_2(dp->icmp_seq));
637 		break;
638 
639 	case ICMP_TSTAMPREPLY:
640 		ND_TCHECK_4(dp->icmp_ttime);
641 		(void)snprintf(buf, sizeof(buf),
642 		    "time stamp reply id %u seq %u: org %s",
643                                GET_BE_U_2(dp->icmp_id),
644                                GET_BE_U_2(dp->icmp_seq),
645                                icmp_tstamp_print(GET_BE_U_4(dp->icmp_otime)));
646 
647                 (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", recv %s",
648                          icmp_tstamp_print(GET_BE_U_4(dp->icmp_rtime)));
649                 (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", xmit %s",
650                          icmp_tstamp_print(GET_BE_U_4(dp->icmp_ttime)));
651                 break;
652 
653 	default:
654 		str = tok2str(icmp2str, "type-#%u", icmp_type);
655 		break;
656 	}
657 	ND_PRINT("ICMP %s, length %u", str, plen);
658 	if (ndo->ndo_vflag && !fragmented) { /* don't attempt checksumming if this is a frag */
659 		if (ND_TTEST_LEN(bp, plen)) {
660 			uint16_t sum;
661 
662 			vec[0].ptr = (const uint8_t *)(const void *)dp;
663 			vec[0].len = plen;
664 			sum = in_cksum(vec, 1);
665 			if (sum != 0) {
666 				uint16_t icmp_sum = GET_BE_U_2(dp->icmp_cksum);
667 				ND_PRINT(" (wrong icmp cksum %x (->%x)!)",
668 					     icmp_sum,
669 					     in_cksum_shouldbe(icmp_sum, sum));
670 			}
671 		}
672 	}
673 
674         /*
675          * print the remnants of the IP packet.
676          * save the snaplength as this may get overridden in the IP printer.
677          */
678 	if (ndo->ndo_vflag >= 1 && ICMP_ERRTYPE(icmp_type)) {
679 		const u_char *snapend_save;
680 
681 		bp += 8;
682 		ND_PRINT("\n\t");
683 		ip = (const struct ip *)bp;
684 		snapend_save = ndo->ndo_snapend;
685 		/*
686 		 * Update the snapend because extensions (MPLS, ...) may be
687 		 * present after the IP packet. In this case the current
688 		 * (outer) packet's snapend is not what ip_print() needs to
689 		 * decode an IP packet nested in the middle of an ICMP payload.
690 		 *
691 		 * This prevents that, in ip_print(), for the nested IP packet,
692 		 * the remaining length < remaining caplen.
693 		 */
694 		ndo->ndo_snapend = ND_MIN(bp + GET_BE_U_2(ip->ip_len),
695 					  ndo->ndo_snapend);
696 		ip_print(ndo, bp, GET_BE_U_2(ip->ip_len));
697 		ndo->ndo_snapend = snapend_save;
698 	}
699 
700 	/* ndo_protocol reassignment after ip_print() call */
701 	ndo->ndo_protocol = "icmp";
702 
703         /*
704          * Attempt to decode multi-part message extensions (rfc4884) only for some ICMP types.
705          */
706         if (ndo->ndo_vflag >= 1 && plen > ICMP_EXTD_MINLEN && ICMP_MULTIPART_EXT_TYPE(icmp_type)) {
707 
708             ND_TCHECK_SIZE(ext_dp);
709 
710             /*
711              * Check first if the multi-part extension header shows a non-zero length.
712              * If the length field is not set then silently verify the checksum
713              * to check if an extension header is present. This is expedient,
714              * however not all implementations set the length field proper.
715              */
716             if (GET_U_1(ext_dp->icmp_length) == 0 &&
717                 ND_TTEST_LEN(ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN)) {
718                 vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
719                 vec[0].len = plen - ICMP_EXTD_MINLEN;
720                 if (in_cksum(vec, 1)) {
721                     return;
722                 }
723             }
724 
725             ND_PRINT("\n\tICMP Multi-Part extension v%u",
726                    ICMP_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res)));
727 
728             /*
729              * Sanity checking of the header.
730              */
731             if (ICMP_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res)) !=
732                 ICMP_EXT_VERSION) {
733                 ND_PRINT(" packet not supported");
734                 return;
735             }
736 
737             hlen = plen - ICMP_EXTD_MINLEN;
738             if (ND_TTEST_LEN(ext_dp->icmp_ext_version_res, hlen)) {
739                 vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
740                 vec[0].len = hlen;
741                 ND_PRINT(", checksum 0x%04x (%scorrect), length %u",
742                        GET_BE_U_2(ext_dp->icmp_ext_checksum),
743                        in_cksum(vec, 1) ? "in" : "",
744                        hlen);
745             }
746 
747             hlen -= 4; /* subtract common header size */
748             obj_tptr = (const uint8_t *)ext_dp->icmp_ext_data;
749 
750             while (hlen > sizeof(struct icmp_multipart_ext_object_header_t)) {
751 
752                 icmp_multipart_ext_object_header = (const struct icmp_multipart_ext_object_header_t *)obj_tptr;
753                 ND_TCHECK_SIZE(icmp_multipart_ext_object_header);
754                 obj_tlen = GET_BE_U_2(icmp_multipart_ext_object_header->length);
755                 obj_class_num = GET_U_1(icmp_multipart_ext_object_header->class_num);
756                 obj_ctype = GET_U_1(icmp_multipart_ext_object_header->ctype);
757                 obj_tptr += sizeof(struct icmp_multipart_ext_object_header_t);
758 
759                 ND_PRINT("\n\t  %s Object (%u), Class-Type: %u, length %u",
760                        tok2str(icmp_multipart_ext_obj_values,"unknown",obj_class_num),
761                        obj_class_num,
762                        obj_ctype,
763                        obj_tlen);
764 
765                 hlen-=sizeof(struct icmp_multipart_ext_object_header_t); /* length field includes tlv header */
766 
767                 /* infinite loop protection */
768                 if ((obj_class_num == 0) ||
769                     (obj_tlen < sizeof(struct icmp_multipart_ext_object_header_t))) {
770                     return;
771                 }
772                 obj_tlen-=sizeof(struct icmp_multipart_ext_object_header_t);
773 
774                 switch (obj_class_num) {
775                 case MPLS_STACK_ENTRY_OBJECT_CLASS:
776                     switch(obj_ctype) {
777                     case 1:
778                         raw_label = GET_BE_U_4(obj_tptr);
779                         ND_PRINT("\n\t    label %u, tc %u", MPLS_LABEL(raw_label), MPLS_TC(raw_label));
780                         if (MPLS_STACK(raw_label))
781                             ND_PRINT(", [S]");
782                         ND_PRINT(", ttl %u", MPLS_TTL(raw_label));
783                         break;
784                     default:
785                         print_unknown_data(ndo, obj_tptr, "\n\t    ", obj_tlen);
786                     }
787                     break;
788 
789                 default:
790                     print_unknown_data(ndo, obj_tptr, "\n\t    ", obj_tlen);
791                     break;
792                 }
793                 if (hlen < obj_tlen)
794                     break;
795                 hlen -= obj_tlen;
796                 obj_tptr += obj_tlen;
797             }
798         }
799 
800 	return;
801 trunc:
802 	nd_print_trunc(ndo);
803 }
804