1 1.1 christos /* 2 1.1 christos * Copyright (c) 1998-2006 The TCPDUMP project 3 1.1 christos * 4 1.1 christos * Redistribution and use in source and binary forms, with or without 5 1.1 christos * modification, are permitted provided that: (1) source code 6 1.1 christos * distributions retain the above copyright notice and this paragraph 7 1.1 christos * in its entirety, and (2) distributions including binary code include 8 1.1 christos * the above copyright notice and this paragraph in its entirety in 9 1.1 christos * the documentation or other materials provided with the distribution. 10 1.1 christos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 11 1.1 christos * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 12 1.1 christos * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 1.1 christos * FOR A PARTICULAR PURPOSE. 14 1.1 christos * 15 1.9 christos * Original code by Hannes Gredler (hannes (at) gredler.at) 16 1.1 christos */ 17 1.1 christos 18 1.8 spz /* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */ 19 1.8 spz 20 1.2 christos #include <sys/cdefs.h> 21 1.1 christos #ifndef lint 22 1.11 christos __RCSID("$NetBSD: print-cfm.c,v 1.11 2024/09/02 16:15:30 christos Exp $"); 23 1.1 christos #endif 24 1.1 christos 25 1.10 christos #include <config.h> 26 1.1 christos 27 1.10 christos #include "netdissect-stdinc.h" 28 1.1 christos 29 1.7 christos #include "netdissect.h" 30 1.1 christos #include "extract.h" 31 1.1 christos #include "addrtoname.h" 32 1.1 christos #include "oui.h" 33 1.1 christos #include "af.h" 34 1.1 christos 35 1.10 christos 36 1.1 christos struct cfm_common_header_t { 37 1.10 christos nd_uint8_t mdlevel_version; 38 1.10 christos nd_uint8_t opcode; 39 1.10 christos nd_uint8_t flags; 40 1.10 christos nd_uint8_t first_tlv_offset; 41 1.1 christos }; 42 1.1 christos 43 1.1 christos #define CFM_VERSION 0 44 1.10 christos #define CFM_EXTRACT_VERSION(x) ((x)&0x1f) 45 1.1 christos #define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5) 46 1.1 christos 47 1.1 christos #define CFM_OPCODE_CCM 1 48 1.1 christos #define CFM_OPCODE_LBR 2 49 1.1 christos #define CFM_OPCODE_LBM 3 50 1.1 christos #define CFM_OPCODE_LTR 4 51 1.1 christos #define CFM_OPCODE_LTM 5 52 1.1 christos 53 1.1 christos static const struct tok cfm_opcode_values[] = { 54 1.10 christos { CFM_OPCODE_CCM, "Continuity Check Message"}, 55 1.1 christos { CFM_OPCODE_LBR, "Loopback Reply"}, 56 1.1 christos { CFM_OPCODE_LBM, "Loopback Message"}, 57 1.1 christos { CFM_OPCODE_LTR, "Linktrace Reply"}, 58 1.1 christos { CFM_OPCODE_LTM, "Linktrace Message"}, 59 1.1 christos { 0, NULL} 60 1.1 christos }; 61 1.1 christos 62 1.1 christos /* 63 1.1 christos * Message Formats. 64 1.1 christos */ 65 1.1 christos struct cfm_ccm_t { 66 1.10 christos nd_uint32_t sequence; 67 1.10 christos nd_uint16_t ma_epi; 68 1.10 christos nd_byte names[48]; 69 1.10 christos nd_byte itu_t_y_1731[16]; 70 1.1 christos }; 71 1.1 christos 72 1.1 christos /* 73 1.1 christos * Timer Bases for the CCM Interval field. 74 1.1 christos * Expressed in units of seconds. 75 1.1 christos */ 76 1.10 christos static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f}; 77 1.1 christos #define CCM_INTERVAL_MIN_MULTIPLIER 3.25 78 1.1 christos #define CCM_INTERVAL_MAX_MULTIPLIER 3.5 79 1.1 christos 80 1.1 christos #define CFM_CCM_RDI_FLAG 0x80 81 1.10 christos #define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07) 82 1.1 christos 83 1.1 christos #define CFM_CCM_MD_FORMAT_8021 0 84 1.1 christos #define CFM_CCM_MD_FORMAT_NONE 1 85 1.1 christos #define CFM_CCM_MD_FORMAT_DNS 2 86 1.1 christos #define CFM_CCM_MD_FORMAT_MAC 3 87 1.1 christos #define CFM_CCM_MD_FORMAT_CHAR 4 88 1.1 christos 89 1.1 christos static const struct tok cfm_md_nameformat_values[] = { 90 1.1 christos { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"}, 91 1.1 christos { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"}, 92 1.1 christos { CFM_CCM_MD_FORMAT_DNS, "DNS string"}, 93 1.1 christos { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"}, 94 1.1 christos { CFM_CCM_MD_FORMAT_CHAR, "Character string"}, 95 1.1 christos { 0, NULL} 96 1.1 christos }; 97 1.1 christos 98 1.1 christos #define CFM_CCM_MA_FORMAT_8021 0 99 1.1 christos #define CFM_CCM_MA_FORMAT_VID 1 100 1.1 christos #define CFM_CCM_MA_FORMAT_CHAR 2 101 1.1 christos #define CFM_CCM_MA_FORMAT_INT 3 102 1.1 christos #define CFM_CCM_MA_FORMAT_VPN 4 103 1.1 christos 104 1.1 christos static const struct tok cfm_ma_nameformat_values[] = { 105 1.1 christos { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"}, 106 1.1 christos { CFM_CCM_MA_FORMAT_VID, "Primary VID"}, 107 1.1 christos { CFM_CCM_MA_FORMAT_CHAR, "Character string"}, 108 1.1 christos { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"}, 109 1.1 christos { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"}, 110 1.1 christos { 0, NULL} 111 1.1 christos }; 112 1.1 christos 113 1.1 christos struct cfm_lbm_t { 114 1.10 christos nd_uint32_t transaction_id; 115 1.1 christos }; 116 1.1 christos 117 1.1 christos struct cfm_ltm_t { 118 1.10 christos nd_uint32_t transaction_id; 119 1.10 christos nd_uint8_t ttl; 120 1.10 christos nd_mac_addr original_mac; 121 1.10 christos nd_mac_addr target_mac; 122 1.1 christos }; 123 1.1 christos 124 1.1 christos static const struct tok cfm_ltm_flag_values[] = { 125 1.1 christos { 0x80, "Use Forwarding-DB only"}, 126 1.1 christos { 0, NULL} 127 1.1 christos }; 128 1.1 christos 129 1.1 christos struct cfm_ltr_t { 130 1.10 christos nd_uint32_t transaction_id; 131 1.10 christos nd_uint8_t ttl; 132 1.10 christos nd_uint8_t replay_action; 133 1.1 christos }; 134 1.1 christos 135 1.1 christos static const struct tok cfm_ltr_flag_values[] = { 136 1.5 christos { 0x80, "UseFDB Only"}, 137 1.5 christos { 0x40, "FwdYes"}, 138 1.5 christos { 0x20, "Terminal MEP"}, 139 1.1 christos { 0, NULL} 140 1.1 christos }; 141 1.1 christos 142 1.1 christos static const struct tok cfm_ltr_replay_action_values[] = { 143 1.1 christos { 1, "Exact Match"}, 144 1.1 christos { 2, "Filtering DB"}, 145 1.1 christos { 3, "MIP CCM DB"}, 146 1.1 christos { 0, NULL} 147 1.1 christos }; 148 1.1 christos 149 1.1 christos 150 1.1 christos #define CFM_TLV_END 0 151 1.1 christos #define CFM_TLV_SENDER_ID 1 152 1.1 christos #define CFM_TLV_PORT_STATUS 2 153 1.1 christos #define CFM_TLV_INTERFACE_STATUS 3 154 1.1 christos #define CFM_TLV_DATA 4 155 1.1 christos #define CFM_TLV_REPLY_INGRESS 5 156 1.1 christos #define CFM_TLV_REPLY_EGRESS 6 157 1.1 christos #define CFM_TLV_PRIVATE 31 158 1.1 christos 159 1.1 christos static const struct tok cfm_tlv_values[] = { 160 1.1 christos { CFM_TLV_END, "End"}, 161 1.1 christos { CFM_TLV_SENDER_ID, "Sender ID"}, 162 1.1 christos { CFM_TLV_PORT_STATUS, "Port status"}, 163 1.1 christos { CFM_TLV_INTERFACE_STATUS, "Interface status"}, 164 1.1 christos { CFM_TLV_DATA, "Data"}, 165 1.1 christos { CFM_TLV_REPLY_INGRESS, "Reply Ingress"}, 166 1.1 christos { CFM_TLV_REPLY_EGRESS, "Reply Egress"}, 167 1.1 christos { CFM_TLV_PRIVATE, "Organization Specific"}, 168 1.1 christos { 0, NULL} 169 1.1 christos }; 170 1.1 christos 171 1.1 christos /* 172 1.1 christos * TLVs 173 1.1 christos */ 174 1.1 christos 175 1.1 christos struct cfm_tlv_header_t { 176 1.10 christos nd_uint8_t type; 177 1.10 christos nd_uint16_t length; 178 1.1 christos }; 179 1.1 christos 180 1.1 christos /* FIXME define TLV formats */ 181 1.1 christos 182 1.1 christos static const struct tok cfm_tlv_port_status_values[] = { 183 1.1 christos { 1, "Blocked"}, 184 1.1 christos { 2, "Up"}, 185 1.1 christos { 0, NULL} 186 1.1 christos }; 187 1.1 christos 188 1.1 christos static const struct tok cfm_tlv_interface_status_values[] = { 189 1.1 christos { 1, "Up"}, 190 1.1 christos { 2, "Down"}, 191 1.1 christos { 3, "Testing"}, 192 1.1 christos { 5, "Dormant"}, 193 1.1 christos { 6, "not present"}, 194 1.1 christos { 7, "lower Layer down"}, 195 1.1 christos { 0, NULL} 196 1.1 christos }; 197 1.1 christos 198 1.1 christos #define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1 199 1.1 christos #define CFM_CHASSIS_ID_INTERFACE_ALIAS 2 200 1.1 christos #define CFM_CHASSIS_ID_PORT_COMPONENT 3 201 1.1 christos #define CFM_CHASSIS_ID_MAC_ADDRESS 4 202 1.1 christos #define CFM_CHASSIS_ID_NETWORK_ADDRESS 5 203 1.1 christos #define CFM_CHASSIS_ID_INTERFACE_NAME 6 204 1.1 christos #define CFM_CHASSIS_ID_LOCAL 7 205 1.1 christos 206 1.1 christos static const struct tok cfm_tlv_senderid_chassisid_values[] = { 207 1.1 christos { 0, "Reserved"}, 208 1.1 christos { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"}, 209 1.1 christos { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"}, 210 1.1 christos { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"}, 211 1.1 christos { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"}, 212 1.1 christos { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"}, 213 1.1 christos { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"}, 214 1.1 christos { CFM_CHASSIS_ID_LOCAL, "Locally assigned"}, 215 1.1 christos { 0, NULL} 216 1.1 christos }; 217 1.1 christos 218 1.1 christos 219 1.5 christos static int 220 1.8 spz cfm_network_addr_print(netdissect_options *ndo, 221 1.10 christos const u_char *tptr, const u_int length) 222 1.6 christos { 223 1.8 spz u_int network_addr_type; 224 1.1 christos u_int hexdump = FALSE; 225 1.1 christos 226 1.1 christos /* 227 1.10 christos * Although AFIs are typically 2 octets wide, 228 1.1 christos * 802.1ab specifies that this field width 229 1.10 christos * is only one octet. 230 1.1 christos */ 231 1.9 christos if (length < 1) { 232 1.10 christos ND_PRINT("\n\t Network Address Type (invalid, no data"); 233 1.9 christos return hexdump; 234 1.9 christos } 235 1.9 christos /* The calling function must make any due ND_TCHECK calls. */ 236 1.10 christos network_addr_type = GET_U_1(tptr); 237 1.10 christos ND_PRINT("\n\t Network Address Type %s (%u)", 238 1.8 spz tok2str(af_values, "Unknown", network_addr_type), 239 1.10 christos network_addr_type); 240 1.1 christos 241 1.1 christos /* 242 1.1 christos * Resolve the passed in Address. 243 1.1 christos */ 244 1.8 spz switch(network_addr_type) { 245 1.1 christos case AFNUM_INET: 246 1.9 christos if (length != 1 + 4) { 247 1.10 christos ND_PRINT("(invalid IPv4 address length %u)", length - 1); 248 1.9 christos hexdump = TRUE; 249 1.9 christos break; 250 1.9 christos } 251 1.10 christos ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1)); 252 1.1 christos break; 253 1.1 christos 254 1.1 christos case AFNUM_INET6: 255 1.9 christos if (length != 1 + 16) { 256 1.10 christos ND_PRINT("(invalid IPv6 address length %u)", length - 1); 257 1.9 christos hexdump = TRUE; 258 1.9 christos break; 259 1.9 christos } 260 1.10 christos ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1)); 261 1.1 christos break; 262 1.1 christos 263 1.1 christos default: 264 1.1 christos hexdump = TRUE; 265 1.1 christos break; 266 1.1 christos } 267 1.1 christos 268 1.1 christos return hexdump; 269 1.1 christos } 270 1.1 christos 271 1.1 christos void 272 1.5 christos cfm_print(netdissect_options *ndo, 273 1.10 christos const u_char *pptr, u_int length) 274 1.6 christos { 275 1.1 christos const struct cfm_common_header_t *cfm_common_header; 276 1.10 christos uint8_t mdlevel_version, opcode, flags, first_tlv_offset; 277 1.1 christos const struct cfm_tlv_header_t *cfm_tlv_header; 278 1.8 spz const uint8_t *tptr, *tlv_ptr; 279 1.8 spz const uint8_t *namesp; 280 1.8 spz u_int names_data_remaining; 281 1.8 spz uint8_t md_nameformat, md_namelength; 282 1.8 spz const uint8_t *md_name; 283 1.8 spz uint8_t ma_nameformat, ma_namelength; 284 1.8 spz const uint8_t *ma_name; 285 1.1 christos u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval; 286 1.1 christos 287 1.1 christos 288 1.1 christos union { 289 1.1 christos const struct cfm_ccm_t *cfm_ccm; 290 1.1 christos const struct cfm_lbm_t *cfm_lbm; 291 1.1 christos const struct cfm_ltm_t *cfm_ltm; 292 1.1 christos const struct cfm_ltr_t *cfm_ltr; 293 1.1 christos } msg_ptr; 294 1.1 christos 295 1.10 christos ndo->ndo_protocol = "cfm"; 296 1.1 christos tptr=pptr; 297 1.1 christos cfm_common_header = (const struct cfm_common_header_t *)pptr; 298 1.8 spz if (length < sizeof(*cfm_common_header)) 299 1.8 spz goto tooshort; 300 1.10 christos ND_TCHECK_SIZE(cfm_common_header); 301 1.1 christos 302 1.1 christos /* 303 1.1 christos * Sanity checking of the header. 304 1.1 christos */ 305 1.10 christos mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version); 306 1.10 christos if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) { 307 1.10 christos ND_PRINT("CFMv%u not supported, length %u", 308 1.10 christos CFM_EXTRACT_VERSION(mdlevel_version), length); 309 1.1 christos return; 310 1.1 christos } 311 1.1 christos 312 1.10 christos opcode = GET_U_1(cfm_common_header->opcode); 313 1.10 christos ND_PRINT("CFMv%u %s, MD Level %u, length %u", 314 1.10 christos CFM_EXTRACT_VERSION(mdlevel_version), 315 1.10 christos tok2str(cfm_opcode_values, "unknown (%u)", opcode), 316 1.10 christos CFM_EXTRACT_MD_LEVEL(mdlevel_version), 317 1.10 christos length); 318 1.1 christos 319 1.1 christos /* 320 1.1 christos * In non-verbose mode just print the opcode and md-level. 321 1.1 christos */ 322 1.5 christos if (ndo->ndo_vflag < 1) { 323 1.1 christos return; 324 1.1 christos } 325 1.1 christos 326 1.10 christos flags = GET_U_1(cfm_common_header->flags); 327 1.10 christos first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset); 328 1.10 christos ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset); 329 1.1 christos 330 1.10 christos tptr += sizeof(struct cfm_common_header_t); 331 1.1 christos tlen = length - sizeof(struct cfm_common_header_t); 332 1.1 christos 333 1.8 spz /* 334 1.8 spz * Sanity check the first TLV offset. 335 1.8 spz */ 336 1.10 christos if (first_tlv_offset > tlen) { 337 1.10 christos ND_PRINT(" (too large, must be <= %u)", tlen); 338 1.8 spz return; 339 1.8 spz } 340 1.8 spz 341 1.10 christos switch (opcode) { 342 1.1 christos case CFM_OPCODE_CCM: 343 1.1 christos msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr; 344 1.10 christos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) { 345 1.10 christos ND_PRINT(" (too small 1, must be >= %zu)", 346 1.10 christos sizeof(*msg_ptr.cfm_ccm)); 347 1.8 spz return; 348 1.8 spz } 349 1.8 spz if (tlen < sizeof(*msg_ptr.cfm_ccm)) 350 1.8 spz goto tooshort; 351 1.10 christos ND_TCHECK_SIZE(msg_ptr.cfm_ccm); 352 1.1 christos 353 1.10 christos ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags); 354 1.10 christos ND_PRINT(", Flags [CCM Interval %u%s]", 355 1.1 christos ccm_interval, 356 1.10 christos flags & CFM_CCM_RDI_FLAG ? 357 1.10 christos ", RDI" : ""); 358 1.1 christos 359 1.1 christos /* 360 1.1 christos * Resolve the CCM interval field. 361 1.1 christos */ 362 1.1 christos if (ccm_interval) { 363 1.10 christos ND_PRINT("\n\t CCM Interval %.3fs" 364 1.1 christos ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs", 365 1.1 christos ccm_interval_base[ccm_interval], 366 1.1 christos ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER, 367 1.10 christos ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER); 368 1.1 christos } 369 1.1 christos 370 1.10 christos ND_PRINT("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x", 371 1.10 christos GET_BE_U_4(msg_ptr.cfm_ccm->sequence), 372 1.10 christos GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi)); 373 1.1 christos 374 1.8 spz namesp = msg_ptr.cfm_ccm->names; 375 1.8 spz names_data_remaining = sizeof(msg_ptr.cfm_ccm->names); 376 1.1 christos 377 1.1 christos /* 378 1.1 christos * Resolve the MD fields. 379 1.1 christos */ 380 1.10 christos md_nameformat = GET_U_1(namesp); 381 1.8 spz namesp++; 382 1.8 spz names_data_remaining--; /* We know this is != 0 */ 383 1.8 spz if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) { 384 1.10 christos md_namelength = GET_U_1(namesp); 385 1.8 spz namesp++; 386 1.8 spz names_data_remaining--; /* We know this is !=0 */ 387 1.10 christos ND_PRINT("\n\t MD Name Format %s (%u), MD Name length %u", 388 1.8 spz tok2str(cfm_md_nameformat_values, "Unknown", 389 1.8 spz md_nameformat), 390 1.8 spz md_nameformat, 391 1.10 christos md_namelength); 392 1.8 spz 393 1.9 christos /* 394 1.9 christos * -3 for the MA short name format and length and one byte 395 1.9 christos * of MA short name. 396 1.9 christos */ 397 1.9 christos if (md_namelength > names_data_remaining - 3) { 398 1.10 christos ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2); 399 1.8 spz return; 400 1.8 spz } 401 1.1 christos 402 1.8 spz md_name = namesp; 403 1.10 christos ND_PRINT("\n\t MD Name: "); 404 1.8 spz switch (md_nameformat) { 405 1.1 christos case CFM_CCM_MD_FORMAT_DNS: 406 1.1 christos case CFM_CCM_MD_FORMAT_CHAR: 407 1.10 christos nd_printjnp(ndo, md_name, md_namelength); 408 1.1 christos break; 409 1.1 christos 410 1.1 christos case CFM_CCM_MD_FORMAT_MAC: 411 1.10 christos if (md_namelength == MAC_ADDR_LEN) { 412 1.10 christos ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(md_name)); 413 1.8 spz } else { 414 1.10 christos ND_PRINT("\n\t MAC (length invalid)"); 415 1.8 spz } 416 1.1 christos break; 417 1.1 christos 418 1.1 christos /* FIXME add printers for those MD formats - hexdump for now */ 419 1.1 christos case CFM_CCM_MA_FORMAT_8021: 420 1.1 christos default: 421 1.8 spz print_unknown_data(ndo, md_name, "\n\t ", 422 1.8 spz md_namelength); 423 1.1 christos } 424 1.8 spz namesp += md_namelength; 425 1.8 spz names_data_remaining -= md_namelength; 426 1.8 spz } else { 427 1.10 christos ND_PRINT("\n\t MD Name Format %s (%u)", 428 1.8 spz tok2str(cfm_md_nameformat_values, "Unknown", 429 1.8 spz md_nameformat), 430 1.10 christos md_nameformat); 431 1.1 christos } 432 1.1 christos 433 1.1 christos 434 1.1 christos /* 435 1.1 christos * Resolve the MA fields. 436 1.1 christos */ 437 1.10 christos ma_nameformat = GET_U_1(namesp); 438 1.8 spz namesp++; 439 1.8 spz names_data_remaining--; /* We know this is != 0 */ 440 1.10 christos ma_namelength = GET_U_1(namesp); 441 1.8 spz namesp++; 442 1.8 spz names_data_remaining--; /* We know this is != 0 */ 443 1.10 christos ND_PRINT("\n\t MA Name-Format %s (%u), MA name length %u", 444 1.1 christos tok2str(cfm_ma_nameformat_values, "Unknown", 445 1.8 spz ma_nameformat), 446 1.8 spz ma_nameformat, 447 1.10 christos ma_namelength); 448 1.8 spz 449 1.8 spz if (ma_namelength > names_data_remaining) { 450 1.10 christos ND_PRINT(" (too large, must be <= %u)", names_data_remaining); 451 1.8 spz return; 452 1.8 spz } 453 1.1 christos 454 1.8 spz ma_name = namesp; 455 1.10 christos ND_PRINT("\n\t MA Name: "); 456 1.8 spz switch (ma_nameformat) { 457 1.1 christos case CFM_CCM_MA_FORMAT_CHAR: 458 1.10 christos nd_printjnp(ndo, ma_name, ma_namelength); 459 1.1 christos break; 460 1.1 christos 461 1.1 christos /* FIXME add printers for those MA formats - hexdump for now */ 462 1.1 christos case CFM_CCM_MA_FORMAT_8021: 463 1.1 christos case CFM_CCM_MA_FORMAT_VID: 464 1.1 christos case CFM_CCM_MA_FORMAT_INT: 465 1.1 christos case CFM_CCM_MA_FORMAT_VPN: 466 1.1 christos default: 467 1.8 spz print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength); 468 1.1 christos } 469 1.1 christos break; 470 1.1 christos 471 1.1 christos case CFM_OPCODE_LTM: 472 1.1 christos msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr; 473 1.10 christos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) { 474 1.10 christos ND_PRINT(" (too small 4, must be >= %zu)", 475 1.10 christos sizeof(*msg_ptr.cfm_ltm)); 476 1.8 spz return; 477 1.8 spz } 478 1.8 spz if (tlen < sizeof(*msg_ptr.cfm_ltm)) 479 1.8 spz goto tooshort; 480 1.10 christos ND_TCHECK_SIZE(msg_ptr.cfm_ltm); 481 1.1 christos 482 1.10 christos ND_PRINT(", Flags [%s]", 483 1.10 christos bittok2str(cfm_ltm_flag_values, "none", flags)); 484 1.1 christos 485 1.10 christos ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 486 1.10 christos GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id), 487 1.10 christos GET_U_1(msg_ptr.cfm_ltm->ttl)); 488 1.10 christos 489 1.10 christos ND_PRINT("\n\t Original-MAC %s, Target-MAC %s", 490 1.10 christos GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac), 491 1.10 christos GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac)); 492 1.1 christos break; 493 1.1 christos 494 1.1 christos case CFM_OPCODE_LTR: 495 1.1 christos msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr; 496 1.10 christos if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) { 497 1.10 christos ND_PRINT(" (too small 5, must be >= %zu)", 498 1.10 christos sizeof(*msg_ptr.cfm_ltr)); 499 1.8 spz return; 500 1.8 spz } 501 1.8 spz if (tlen < sizeof(*msg_ptr.cfm_ltr)) 502 1.8 spz goto tooshort; 503 1.10 christos ND_TCHECK_SIZE(msg_ptr.cfm_ltr); 504 1.1 christos 505 1.10 christos ND_PRINT(", Flags [%s]", 506 1.10 christos bittok2str(cfm_ltr_flag_values, "none", flags)); 507 1.1 christos 508 1.10 christos ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u", 509 1.10 christos GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id), 510 1.10 christos GET_U_1(msg_ptr.cfm_ltr->ttl)); 511 1.1 christos 512 1.10 christos ND_PRINT("\n\t Replay-Action %s (%u)", 513 1.1 christos tok2str(cfm_ltr_replay_action_values, 514 1.1 christos "Unknown", 515 1.10 christos GET_U_1(msg_ptr.cfm_ltr->replay_action)), 516 1.10 christos GET_U_1(msg_ptr.cfm_ltr->replay_action)); 517 1.1 christos break; 518 1.1 christos 519 1.1 christos /* 520 1.1 christos * No message decoder yet. 521 1.1 christos * Hexdump everything up until the start of the TLVs 522 1.1 christos */ 523 1.1 christos case CFM_OPCODE_LBR: 524 1.1 christos case CFM_OPCODE_LBM: 525 1.1 christos default: 526 1.8 spz print_unknown_data(ndo, tptr, "\n\t ", 527 1.10 christos tlen - first_tlv_offset); 528 1.1 christos break; 529 1.1 christos } 530 1.1 christos 531 1.10 christos tptr += first_tlv_offset; 532 1.10 christos tlen -= first_tlv_offset; 533 1.5 christos 534 1.1 christos while (tlen > 0) { 535 1.1 christos cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr; 536 1.1 christos 537 1.1 christos /* Enough to read the tlv type ? */ 538 1.10 christos cfm_tlv_type = GET_U_1(cfm_tlv_header->type); 539 1.1 christos 540 1.10 christos ND_PRINT("\n\t%s TLV (0x%02x)", 541 1.1 christos tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type), 542 1.10 christos cfm_tlv_type); 543 1.1 christos 544 1.8 spz if (cfm_tlv_type == CFM_TLV_END) { 545 1.8 spz /* Length is "Not present if the Type field is 0." */ 546 1.1 christos return; 547 1.1 christos } 548 1.1 christos 549 1.8 spz /* do we have the full tlv header ? */ 550 1.8 spz if (tlen < sizeof(struct cfm_tlv_header_t)) 551 1.8 spz goto tooshort; 552 1.10 christos ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t)); 553 1.10 christos cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length); 554 1.8 spz 555 1.10 christos ND_PRINT(", length %u", cfm_tlv_len); 556 1.8 spz 557 1.1 christos tptr += sizeof(struct cfm_tlv_header_t); 558 1.1 christos tlen -= sizeof(struct cfm_tlv_header_t); 559 1.1 christos tlv_ptr = tptr; 560 1.1 christos 561 1.8 spz /* do we have the full tlv ? */ 562 1.8 spz if (tlen < cfm_tlv_len) 563 1.8 spz goto tooshort; 564 1.10 christos ND_TCHECK_LEN(tptr, cfm_tlv_len); 565 1.1 christos hexdump = FALSE; 566 1.1 christos 567 1.1 christos switch(cfm_tlv_type) { 568 1.1 christos case CFM_TLV_PORT_STATUS: 569 1.8 spz if (cfm_tlv_len < 1) { 570 1.10 christos ND_PRINT(" (too short, must be >= 1)"); 571 1.8 spz return; 572 1.8 spz } 573 1.10 christos ND_PRINT(", Status: %s (%u)", 574 1.10 christos tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)), 575 1.10 christos GET_U_1(tptr)); 576 1.1 christos break; 577 1.1 christos 578 1.1 christos case CFM_TLV_INTERFACE_STATUS: 579 1.8 spz if (cfm_tlv_len < 1) { 580 1.10 christos ND_PRINT(" (too short, must be >= 1)"); 581 1.8 spz return; 582 1.8 spz } 583 1.10 christos ND_PRINT(", Status: %s (%u)", 584 1.10 christos tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)), 585 1.10 christos GET_U_1(tptr)); 586 1.1 christos break; 587 1.1 christos 588 1.1 christos case CFM_TLV_PRIVATE: 589 1.8 spz if (cfm_tlv_len < 4) { 590 1.10 christos ND_PRINT(" (too short, must be >= 4)"); 591 1.8 spz return; 592 1.8 spz } 593 1.10 christos ND_PRINT(", Vendor: %s (%u), Sub-Type %u", 594 1.10 christos tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)), 595 1.10 christos GET_BE_U_3(tptr), 596 1.10 christos GET_U_1(tptr + 3)); 597 1.1 christos hexdump = TRUE; 598 1.1 christos break; 599 1.1 christos 600 1.1 christos case CFM_TLV_SENDER_ID: 601 1.1 christos { 602 1.1 christos u_int chassis_id_type, chassis_id_length; 603 1.1 christos u_int mgmt_addr_length; 604 1.1 christos 605 1.8 spz if (cfm_tlv_len < 1) { 606 1.10 christos ND_PRINT(" (too short, must be >= 1)"); 607 1.9 christos goto next_tlv; 608 1.8 spz } 609 1.8 spz 610 1.1 christos /* 611 1.8 spz * Get the Chassis ID length and check it. 612 1.9 christos * IEEE 802.1Q-2014 Section 21.5.3.1 613 1.1 christos */ 614 1.10 christos chassis_id_length = GET_U_1(tptr); 615 1.1 christos tptr++; 616 1.1 christos tlen--; 617 1.8 spz cfm_tlv_len--; 618 1.1 christos 619 1.1 christos if (chassis_id_length) { 620 1.9 christos /* 621 1.9 christos * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references 622 1.9 christos * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently 623 1.9 christos * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype 624 1.9 christos */ 625 1.8 spz if (cfm_tlv_len < 1) { 626 1.10 christos ND_PRINT("\n\t (TLV too short)"); 627 1.9 christos goto next_tlv; 628 1.8 spz } 629 1.10 christos chassis_id_type = GET_U_1(tptr); 630 1.8 spz cfm_tlv_len--; 631 1.10 christos ND_PRINT("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u", 632 1.1 christos tok2str(cfm_tlv_senderid_chassisid_values, 633 1.1 christos "Unknown", 634 1.1 christos chassis_id_type), 635 1.1 christos chassis_id_type, 636 1.10 christos chassis_id_length); 637 1.1 christos 638 1.8 spz if (cfm_tlv_len < chassis_id_length) { 639 1.10 christos ND_PRINT("\n\t (TLV too short)"); 640 1.9 christos goto next_tlv; 641 1.8 spz } 642 1.8 spz 643 1.9 christos /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */ 644 1.1 christos switch (chassis_id_type) { 645 1.1 christos case CFM_CHASSIS_ID_MAC_ADDRESS: 646 1.10 christos if (chassis_id_length != MAC_ADDR_LEN) { 647 1.10 christos ND_PRINT(" (invalid MAC address length)"); 648 1.9 christos hexdump = TRUE; 649 1.9 christos break; 650 1.9 christos } 651 1.10 christos ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(tptr + 1)); 652 1.1 christos break; 653 1.1 christos 654 1.1 christos case CFM_CHASSIS_ID_NETWORK_ADDRESS: 655 1.9 christos hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length); 656 1.1 christos break; 657 1.1 christos 658 1.1 christos case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */ 659 1.1 christos case CFM_CHASSIS_ID_INTERFACE_ALIAS: 660 1.1 christos case CFM_CHASSIS_ID_LOCAL: 661 1.1 christos case CFM_CHASSIS_ID_CHASSIS_COMPONENT: 662 1.1 christos case CFM_CHASSIS_ID_PORT_COMPONENT: 663 1.10 christos nd_printjnp(ndo, tptr + 1, chassis_id_length); 664 1.1 christos break; 665 1.1 christos 666 1.1 christos default: 667 1.1 christos hexdump = TRUE; 668 1.1 christos break; 669 1.1 christos } 670 1.8 spz cfm_tlv_len -= chassis_id_length; 671 1.8 spz 672 1.8 spz tptr += 1 + chassis_id_length; 673 1.8 spz tlen -= 1 + chassis_id_length; 674 1.1 christos } 675 1.1 christos 676 1.1 christos /* 677 1.1 christos * Check if there is a Management Address. 678 1.9 christos * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length 679 1.9 christos * This and all subsequent fields are not present if the TLV length 680 1.9 christos * allows only the above fields. 681 1.1 christos */ 682 1.8 spz if (cfm_tlv_len == 0) { 683 1.8 spz /* No, there isn't; we're done. */ 684 1.9 christos break; 685 1.1 christos } 686 1.1 christos 687 1.9 christos /* Here mgmt_addr_length stands for the management domain length. */ 688 1.10 christos mgmt_addr_length = GET_U_1(tptr); 689 1.1 christos tptr++; 690 1.1 christos tlen--; 691 1.8 spz cfm_tlv_len--; 692 1.10 christos ND_PRINT("\n\t Management Address Domain Length %u", mgmt_addr_length); 693 1.1 christos if (mgmt_addr_length) { 694 1.9 christos /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */ 695 1.8 spz if (cfm_tlv_len < mgmt_addr_length) { 696 1.10 christos ND_PRINT("\n\t (TLV too short)"); 697 1.9 christos goto next_tlv; 698 1.8 spz } 699 1.8 spz cfm_tlv_len -= mgmt_addr_length; 700 1.8 spz /* 701 1.8 spz * XXX - this is an OID; print it as such. 702 1.8 spz */ 703 1.9 christos hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length); 704 1.8 spz tptr += mgmt_addr_length; 705 1.8 spz tlen -= mgmt_addr_length; 706 1.8 spz 707 1.9 christos /* 708 1.9 christos * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length 709 1.9 christos * This field is present if Management Address Domain Length is not 0. 710 1.9 christos */ 711 1.8 spz if (cfm_tlv_len < 1) { 712 1.10 christos ND_PRINT(" (Management Address Length is missing)"); 713 1.9 christos hexdump = TRUE; 714 1.9 christos break; 715 1.8 spz } 716 1.9 christos 717 1.9 christos /* Here mgmt_addr_length stands for the management address length. */ 718 1.10 christos mgmt_addr_length = GET_U_1(tptr); 719 1.8 spz tptr++; 720 1.8 spz tlen--; 721 1.8 spz cfm_tlv_len--; 722 1.10 christos ND_PRINT("\n\t Management Address Length %u", mgmt_addr_length); 723 1.8 spz if (mgmt_addr_length) { 724 1.9 christos /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */ 725 1.8 spz if (cfm_tlv_len < mgmt_addr_length) { 726 1.10 christos ND_PRINT("\n\t (TLV too short)"); 727 1.8 spz return; 728 1.8 spz } 729 1.8 spz cfm_tlv_len -= mgmt_addr_length; 730 1.8 spz /* 731 1.8 spz * XXX - this is a TransportDomain; print it as such. 732 1.8 spz */ 733 1.9 christos hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length); 734 1.8 spz tptr += mgmt_addr_length; 735 1.8 spz tlen -= mgmt_addr_length; 736 1.8 spz } 737 1.1 christos } 738 1.8 spz break; 739 1.1 christos } 740 1.1 christos 741 1.1 christos /* 742 1.1 christos * FIXME those are the defined TLVs that lack a decoder 743 1.1 christos * you are welcome to contribute code ;-) 744 1.1 christos */ 745 1.1 christos 746 1.1 christos case CFM_TLV_DATA: 747 1.1 christos case CFM_TLV_REPLY_INGRESS: 748 1.1 christos case CFM_TLV_REPLY_EGRESS: 749 1.1 christos default: 750 1.1 christos hexdump = TRUE; 751 1.1 christos break; 752 1.1 christos } 753 1.1 christos /* do we want to see an additional hexdump ? */ 754 1.5 christos if (hexdump || ndo->ndo_vflag > 1) 755 1.5 christos print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len); 756 1.1 christos 757 1.9 christos next_tlv: 758 1.1 christos tptr+=cfm_tlv_len; 759 1.1 christos tlen-=cfm_tlv_len; 760 1.1 christos } 761 1.1 christos return; 762 1.8 spz 763 1.8 spz tooshort: 764 1.10 christos ND_PRINT("\n\t\t packet is too short"); 765 1.8 spz return; 766 1.8 spz 767 1.1 christos trunc: 768 1.10 christos nd_print_trunc(ndo); 769 1.1 christos } 770