1 /* 2 * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997 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 * Code by Gert Doering, SpaceNet GmbH, gert@space.net 22 * 23 * Reference documentation: 24 * https://web.archive.org/web/20000914194913/http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.pdf 25 */ 26 27 /* \summary: Cisco Discovery Protocol (CDP) printer */ 28 29 #ifdef HAVE_CONFIG_H 30 #include <config.h> 31 #endif 32 33 #include "netdissect-stdinc.h" 34 35 #include <string.h> 36 37 #define ND_LONGJMP_FROM_TCHECK 38 #include "netdissect.h" 39 #include "addrtoname.h" 40 #include "extract.h" 41 #include "nlpid.h" 42 43 44 #define CDP_HEADER_LEN 4 45 #define CDP_HEADER_VERSION_OFFSET 0 46 #define CDP_HEADER_TTL_OFFSET 1 47 #define CDP_HEADER_CHECKSUM_OFFSET 2 48 49 #define CDP_TLV_HEADER_LEN 4 50 #define CDP_TLV_TYPE_OFFSET 0 51 #define CDP_TLV_LEN_OFFSET 2 52 53 static const struct tok cdp_capability_values[] = { 54 { 0x01, "Router" }, 55 { 0x02, "Transparent Bridge" }, 56 { 0x04, "Source Route Bridge" }, 57 { 0x08, "L2 Switch" }, 58 { 0x10, "L3 capable" }, 59 { 0x20, "IGMP snooping" }, 60 { 0x40, "L1 capable" }, 61 { 0, NULL } 62 }; 63 64 static void cdp_print_addr(netdissect_options *, const u_char *, u_int); 65 static void cdp_print_prefixes(netdissect_options *, const u_char *, u_int); 66 67 static void 68 cdp_print_string(netdissect_options *ndo, 69 const u_char *cp, const u_int len) 70 { 71 ND_PRINT("'"); 72 (void)nd_printn(ndo, cp, len, NULL); 73 ND_PRINT("'"); 74 } 75 76 static void 77 cdp_print_power(netdissect_options *ndo, 78 const u_char *cp, const u_int len) 79 { 80 u_int val = 0; 81 82 switch (len) { 83 case 1: 84 val = GET_U_1(cp); 85 break; 86 case 2: 87 val = GET_BE_U_2(cp); 88 break; 89 case 3: 90 val = GET_BE_U_3(cp); 91 break; 92 } 93 ND_PRINT("%1.2fW", val / 1000.0); 94 } 95 96 static void 97 cdp_print_capability(netdissect_options *ndo, 98 const u_char *cp, const u_int len _U_) 99 { 100 uint32_t val = GET_BE_U_4(cp); 101 102 ND_PRINT("(0x%08x): %s", val, 103 bittok2str(cdp_capability_values, "none", val)); 104 } 105 106 /* Rework the version string to get a nice indentation. */ 107 static void 108 cdp_print_version(netdissect_options *ndo, 109 const u_char *cp, const u_int len) 110 { 111 unsigned i; 112 113 ND_PRINT("\n\t "); 114 for (i = 0; i < len; i++) { 115 u_char c = GET_U_1(cp + i); 116 117 if (c == '\n') 118 ND_PRINT("\n\t "); 119 else 120 fn_print_char(ndo, c); 121 } 122 } 123 124 static void 125 cdp_print_uint16(netdissect_options *ndo, 126 const u_char *cp, const u_int len _U_) 127 { 128 ND_PRINT("%u", GET_BE_U_2(cp)); 129 } 130 131 static void 132 cdp_print_duplex(netdissect_options *ndo, 133 const u_char *cp, const u_int len _U_) 134 { 135 ND_PRINT("%s", GET_U_1(cp) ? "full": "half"); 136 } 137 138 /* https://www.cisco.com/c/en/us/td/docs/voice_ip_comm/cata/186/2_12_m/english/release/notes/186rn21m.html 139 * plus more details from other sources 140 * 141 * There are apparently versions of the request with both 142 * 2 bytes and 3 bytes of value. The 3 bytes of value 143 * appear to be a 1-byte application type followed by a 144 * 2-byte VLAN ID; the 2 bytes of value are unknown 145 * (they're 0x20 0x00 in some captures I've seen; that 146 * is not a valid VLAN ID, as VLAN IDs are 12 bits). 147 * 148 * The replies all appear to be 3 bytes long. 149 */ 150 static void 151 cdp_print_ata186(netdissect_options *ndo, 152 const u_char *cp, const u_int len) 153 { 154 if (len == 2) 155 ND_PRINT("unknown 0x%04x", GET_BE_U_2(cp)); 156 else 157 ND_PRINT("app %u, vlan %u", GET_U_1(cp), GET_BE_U_2(cp + 1)); 158 } 159 160 static void 161 cdp_print_mtu(netdissect_options *ndo, 162 const u_char *cp, const u_int len _U_) 163 { 164 ND_PRINT("%u bytes", GET_BE_U_4(cp)); 165 } 166 167 static void 168 cdp_print_uint8x(netdissect_options *ndo, 169 const u_char *cp, const u_int len _U_) 170 { 171 ND_PRINT("0x%02x", GET_U_1(cp)); 172 } 173 174 static void 175 cdp_print_phys_loc(netdissect_options *ndo, 176 const u_char *cp, const u_int len) 177 { 178 ND_PRINT("0x%02x", GET_U_1(cp)); 179 if (len > 1) { 180 ND_PRINT("/"); 181 (void)nd_printn(ndo, cp + 1, len - 1, NULL); 182 } 183 } 184 185 struct cdp_tlvinfo { 186 const char *name; 187 void (*printer)(netdissect_options *ndo, const u_char *, u_int); 188 int min_len, max_len; 189 }; 190 191 #define T_DEV_ID 0x01 192 #define T_MAX 0x17 193 static const struct cdp_tlvinfo cdptlvs[T_MAX + 1] = { 194 /* 0x00 */ 195 [ T_DEV_ID ] = { "Device-ID", cdp_print_string, -1, -1 }, 196 [ 0x02 ] = { "Address", cdp_print_addr, -1, -1 }, 197 [ 0x03 ] = { "Port-ID", cdp_print_string, -1, -1 }, 198 [ 0x04 ] = { "Capability", cdp_print_capability, 4, 4 }, 199 [ 0x05 ] = { "Version String", cdp_print_version, -1, -1 }, 200 [ 0x06 ] = { "Platform", cdp_print_string, -1, -1 }, 201 [ 0x07 ] = { "Prefixes", cdp_print_prefixes, -1, -1 }, 202 /* not documented */ 203 [ 0x08 ] = { "Protocol-Hello option", NULL, -1, -1 }, 204 /* CDPv2 */ 205 [ 0x09 ] = { "VTP Management Domain", cdp_print_string, -1, -1 }, 206 /* CDPv2 */ 207 [ 0x0a ] = { "Native VLAN ID", cdp_print_uint16, 2, 2 }, 208 /* CDPv2 */ 209 [ 0x0b ] = { "Duplex", cdp_print_duplex, 1, 1 }, 210 /* 0x0c */ 211 /* 0x0d */ 212 /* incomplete doc. */ 213 [ 0x0e ] = { "ATA-186 VoIP VLAN assignment", cdp_print_ata186, 3, 3 }, 214 /* incomplete doc. */ 215 [ 0x0f ] = { "ATA-186 VoIP VLAN request", cdp_print_ata186, 2, 3 }, 216 /* not documented */ 217 [ 0x10 ] = { "power consumption", cdp_print_power, 1, 3 }, 218 /* not documented */ 219 [ 0x11 ] = { "MTU", cdp_print_mtu, 4, 4 }, 220 /* not documented */ 221 [ 0x12 ] = { "AVVID trust bitmap", cdp_print_uint8x, 1, 1 }, 222 /* not documented */ 223 [ 0x13 ] = { "AVVID untrusted ports CoS", cdp_print_uint8x, 1, 1 }, 224 /* not documented */ 225 [ 0x14 ] = { "System Name", cdp_print_string, -1, -1 }, 226 /* not documented */ 227 [ 0x15 ] = { "System Object ID (not decoded)", NULL, -1, -1 }, 228 [ 0x16 ] = { "Management Addresses", cdp_print_addr, 4, -1 }, 229 /* not documented */ 230 [ 0x17 ] = { "Physical Location", cdp_print_phys_loc, 1, -1 }, 231 }; 232 233 void 234 cdp_print(netdissect_options *ndo, 235 const u_char *tptr, u_int length) 236 { 237 u_int orig_length = length; 238 uint16_t checksum; 239 240 ndo->ndo_protocol = "cdp"; 241 242 if (length < CDP_HEADER_LEN) { 243 ND_PRINT(" (packet length %u < %u)", length, CDP_HEADER_LEN); 244 goto invalid; 245 } 246 ND_PRINT("CDPv%u, ttl: %us", 247 GET_U_1(tptr + CDP_HEADER_VERSION_OFFSET), 248 GET_U_1(tptr + CDP_HEADER_TTL_OFFSET)); 249 checksum = GET_BE_U_2(tptr + CDP_HEADER_CHECKSUM_OFFSET); 250 if (ndo->ndo_vflag) 251 ND_PRINT(", checksum: 0x%04x (unverified), length %u", 252 checksum, orig_length); 253 tptr += CDP_HEADER_LEN; 254 length -= CDP_HEADER_LEN; 255 256 while (length) { 257 u_int type, len; 258 const struct cdp_tlvinfo *info; 259 const char *name; 260 u_char covered = 0; 261 262 if (length < CDP_TLV_HEADER_LEN) { 263 ND_PRINT(" (remaining packet length %u < %u)", 264 length, CDP_TLV_HEADER_LEN); 265 goto invalid; 266 } 267 type = GET_BE_U_2(tptr + CDP_TLV_TYPE_OFFSET); 268 len = GET_BE_U_2(tptr + CDP_TLV_LEN_OFFSET); /* object length includes the 4 bytes header length */ 269 info = type <= T_MAX ? &cdptlvs[type] : NULL; 270 name = (info && info->name) ? info->name : "unknown field type"; 271 if (len < CDP_TLV_HEADER_LEN) { 272 if (ndo->ndo_vflag) 273 ND_PRINT("\n\t%s (0x%02x), TLV length: %u byte%s (too short)", 274 name, type, len, PLURAL_SUFFIX(len)); 275 else 276 ND_PRINT(", %s TLV length %u too short", 277 name, len); 278 goto invalid; 279 } 280 if (len > length) { 281 ND_PRINT(" (TLV length %u > %u)", len, length); 282 goto invalid; 283 } 284 tptr += CDP_TLV_HEADER_LEN; 285 length -= CDP_TLV_HEADER_LEN; 286 len -= CDP_TLV_HEADER_LEN; 287 288 /* In non-verbose mode just print Device-ID. */ 289 if (!ndo->ndo_vflag && type == T_DEV_ID) 290 ND_PRINT(", Device-ID "); 291 else if (ndo->ndo_vflag) 292 ND_PRINT("\n\t%s (0x%02x), value length: %u byte%s: ", 293 name, type, len, PLURAL_SUFFIX(len)); 294 295 if (info) { 296 if ((info->min_len > 0 && len < (unsigned)info->min_len) || 297 (info->max_len > 0 && len > (unsigned)info->max_len)) 298 ND_PRINT(" (malformed TLV)"); 299 else if (ndo->ndo_vflag || type == T_DEV_ID) { 300 if (info->printer) 301 info->printer(ndo, tptr, len); 302 else 303 ND_TCHECK_LEN(tptr, len); 304 /* 305 * When the type is defined without a printer, 306 * do not print the hex dump. 307 */ 308 covered = 1; 309 } 310 } 311 312 if (!covered) { 313 ND_TCHECK_LEN(tptr, len); 314 print_unknown_data(ndo, tptr, "\n\t ", len); 315 } 316 tptr += len; 317 length -= len; 318 } 319 if (ndo->ndo_vflag < 1) 320 ND_PRINT(", length %u", orig_length); 321 322 return; 323 invalid: 324 nd_print_invalid(ndo); 325 ND_TCHECK_LEN(tptr, length); 326 } 327 328 /* 329 * Protocol type values. 330 * 331 * PT_NLPID means that the protocol type field contains an OSI NLPID. 332 * 333 * PT_IEEE_802_2 means that the protocol type field contains an IEEE 802.2 334 * LLC header that specifies that the payload is for that protocol. 335 */ 336 #define PT_NLPID 1 /* OSI NLPID */ 337 #define PT_IEEE_802_2 2 /* IEEE 802.2 LLC header */ 338 339 static void 340 cdp_print_addr(netdissect_options *ndo, 341 const u_char * p, u_int l) 342 { 343 u_int num; 344 static const u_char prot_ipv6[] = { 345 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x86, 0xdd 346 }; 347 348 if (l < 4) { 349 ND_PRINT(" (not enough space for num)"); 350 goto invalid; 351 } 352 num = GET_BE_U_4(p); 353 p += 4; 354 l -= 4; 355 356 while (num) { 357 u_int pt, pl, al; 358 359 if (l < 2) { 360 ND_PRINT(" (not enough space for PT+PL)"); 361 goto invalid; 362 } 363 pt = GET_U_1(p); /* type of "protocol" field */ 364 pl = GET_U_1(p + 1); /* length of "protocol" field */ 365 p += 2; 366 l -= 2; 367 368 if (l < pl + 2) { 369 ND_PRINT(" (not enough space for P+AL)"); 370 goto invalid; 371 } 372 /* Skip the protocol for now. */ 373 al = GET_BE_U_2(p + pl); /* address length */ 374 375 if (pt == PT_NLPID && pl == 1 && GET_U_1(p) == NLPID_IP && 376 al == 4) { 377 /* 378 * IPv4: protocol type = NLPID, protocol length = 1 379 * (1-byte NLPID), protocol = 0xcc (NLPID for IPv4), 380 * address length = 4 381 */ 382 p += pl + 2; 383 l -= pl + 2; 384 /* p is just beyond al now. */ 385 if (l < al) { 386 ND_PRINT(" (not enough space for A)"); 387 goto invalid; 388 } 389 ND_PRINT("IPv4 (%u) %s", num, GET_IPADDR_STRING(p)); 390 p += al; 391 l -= al; 392 } 393 else if (pt == PT_IEEE_802_2 && pl == 8 && 394 memcmp(p, prot_ipv6, 8) == 0 && al == 16) { 395 /* 396 * IPv6: protocol type = IEEE 802.2 header, 397 * protocol length = 8 (size of LLC+SNAP header), 398 * protocol = LLC+SNAP header with the IPv6 399 * Ethertype, address length = 16 400 */ 401 p += pl + 2; 402 l -= pl + 2; 403 /* p is just beyond al now. */ 404 if (l < al) { 405 ND_PRINT(" (not enough space for A)"); 406 goto invalid; 407 } 408 ND_PRINT("IPv6 (%u) %s", num, GET_IP6ADDR_STRING(p)); 409 p += al; 410 l -= al; 411 } 412 else { 413 /* 414 * Generic case: just print raw data 415 */ 416 ND_PRINT("pt=0x%02x, pl=%u, pb=", pt, pl); 417 while (pl != 0) { 418 ND_PRINT(" %02x", GET_U_1(p)); 419 p++; 420 l--; 421 pl--; 422 } 423 ND_PRINT(", al=%u, a=", al); 424 p += 2; 425 l -= 2; 426 /* p is just beyond al now. */ 427 if (l < al) { 428 ND_PRINT(" (not enough space for A)"); 429 goto invalid; 430 } 431 while (al != 0) { 432 ND_PRINT(" %02x", GET_U_1(p)); 433 p++; 434 l--; 435 al--; 436 } 437 } 438 num--; 439 if (num) 440 ND_PRINT(" "); 441 } 442 if (l) 443 ND_PRINT(" (%u bytes of stray data)", l); 444 return; 445 446 invalid: 447 ND_TCHECK_LEN(p, l); 448 } 449 450 static void 451 cdp_print_prefixes(netdissect_options *ndo, 452 const u_char * p, u_int l) 453 { 454 if (l % 5) { 455 ND_PRINT(" [length %u is not a multiple of 5]", l); 456 goto invalid; 457 } 458 459 ND_PRINT(" IPv4 Prefixes (%u):", l / 5); 460 461 while (l > 0) { 462 ND_PRINT(" %u.%u.%u.%u/%u", 463 GET_U_1(p), GET_U_1(p + 1), GET_U_1(p + 2), 464 GET_U_1(p + 3), GET_U_1(p + 4)); 465 l -= 5; 466 p += 5; 467 } 468 return; 469 470 invalid: 471 ND_TCHECK_LEN(p, l); 472 } 473