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