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