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