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 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm 25 */ 26 27 #include <sys/cdefs.h> 28 #ifndef lint 29 __RCSID("$NetBSD: print-cdp.c,v 1.8 2017/02/05 04:05:05 spz Exp $"); 30 #endif 31 32 /* \summary: Cisco Discovery Protocol (CDP) printer */ 33 34 #ifdef HAVE_CONFIG_H 35 #include "config.h" 36 #endif 37 38 #include <netdissect-stdinc.h> 39 40 #include <string.h> 41 42 #include "netdissect.h" 43 #include "addrtoname.h" 44 #include "extract.h" 45 #include "nlpid.h" 46 47 static const char tstr[] = "[|cdp]"; 48 49 #define CDP_HEADER_LEN 4 50 #define CDP_HEADER_VERSION_OFFSET 0 51 #define CDP_HEADER_TTL_OFFSET 1 52 #define CDP_HEADER_CHECKSUM_OFFSET 2 53 54 #define CDP_TLV_HEADER_LEN 4 55 #define CDP_TLV_TYPE_OFFSET 0 56 #define CDP_TLV_LEN_OFFSET 2 57 58 static const struct tok cdp_tlv_values[] = { 59 { 0x01, "Device-ID"}, 60 { 0x02, "Address"}, 61 { 0x03, "Port-ID"}, 62 { 0x04, "Capability"}, 63 { 0x05, "Version String"}, 64 { 0x06, "Platform"}, 65 { 0x07, "Prefixes"}, 66 { 0x08, "Protocol-Hello option"}, 67 { 0x09, "VTP Management Domain"}, 68 { 0x0a, "Native VLAN ID"}, 69 { 0x0b, "Duplex"}, 70 { 0x0e, "ATA-186 VoIP VLAN request"}, 71 { 0x0f, "ATA-186 VoIP VLAN assignment"}, 72 { 0x10, "power consumption"}, 73 { 0x11, "MTU"}, 74 { 0x12, "AVVID trust bitmap"}, 75 { 0x13, "AVVID untrusted ports CoS"}, 76 { 0x14, "System Name"}, 77 { 0x15, "System Object ID (not decoded)"}, 78 { 0x16, "Management Addresses"}, 79 { 0x17, "Physical Location"}, 80 { 0, NULL} 81 }; 82 83 static const struct tok cdp_capability_values[] = { 84 { 0x01, "Router" }, 85 { 0x02, "Transparent Bridge" }, 86 { 0x04, "Source Route Bridge" }, 87 { 0x08, "L2 Switch" }, 88 { 0x10, "L3 capable" }, 89 { 0x20, "IGMP snooping" }, 90 { 0x40, "L1 capable" }, 91 { 0, NULL } 92 }; 93 94 static int cdp_print_addr(netdissect_options *, const u_char *, int); 95 static int cdp_print_prefixes(netdissect_options *, const u_char *, int); 96 static unsigned long cdp_get_number(const u_char *, int); 97 98 void 99 cdp_print(netdissect_options *ndo, 100 const u_char *pptr, u_int length, u_int caplen) 101 { 102 int type, len, i, j; 103 const u_char *tptr; 104 105 if (caplen < CDP_HEADER_LEN) { 106 ND_PRINT((ndo, "%s", tstr)); 107 return; 108 } 109 110 tptr = pptr; /* temporary pointer */ 111 112 ND_TCHECK2(*tptr, CDP_HEADER_LEN); 113 ND_PRINT((ndo, "CDPv%u, ttl: %us", *(tptr + CDP_HEADER_VERSION_OFFSET), 114 *(tptr + CDP_HEADER_TTL_OFFSET))); 115 if (ndo->ndo_vflag) 116 ND_PRINT((ndo, ", checksum: 0x%04x (unverified), length %u", EXTRACT_16BITS(tptr+CDP_HEADER_CHECKSUM_OFFSET), length)); 117 tptr += CDP_HEADER_LEN; 118 119 while (tptr < (pptr+length)) { 120 ND_TCHECK2(*tptr, CDP_TLV_HEADER_LEN); /* read out Type and Length */ 121 type = EXTRACT_16BITS(tptr+CDP_TLV_TYPE_OFFSET); 122 len = EXTRACT_16BITS(tptr+CDP_TLV_LEN_OFFSET); /* object length includes the 4 bytes header length */ 123 if (len < CDP_TLV_HEADER_LEN) { 124 if (ndo->ndo_vflag) 125 ND_PRINT((ndo, "\n\t%s (0x%02x), TLV length: %u byte%s (too short)", 126 tok2str(cdp_tlv_values,"unknown field type", type), 127 type, 128 len, 129 PLURAL_SUFFIX(len))); /* plural */ 130 else 131 ND_PRINT((ndo, ", %s TLV length %u too short", 132 tok2str(cdp_tlv_values,"unknown field type", type), 133 len)); 134 break; 135 } 136 tptr += CDP_TLV_HEADER_LEN; 137 len -= CDP_TLV_HEADER_LEN; 138 139 ND_TCHECK2(*tptr, len); 140 141 if (ndo->ndo_vflag || type == 1) { /* in non-verbose mode just print Device-ID */ 142 143 if (ndo->ndo_vflag) 144 ND_PRINT((ndo, "\n\t%s (0x%02x), value length: %u byte%s: ", 145 tok2str(cdp_tlv_values,"unknown field type", type), 146 type, 147 len, 148 PLURAL_SUFFIX(len))); /* plural */ 149 150 switch (type) { 151 152 case 0x01: /* Device-ID */ 153 if (!ndo->ndo_vflag) 154 ND_PRINT((ndo, ", Device-ID ")); 155 ND_PRINT((ndo, "'")); 156 (void)fn_printn(ndo, tptr, len, NULL); 157 ND_PRINT((ndo, "'")); 158 break; 159 case 0x02: /* Address */ 160 if (cdp_print_addr(ndo, tptr, len) < 0) 161 goto trunc; 162 break; 163 case 0x03: /* Port-ID */ 164 ND_PRINT((ndo, "'")); 165 (void)fn_printn(ndo, tptr, len, NULL); 166 ND_PRINT((ndo, "'")); 167 break; 168 case 0x04: /* Capabilities */ 169 if (len < 4) 170 goto trunc; 171 ND_PRINT((ndo, "(0x%08x): %s", 172 EXTRACT_32BITS(tptr), 173 bittok2str(cdp_capability_values, "none", EXTRACT_32BITS(tptr)))); 174 break; 175 case 0x05: /* Version */ 176 ND_PRINT((ndo, "\n\t ")); 177 for (i=0;i<len;i++) { 178 j = *(tptr+i); 179 if (j == '\n') /* lets rework the version string to 180 get a nice indentation */ 181 ND_PRINT((ndo, "\n\t ")); 182 else 183 fn_print_char(ndo, j); 184 } 185 break; 186 case 0x06: /* Platform */ 187 ND_PRINT((ndo, "'")); 188 (void)fn_printn(ndo, tptr, len, NULL); 189 ND_PRINT((ndo, "'")); 190 break; 191 case 0x07: /* Prefixes */ 192 if (cdp_print_prefixes(ndo, tptr, len) < 0) 193 goto trunc; 194 break; 195 case 0x08: /* Protocol Hello Option - not documented */ 196 break; 197 case 0x09: /* VTP Mgmt Domain - CDPv2 */ 198 ND_PRINT((ndo, "'")); 199 (void)fn_printn(ndo, tptr, len, NULL); 200 ND_PRINT((ndo, "'")); 201 break; 202 case 0x0a: /* Native VLAN ID - CDPv2 */ 203 if (len < 2) 204 goto trunc; 205 ND_PRINT((ndo, "%d", EXTRACT_16BITS(tptr))); 206 break; 207 case 0x0b: /* Duplex - CDPv2 */ 208 if (len < 1) 209 goto trunc; 210 ND_PRINT((ndo, "%s", *(tptr) ? "full": "half")); 211 break; 212 213 /* http://www.cisco.com/c/en/us/td/docs/voice_ip_comm/cata/186/2_12_m/english/release/notes/186rn21m.html 214 * plus more details from other sources 215 */ 216 case 0x0e: /* ATA-186 VoIP VLAN request - incomplete doc. */ 217 if (len < 3) 218 goto trunc; 219 ND_PRINT((ndo, "app %d, vlan %d", *(tptr), EXTRACT_16BITS(tptr + 1))); 220 break; 221 case 0x10: /* ATA-186 VoIP VLAN assignment - incomplete doc. */ 222 ND_PRINT((ndo, "%1.2fW", cdp_get_number(tptr, len) / 1000.0)); 223 break; 224 case 0x11: /* MTU - not documented */ 225 if (len < 4) 226 goto trunc; 227 ND_PRINT((ndo, "%u bytes", EXTRACT_32BITS(tptr))); 228 break; 229 case 0x12: /* AVVID trust bitmap - not documented */ 230 if (len < 1) 231 goto trunc; 232 ND_PRINT((ndo, "0x%02x", *(tptr))); 233 break; 234 case 0x13: /* AVVID untrusted port CoS - not documented */ 235 if (len < 1) 236 goto trunc; 237 ND_PRINT((ndo, "0x%02x", *(tptr))); 238 break; 239 case 0x14: /* System Name - not documented */ 240 ND_PRINT((ndo, "'")); 241 (void)fn_printn(ndo, tptr, len, NULL); 242 ND_PRINT((ndo, "'")); 243 break; 244 case 0x16: /* System Object ID - not documented */ 245 if (cdp_print_addr(ndo, tptr, len) < 0) 246 goto trunc; 247 break; 248 case 0x17: /* Physical Location - not documented */ 249 if (len < 1) 250 goto trunc; 251 ND_PRINT((ndo, "0x%02x", *(tptr))); 252 if (len > 1) { 253 ND_PRINT((ndo, "/")); 254 (void)fn_printn(ndo, tptr + 1, len - 1, NULL); 255 } 256 break; 257 default: 258 print_unknown_data(ndo, tptr, "\n\t ", len); 259 break; 260 } 261 } 262 tptr = tptr+len; 263 } 264 if (ndo->ndo_vflag < 1) 265 ND_PRINT((ndo, ", length %u", caplen)); 266 267 return; 268 trunc: 269 ND_PRINT((ndo, "%s", tstr)); 270 } 271 272 /* 273 * Protocol type values. 274 * 275 * PT_NLPID means that the protocol type field contains an OSI NLPID. 276 * 277 * PT_IEEE_802_2 means that the protocol type field contains an IEEE 802.2 278 * LLC header that specifies that the payload is for that protocol. 279 */ 280 #define PT_NLPID 1 /* OSI NLPID */ 281 #define PT_IEEE_802_2 2 /* IEEE 802.2 LLC header */ 282 283 static int 284 cdp_print_addr(netdissect_options *ndo, 285 const u_char * p, int l) 286 { 287 int pt, pl, al, num; 288 const u_char *endp = p + l; 289 static const u_char prot_ipv6[] = { 290 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x86, 0xdd 291 }; 292 293 ND_TCHECK2(*p, 4); 294 if (p + 4 > endp) 295 goto trunc; 296 num = EXTRACT_32BITS(p); 297 p += 4; 298 299 while (p < endp && num >= 0) { 300 ND_TCHECK2(*p, 2); 301 if (p + 2 > endp) 302 goto trunc; 303 pt = p[0]; /* type of "protocol" field */ 304 pl = p[1]; /* length of "protocol" field */ 305 p += 2; 306 307 ND_TCHECK2(p[pl], 2); 308 if (p + pl + 2 > endp) 309 goto trunc; 310 al = EXTRACT_16BITS(&p[pl]); /* address length */ 311 312 if (pt == PT_NLPID && pl == 1 && *p == NLPID_IP && al == 4) { 313 /* 314 * IPv4: protocol type = NLPID, protocol length = 1 315 * (1-byte NLPID), protocol = 0xcc (NLPID for IPv4), 316 * address length = 4 317 */ 318 p += 3; 319 320 ND_TCHECK2(*p, 4); 321 if (p + 4 > endp) 322 goto trunc; 323 ND_PRINT((ndo, "IPv4 (%u) %s", num, ipaddr_string(ndo, p))); 324 p += 4; 325 } 326 else if (pt == PT_IEEE_802_2 && pl == 8 && 327 memcmp(p, prot_ipv6, 8) == 0 && al == 16) { 328 /* 329 * IPv6: protocol type = IEEE 802.2 header, 330 * protocol length = 8 (size of LLC+SNAP header), 331 * protocol = LLC+SNAP header with the IPv6 332 * Ethertype, address length = 16 333 */ 334 p += 10; 335 ND_TCHECK2(*p, al); 336 if (p + al > endp) 337 goto trunc; 338 339 ND_PRINT((ndo, "IPv6 (%u) %s", num, ip6addr_string(ndo, p))); 340 p += al; 341 } 342 else { 343 /* 344 * Generic case: just print raw data 345 */ 346 ND_TCHECK2(*p, pl); 347 if (p + pl > endp) 348 goto trunc; 349 ND_PRINT((ndo, "pt=0x%02x, pl=%d, pb=", *(p - 2), pl)); 350 while (pl-- > 0) 351 ND_PRINT((ndo, " %02x", *p++)); 352 ND_TCHECK2(*p, 2); 353 if (p + 2 > endp) 354 goto trunc; 355 al = (*p << 8) + *(p + 1); 356 ND_PRINT((ndo, ", al=%d, a=", al)); 357 p += 2; 358 ND_TCHECK2(*p, al); 359 if (p + al > endp) 360 goto trunc; 361 while (al-- > 0) 362 ND_PRINT((ndo, " %02x", *p++)); 363 } 364 num--; 365 if (num) 366 ND_PRINT((ndo, " ")); 367 } 368 369 return 0; 370 371 trunc: 372 return -1; 373 } 374 375 376 static int 377 cdp_print_prefixes(netdissect_options *ndo, 378 const u_char * p, int l) 379 { 380 if (l % 5) 381 goto trunc; 382 383 ND_PRINT((ndo, " IPv4 Prefixes (%d):", l / 5)); 384 385 while (l > 0) { 386 ND_PRINT((ndo, " %u.%u.%u.%u/%u", p[0], p[1], p[2], p[3], p[4])); 387 l -= 5; 388 p += 5; 389 } 390 391 return 0; 392 393 trunc: 394 return -1; 395 } 396 397 /* read in a <n>-byte number, MSB first 398 * (of course this can handle max sizeof(long)) 399 */ 400 static unsigned long cdp_get_number(const u_char * p, int l) 401 { 402 unsigned long res=0; 403 while( l>0 ) 404 { 405 res = (res<<8) + *p; 406 p++; l--; 407 } 408 return res; 409 } 410