1/*
2 * Copyright (C) Arnaldo Carvalho de Melo 2004
3 * Copyright (C) Ian McDonald 2005
4 * Copyright (C) Yoshifumi Nishida 2005
5 *
6 * This software may be distributed either under the terms of the
7 * BSD-style license that accompanies tcpdump or the GNU GPL version 2
8 */
9
10/* \summary: Datagram Congestion Control Protocol (DCCP) printer */
11
12/* specification: RFC 4340 */
13
14#ifdef HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#include "netdissect-stdinc.h"
19
20#include "netdissect.h"
21#include "addrtoname.h"
22#include "extract.h"
23#include "ip.h"
24#include "ip6.h"
25#include "ipproto.h"
26
27/* RFC4340: Datagram Congestion Control Protocol (DCCP) */
28
29/**
30 * struct dccp_hdr - generic part of DCCP packet header, with a 24-bit
31 * sequence number
32 *
33 * @dccph_sport - Relevant port on the endpoint that sent this packet
34 * @dccph_dport - Relevant port on the other endpoint
35 * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words
36 * @dccph_ccval - Used by the HC-Sender CCID
37 * @dccph_cscov - Parts of the packet that are covered by the Checksum field
38 * @dccph_checksum - Internet checksum, depends on dccph_cscov
39 * @dccph_x - 0 = 24 bit sequence number, 1 = 48
40 * @dccph_type - packet type, see DCCP_PKT_ prefixed macros
41 * @dccph_seq - 24-bit sequence number
42 */
43struct dccp_hdr {
44	nd_uint16_t	dccph_sport,
45			dccph_dport;
46	nd_uint8_t	dccph_doff;
47	nd_uint8_t	dccph_ccval_cscov;
48	nd_uint16_t	dccph_checksum;
49	nd_uint8_t	dccph_xtr;
50	nd_uint24_t	dccph_seq;
51};
52
53/**
54 * struct dccp_hdr_ext - generic part of DCCP packet header, with a 48-bit
55 * sequence number
56 *
57 * @dccph_sport - Relevant port on the endpoint that sent this packet
58 * @dccph_dport - Relevant port on the other endpoint
59 * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words
60 * @dccph_ccval - Used by the HC-Sender CCID
61 * @dccph_cscov - Parts of the packet that are covered by the Checksum field
62 * @dccph_checksum - Internet checksum, depends on dccph_cscov
63 * @dccph_x - 0 = 24 bit sequence number, 1 = 48
64 * @dccph_type - packet type, see DCCP_PKT_ prefixed macros
65 * @dccph_seq - 48-bit sequence number
66 */
67struct dccp_hdr_ext {
68	nd_uint16_t	dccph_sport,
69			dccph_dport;
70	nd_uint8_t	dccph_doff;
71	nd_uint8_t	dccph_ccval_cscov;
72	nd_uint16_t	dccph_checksum;
73	nd_uint8_t	dccph_xtr;
74	nd_uint8_t	reserved;
75	nd_uint48_t	dccph_seq;
76};
77
78#define DCCPH_CCVAL(dh)	((GET_U_1((dh)->dccph_ccval_cscov) >> 4) & 0xF)
79#define DCCPH_CSCOV(dh)	(GET_U_1((dh)->dccph_ccval_cscov) & 0xF)
80
81#define DCCPH_X(dh)	(GET_U_1((dh)->dccph_xtr) & 1)
82#define DCCPH_TYPE(dh)	((GET_U_1((dh)->dccph_xtr) >> 1) & 0xF)
83
84/**
85 * struct dccp_hdr_request - Connection initiation request header
86 *
87 * @dccph_req_service - Service to which the client app wants to connect
88 */
89struct dccp_hdr_request {
90	nd_uint32_t	dccph_req_service;
91};
92
93/**
94 * struct dccp_hdr_response - Connection initiation response header
95 *
96 * @dccph_resp_ack - 48 bit ack number, contains GSR
97 * @dccph_resp_service - Echoes the Service Code on a received DCCP-Request
98 */
99struct dccp_hdr_response {
100	nd_uint64_t	dccph_resp_ack;	/* always 8 bytes, first 2 reserved */
101	nd_uint32_t	dccph_resp_service;
102};
103
104/**
105 * struct dccp_hdr_reset - Unconditionally shut down a connection
106 *
107 * @dccph_resp_ack - 48 bit ack number
108 * @dccph_reset_service - Echoes the Service Code on a received DCCP-Request
109 */
110struct dccp_hdr_reset {
111	nd_uint64_t	dccph_reset_ack;	/* always 8 bytes, first 2 reserved */
112	nd_uint8_t	dccph_reset_code;
113	nd_uint8_t	dccph_reset_data1;
114	nd_uint8_t	dccph_reset_data2;
115	nd_uint8_t	dccph_reset_data3;
116};
117
118enum dccp_pkt_type {
119	DCCP_PKT_REQUEST = 0,
120	DCCP_PKT_RESPONSE,
121	DCCP_PKT_DATA,
122	DCCP_PKT_ACK,
123	DCCP_PKT_DATAACK,
124	DCCP_PKT_CLOSEREQ,
125	DCCP_PKT_CLOSE,
126	DCCP_PKT_RESET,
127	DCCP_PKT_SYNC,
128	DCCP_PKT_SYNCACK
129};
130
131static const struct tok dccp_pkt_type_str[] = {
132	{ DCCP_PKT_REQUEST, "DCCP-Request" },
133	{ DCCP_PKT_RESPONSE, "DCCP-Response" },
134	{ DCCP_PKT_DATA, "DCCP-Data" },
135	{ DCCP_PKT_ACK, "DCCP-Ack" },
136	{ DCCP_PKT_DATAACK, "DCCP-DataAck" },
137	{ DCCP_PKT_CLOSEREQ, "DCCP-CloseReq" },
138	{ DCCP_PKT_CLOSE, "DCCP-Close" },
139	{ DCCP_PKT_RESET, "DCCP-Reset" },
140	{ DCCP_PKT_SYNC, "DCCP-Sync" },
141	{ DCCP_PKT_SYNCACK, "DCCP-SyncAck" },
142	{ 0, NULL}
143};
144
145enum dccp_reset_codes {
146	DCCP_RESET_CODE_UNSPECIFIED = 0,
147	DCCP_RESET_CODE_CLOSED,
148	DCCP_RESET_CODE_ABORTED,
149	DCCP_RESET_CODE_NO_CONNECTION,
150	DCCP_RESET_CODE_PACKET_ERROR,
151	DCCP_RESET_CODE_OPTION_ERROR,
152	DCCP_RESET_CODE_MANDATORY_ERROR,
153	DCCP_RESET_CODE_CONNECTION_REFUSED,
154	DCCP_RESET_CODE_BAD_SERVICE_CODE,
155	DCCP_RESET_CODE_TOO_BUSY,
156	DCCP_RESET_CODE_BAD_INIT_COOKIE,
157	DCCP_RESET_CODE_AGGRESSION_PENALTY,
158	__DCCP_RESET_CODE_LAST
159};
160
161
162static const char *dccp_reset_codes[] = {
163	"unspecified",
164	"closed",
165	"aborted",
166	"no_connection",
167	"packet_error",
168	"option_error",
169	"mandatory_error",
170	"connection_refused",
171	"bad_service_code",
172	"too_busy",
173	"bad_init_cookie",
174	"aggression_penalty",
175};
176
177static const char *dccp_feature_nums[] = {
178	"reserved",
179	"ccid",
180	"allow_short_seqno",
181	"sequence_window",
182	"ecn_incapable",
183	"ack_ratio",
184	"send_ack_vector",
185	"send_ndp_count",
186	"minimum checksum coverage",
187	"check data checksum",
188};
189
190static u_int
191dccp_csum_coverage(netdissect_options *ndo,
192		   const struct dccp_hdr *dh, u_int len)
193{
194	u_int cov;
195
196	if (DCCPH_CSCOV(dh) == 0)
197		return len;
198	cov = (GET_U_1(dh->dccph_doff) + DCCPH_CSCOV(dh) - 1) * sizeof(uint32_t);
199	return (cov > len)? len : cov;
200}
201
202static uint16_t dccp_cksum(netdissect_options *ndo, const struct ip *ip,
203	const struct dccp_hdr *dh, u_int len)
204{
205	return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)dh, len,
206				dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP);
207}
208
209static uint16_t dccp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
210	const struct dccp_hdr *dh, u_int len)
211{
212	return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)dh, len,
213				dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP);
214}
215
216static const char *dccp_reset_code(uint8_t code)
217{
218	if (code >= __DCCP_RESET_CODE_LAST)
219		return "invalid";
220	return dccp_reset_codes[code];
221}
222
223static uint64_t
224dccp_seqno(netdissect_options *ndo, const u_char *bp)
225{
226	const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
227	uint64_t seqno;
228
229	if (DCCPH_X(dh) != 0) {
230		const struct dccp_hdr_ext *dhx = (const struct dccp_hdr_ext *)bp;
231		seqno = GET_BE_U_6(dhx->dccph_seq);
232	} else {
233		seqno = GET_BE_U_3(dh->dccph_seq);
234	}
235
236	return seqno;
237}
238
239static unsigned int
240dccp_basic_hdr_len(netdissect_options *ndo, const struct dccp_hdr *dh)
241{
242	return DCCPH_X(dh) ? sizeof(struct dccp_hdr_ext) : sizeof(struct dccp_hdr);
243}
244
245static void dccp_print_ack_no(netdissect_options *ndo, const u_char *bp)
246{
247	const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
248	const u_char *ackp = bp + dccp_basic_hdr_len(ndo, dh);
249	uint64_t ackno;
250
251	if (DCCPH_X(dh) != 0) {
252		ackno = GET_BE_U_6(ackp + 2);
253	} else {
254		ackno = GET_BE_U_3(ackp + 1);
255	}
256
257	ND_PRINT("(ack=%" PRIu64 ") ", ackno);
258}
259
260static u_int dccp_print_option(netdissect_options *, const u_char *, u_int);
261
262/**
263 * dccp_print - show dccp packet
264 * @bp - beginning of dccp packet
265 * @data2 - beginning of enclosing
266 * @len - length of ip packet
267 */
268void
269dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2,
270	   u_int len)
271{
272	const struct dccp_hdr *dh;
273	const struct ip *ip;
274	const struct ip6_hdr *ip6;
275	const u_char *cp;
276	u_short sport, dport;
277	u_int hlen;
278	u_int fixed_hdrlen;
279	uint8_t	dccph_type;
280
281	ndo->ndo_protocol = "dccp";
282	dh = (const struct dccp_hdr *)bp;
283
284	ip = (const struct ip *)data2;
285	if (IP_V(ip) == 6)
286		ip6 = (const struct ip6_hdr *)data2;
287	else
288		ip6 = NULL;
289
290	/* make sure we have enough data to look at the X bit */
291	cp = (const u_char *)(dh + 1);
292	if (cp > ndo->ndo_snapend)
293		goto trunc;
294	if (len < sizeof(struct dccp_hdr)) {
295		ND_PRINT("truncated-dccp - %zu bytes missing!",
296			 sizeof(struct dccp_hdr) - len);
297		return;
298	}
299
300	/* get the length of the generic header */
301	fixed_hdrlen = dccp_basic_hdr_len(ndo, dh);
302	if (len < fixed_hdrlen) {
303		ND_PRINT("truncated-dccp - %u bytes missing!",
304			  fixed_hdrlen - len);
305		return;
306	}
307	ND_TCHECK_LEN(dh, fixed_hdrlen);
308
309	sport = GET_BE_U_2(dh->dccph_sport);
310	dport = GET_BE_U_2(dh->dccph_dport);
311	hlen = GET_U_1(dh->dccph_doff) * 4;
312
313	if (ip6) {
314		ND_PRINT("%s.%u > %s.%u: ",
315			  GET_IP6ADDR_STRING(ip6->ip6_src), sport,
316			  GET_IP6ADDR_STRING(ip6->ip6_dst), dport);
317	} else {
318		ND_PRINT("%s.%u > %s.%u: ",
319			  GET_IPADDR_STRING(ip->ip_src), sport,
320			  GET_IPADDR_STRING(ip->ip_dst), dport);
321	}
322
323	nd_print_protocol_caps(ndo);
324
325	if (ndo->ndo_qflag) {
326		ND_PRINT(" %u", len - hlen);
327		if (hlen > len) {
328			ND_PRINT(" [bad hdr length %u - too long, > %u]",
329				  hlen, len);
330		}
331		return;
332	}
333
334	/* other variables in generic header */
335	if (ndo->ndo_vflag) {
336		ND_PRINT(" (CCVal %u, CsCov %u", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh));
337	}
338
339	/* checksum calculation */
340	if (ndo->ndo_vflag && ND_TTEST_LEN(bp, len)) {
341		uint16_t sum = 0, dccp_sum;
342
343		dccp_sum = GET_BE_U_2(dh->dccph_checksum);
344		ND_PRINT(", cksum 0x%04x ", dccp_sum);
345		if (IP_V(ip) == 4)
346			sum = dccp_cksum(ndo, ip, dh, len);
347		else if (IP_V(ip) == 6)
348			sum = dccp6_cksum(ndo, ip6, dh, len);
349		if (sum != 0)
350			ND_PRINT("(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum));
351		else
352			ND_PRINT("(correct)");
353	}
354
355	if (ndo->ndo_vflag)
356		ND_PRINT(")");
357	ND_PRINT(" ");
358
359	dccph_type = DCCPH_TYPE(dh);
360	switch (dccph_type) {
361	case DCCP_PKT_REQUEST: {
362		const struct dccp_hdr_request *dhr =
363			(const struct dccp_hdr_request *)(bp + fixed_hdrlen);
364		fixed_hdrlen += 4;
365		if (len < fixed_hdrlen) {
366			ND_PRINT("truncated-%s - %u bytes missing!",
367				  tok2str(dccp_pkt_type_str, "", dccph_type),
368				  fixed_hdrlen - len);
369			return;
370		}
371		ND_TCHECK_SIZE(dhr);
372		ND_PRINT("%s (service=%u) ",
373			  tok2str(dccp_pkt_type_str, "", dccph_type),
374			  GET_BE_U_4(dhr->dccph_req_service));
375		break;
376	}
377	case DCCP_PKT_RESPONSE: {
378		const struct dccp_hdr_response *dhr =
379			(const struct dccp_hdr_response *)(bp + fixed_hdrlen);
380		fixed_hdrlen += 12;
381		if (len < fixed_hdrlen) {
382			ND_PRINT("truncated-%s - %u bytes missing!",
383				  tok2str(dccp_pkt_type_str, "", dccph_type),
384				  fixed_hdrlen - len);
385			return;
386		}
387		ND_TCHECK_SIZE(dhr);
388		ND_PRINT("%s (service=%u) ",
389			  tok2str(dccp_pkt_type_str, "", dccph_type),
390			  GET_BE_U_4(dhr->dccph_resp_service));
391		break;
392	}
393	case DCCP_PKT_DATA:
394		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
395		break;
396	case DCCP_PKT_ACK: {
397		fixed_hdrlen += 8;
398		if (len < fixed_hdrlen) {
399			ND_PRINT("truncated-%s - %u bytes missing!",
400				  tok2str(dccp_pkt_type_str, "", dccph_type),
401				  fixed_hdrlen - len);
402			return;
403		}
404		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
405		break;
406	}
407	case DCCP_PKT_DATAACK: {
408		fixed_hdrlen += 8;
409		if (len < fixed_hdrlen) {
410			ND_PRINT("truncated-%s - %u bytes missing!",
411				  tok2str(dccp_pkt_type_str, "", dccph_type),
412				  fixed_hdrlen - len);
413			return;
414		}
415		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
416		break;
417	}
418	case DCCP_PKT_CLOSEREQ:
419		fixed_hdrlen += 8;
420		if (len < fixed_hdrlen) {
421			ND_PRINT("truncated-%s - %u bytes missing!",
422				  tok2str(dccp_pkt_type_str, "", dccph_type),
423				  fixed_hdrlen - len);
424			return;
425		}
426		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
427		break;
428	case DCCP_PKT_CLOSE:
429		fixed_hdrlen += 8;
430		if (len < fixed_hdrlen) {
431			ND_PRINT("truncated-%s - %u bytes missing!",
432				  tok2str(dccp_pkt_type_str, "", dccph_type),
433				  fixed_hdrlen - len);
434			return;
435		}
436		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
437		break;
438	case DCCP_PKT_RESET: {
439		const struct dccp_hdr_reset *dhr =
440			(const struct dccp_hdr_reset *)(bp + fixed_hdrlen);
441		fixed_hdrlen += 12;
442		if (len < fixed_hdrlen) {
443			ND_PRINT("truncated-%s - %u bytes missing!",
444				  tok2str(dccp_pkt_type_str, "", dccph_type),
445				  fixed_hdrlen - len);
446			return;
447		}
448		ND_TCHECK_SIZE(dhr);
449		ND_PRINT("%s (code=%s) ",
450			  tok2str(dccp_pkt_type_str, "", dccph_type),
451			  dccp_reset_code(GET_U_1(dhr->dccph_reset_code)));
452		break;
453	}
454	case DCCP_PKT_SYNC:
455		fixed_hdrlen += 8;
456		if (len < fixed_hdrlen) {
457			ND_PRINT("truncated-%s - %u bytes missing!",
458				  tok2str(dccp_pkt_type_str, "", dccph_type),
459				  fixed_hdrlen - len);
460			return;
461		}
462		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
463		break;
464	case DCCP_PKT_SYNCACK:
465		fixed_hdrlen += 8;
466		if (len < fixed_hdrlen) {
467			ND_PRINT("truncated-%s - %u bytes missing!",
468				  tok2str(dccp_pkt_type_str, "", dccph_type),
469				  fixed_hdrlen - len);
470			return;
471		}
472		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
473		break;
474	default:
475		ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "unknown-type-%u", dccph_type));
476		break;
477	}
478
479	if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) &&
480			(DCCPH_TYPE(dh) != DCCP_PKT_REQUEST))
481		dccp_print_ack_no(ndo, bp);
482
483	if (ndo->ndo_vflag < 2)
484		return;
485
486	ND_PRINT("seq %" PRIu64, dccp_seqno(ndo, bp));
487
488	/* process options */
489	if (hlen > fixed_hdrlen){
490		u_int optlen;
491		cp = bp + fixed_hdrlen;
492		ND_PRINT(" <");
493
494		hlen -= fixed_hdrlen;
495		while(1){
496			optlen = dccp_print_option(ndo, cp, hlen);
497			if (!optlen)
498				break;
499			if (hlen <= optlen)
500				break;
501			hlen -= optlen;
502			cp += optlen;
503			ND_PRINT(", ");
504		}
505		ND_PRINT(">");
506	}
507	return;
508trunc:
509	nd_print_trunc(ndo);
510}
511
512static const struct tok dccp_option_values[] = {
513	{ 0, "nop" },
514	{ 1, "mandatory" },
515	{ 2, "slowreceiver" },
516	{ 32, "change_l" },
517	{ 33, "confirm_l" },
518	{ 34, "change_r" },
519	{ 35, "confirm_r" },
520	{ 36, "initcookie" },
521	{ 37, "ndp_count" },
522	{ 38, "ack_vector0" },
523	{ 39, "ack_vector1" },
524	{ 40, "data_dropped" },
525	{ 41, "timestamp" },
526	{ 42, "timestamp_echo" },
527	{ 43, "elapsed_time" },
528	{ 44, "data_checksum" },
529	{ 0, NULL }
530};
531
532static u_int
533dccp_print_option(netdissect_options *ndo, const u_char *option, u_int hlen)
534{
535	uint8_t optlen, i;
536
537	if (GET_U_1(option) >= 32) {
538		optlen = GET_U_1(option + 1);
539		if (optlen < 2) {
540			if (GET_U_1(option) >= 128)
541				ND_PRINT("CCID option %u optlen too short",
542					 GET_U_1(option));
543			else
544				ND_PRINT("%s optlen too short",
545					  tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
546			return 0;
547		}
548	} else
549		optlen = 1;
550
551	if (hlen < optlen) {
552		if (GET_U_1(option) >= 128)
553			ND_PRINT("CCID option %u optlen goes past header length",
554				  GET_U_1(option));
555		else
556			ND_PRINT("%s optlen goes past header length",
557				  tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
558		return 0;
559	}
560	ND_TCHECK_LEN(option, optlen);
561
562	if (GET_U_1(option) >= 128) {
563		ND_PRINT("CCID option %u", GET_U_1(option));
564		switch (optlen) {
565			case 4:
566				ND_PRINT(" %u", GET_BE_U_2(option + 2));
567				break;
568			case 6:
569				ND_PRINT(" %u", GET_BE_U_4(option + 2));
570				break;
571			default:
572				break;
573		}
574	} else {
575		ND_PRINT("%s",
576			 tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
577		switch (GET_U_1(option)) {
578		case 32:
579		case 33:
580		case 34:
581		case 35:
582			if (optlen < 3) {
583				ND_PRINT(" optlen too short");
584				return optlen;
585			}
586			if (GET_U_1(option + 2) < 10){
587				ND_PRINT(" %s",
588					 dccp_feature_nums[GET_U_1(option + 2)]);
589				for (i = 0; i < optlen - 3; i++)
590					ND_PRINT(" %u",
591						 GET_U_1(option + 3 + i));
592			}
593			break;
594		case 36:
595			if (optlen > 2) {
596				ND_PRINT(" 0x");
597				for (i = 0; i < optlen - 2; i++)
598					ND_PRINT("%02x",
599						 GET_U_1(option + 2 + i));
600			}
601			break;
602		case 37:
603			for (i = 0; i < optlen - 2; i++)
604				ND_PRINT(" %u", GET_U_1(option + 2 + i));
605			break;
606		case 38:
607			if (optlen > 2) {
608				ND_PRINT(" 0x");
609				for (i = 0; i < optlen - 2; i++)
610					ND_PRINT("%02x",
611						 GET_U_1(option + 2 + i));
612			}
613			break;
614		case 39:
615			if (optlen > 2) {
616				ND_PRINT(" 0x");
617				for (i = 0; i < optlen - 2; i++)
618					ND_PRINT("%02x",
619						 GET_U_1(option + 2 + i));
620			}
621			break;
622		case 40:
623			if (optlen > 2) {
624				ND_PRINT(" 0x");
625				for (i = 0; i < optlen - 2; i++)
626					ND_PRINT("%02x",
627						 GET_U_1(option + 2 + i));
628			}
629			break;
630		case 41:
631		/*
632		 * 13.1.  Timestamp Option
633		 *
634		 *  +--------+--------+--------+--------+--------+--------+
635		 *  |00101001|00000110|          Timestamp Value          |
636		 *  +--------+--------+--------+--------+--------+--------+
637		 *   Type=41  Length=6
638		 */
639			if (optlen == 6)
640				ND_PRINT(" %u", GET_BE_U_4(option + 2));
641			else
642				ND_PRINT(" [optlen != 6]");
643			break;
644		case 42:
645		/*
646		 * 13.3.  Timestamp Echo Option
647		 *
648		 *  +--------+--------+--------+--------+--------+--------+
649		 *  |00101010|00000110|           Timestamp Echo          |
650		 *  +--------+--------+--------+--------+--------+--------+
651		 *   Type=42    Len=6
652		 *
653		 *  +--------+--------+------- ... -------+--------+--------+
654		 *  |00101010|00001000|  Timestamp Echo   |   Elapsed Time  |
655		 *  +--------+--------+------- ... -------+--------+--------+
656		 *   Type=42    Len=8       (4 bytes)
657		 *
658		 *  +--------+--------+------- ... -------+------- ... -------+
659		 *  |00101010|00001010|  Timestamp Echo   |    Elapsed Time   |
660		 *  +--------+--------+------- ... -------+------- ... -------+
661		 *   Type=42   Len=10       (4 bytes)           (4 bytes)
662		 */
663			switch (optlen) {
664			case 6:
665				ND_PRINT(" %u", GET_BE_U_4(option + 2));
666				break;
667			case 8:
668				ND_PRINT(" %u", GET_BE_U_4(option + 2));
669				ND_PRINT(" (elapsed time %u)",
670					 GET_BE_U_2(option + 6));
671				break;
672			case 10:
673				ND_PRINT(" %u", GET_BE_U_4(option + 2));
674				ND_PRINT(" (elapsed time %u)",
675					 GET_BE_U_4(option + 6));
676				break;
677			default:
678				ND_PRINT(" [optlen != 6 or 8 or 10]");
679				break;
680			}
681			break;
682		case 43:
683			if (optlen == 6)
684				ND_PRINT(" %u", GET_BE_U_4(option + 2));
685			else if (optlen == 4)
686				ND_PRINT(" %u", GET_BE_U_2(option + 2));
687			else
688				ND_PRINT(" [optlen != 4 or 6]");
689			break;
690		case 44:
691			if (optlen > 2) {
692				ND_PRINT(" ");
693				for (i = 0; i < optlen - 2; i++)
694					ND_PRINT("%02x",
695						 GET_U_1(option + 2 + i));
696			}
697			break;
698		}
699	}
700
701	return optlen;
702trunc:
703	nd_print_trunc(ndo);
704	return 0;
705}
706