print-cfm.c 24 KB

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