1/* 2 * Redistribution and use in source and binary forms, with or without 3 * modification, are permitted provided that: (1) source code 4 * distributions retain the above copyright notice and this paragraph 5 * in its entirety, and (2) distributions including binary code include 6 * the above copyright notice and this paragraph in its entirety in 7 * the documentation or other materials provided with the distribution. 8 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 9 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 10 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 11 * FOR A PARTICULAR PURPOSE. 12 * 13 * Original code by Hannes Gredler (hannes@gredler.at) 14 */ 15 16#include <sys/cdefs.h> 17#ifndef lint 18__RCSID("$NetBSD: print-bfd.c,v 1.9 2023/08/17 20:19:40 christos Exp $"); 19#endif 20 21/* \summary: Bidirectional Forwarding Detection (BFD) printer */ 22 23/* 24 * specification: draft-ietf-bfd-base-01 for version 0, 25 * RFC 5880 for version 1, and RFC 5881 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include <config.h> 30#endif 31 32#include "netdissect-stdinc.h" 33 34#define ND_LONGJMP_FROM_TCHECK 35#include "netdissect.h" 36#include "extract.h" 37 38#include "udp.h" 39 40/* 41 * Control packet, BFDv0, draft-ietf-bfd-base-01 42 * 43 * 0 1 2 3 44 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 46 * |Vers | Diag |H|D|P|F|C|A|Rsv| Detect Mult | Length | 47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 * | My Discriminator | 49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 * | Your Discriminator | 51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 * | Desired Min TX Interval | 53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 * | Required Min RX Interval | 55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 56 * | Required Min Echo RX Interval | 57 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 58 */ 59 60/* 61 * Control packet, BFDv1, RFC 5880 62 * 63 * 0 1 2 3 64 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 66 * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length | 67 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 * | My Discriminator | 69 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 * | Your Discriminator | 71 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 * | Desired Min TX Interval | 73 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 * | Required Min RX Interval | 75 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 * | Required Min Echo RX Interval | 77 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 78 */ 79 80struct bfd_header_t { 81 nd_uint8_t version_diag; 82 nd_uint8_t flags; 83 nd_uint8_t detect_time_multiplier; 84 nd_uint8_t length; 85 nd_uint32_t my_discriminator; 86 nd_uint32_t your_discriminator; 87 nd_uint32_t desired_min_tx_interval; 88 nd_uint32_t required_min_rx_interval; 89 nd_uint32_t required_min_echo_interval; 90}; 91 92/* 93 * An optional Authentication Header may be present 94 * 95 * 0 1 2 3 96 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 97 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 98 * | Auth Type | Auth Len | Authentication Data... | 99 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 100 */ 101 102struct bfd_auth_header_t { 103 nd_uint8_t auth_type; 104 nd_uint8_t auth_len; 105 nd_uint8_t auth_data; 106 nd_uint8_t dummy; /* minimum 4 bytes */ 107}; 108 109enum auth_type { 110 AUTH_PASSWORD = 1, 111 AUTH_MD5 = 2, 112 AUTH_MET_MD5 = 3, 113 AUTH_SHA1 = 4, 114 AUTH_MET_SHA1 = 5 115}; 116 117static const struct tok bfd_v1_authentication_values[] = { 118 { AUTH_PASSWORD, "Simple Password" }, 119 { AUTH_MD5, "Keyed MD5" }, 120 { AUTH_MET_MD5, "Meticulous Keyed MD5" }, 121 { AUTH_SHA1, "Keyed SHA1" }, 122 { AUTH_MET_SHA1, "Meticulous Keyed SHA1" }, 123 { 0, NULL } 124}; 125 126enum auth_length { 127 AUTH_PASSWORD_FIELD_MIN_LEN = 4, /* header + password min: 3 + 1 */ 128 AUTH_PASSWORD_FIELD_MAX_LEN = 19, /* header + password max: 3 + 16 */ 129 AUTH_MD5_FIELD_LEN = 24, 130 AUTH_MD5_HASH_LEN = 16, 131 AUTH_SHA1_FIELD_LEN = 28, 132 AUTH_SHA1_HASH_LEN = 20 133}; 134 135#define BFD_EXTRACT_VERSION(x) (((x)&0xe0)>>5) 136#define BFD_EXTRACT_DIAG(x) ((x)&0x1f) 137 138static const struct tok bfd_diag_values[] = { 139 { 0, "No Diagnostic" }, 140 { 1, "Control Detection Time Expired" }, 141 { 2, "Echo Function Failed" }, 142 { 3, "Neighbor Signaled Session Down" }, 143 { 4, "Forwarding Plane Reset" }, 144 { 5, "Path Down" }, 145 { 6, "Concatenated Path Down" }, 146 { 7, "Administratively Down" }, 147 { 8, "Reverse Concatenated Path Down" }, 148 { 0, NULL } 149}; 150 151static const struct tok bfd_port_values[] = { 152 { BFD_CONTROL_PORT, "Control" }, 153 { BFD_MULTIHOP_PORT, "Multihop" }, 154 { BFD_LAG_PORT, "Lag" }, 155 { 0, NULL } 156}; 157 158#define BFD_FLAG_AUTH 0x04 159 160static const struct tok bfd_v0_flag_values[] = { 161 { 0x80, "I Hear You" }, 162 { 0x40, "Demand" }, 163 { 0x20, "Poll" }, 164 { 0x10, "Final" }, 165 { 0x08, "Control Plane Independent" }, 166 { BFD_FLAG_AUTH, "Authentication Present" }, 167 { 0x02, "Reserved" }, 168 { 0x01, "Reserved" }, 169 { 0, NULL } 170}; 171 172static const struct tok bfd_v1_flag_values[] = { 173 { 0x20, "Poll" }, 174 { 0x10, "Final" }, 175 { 0x08, "Control Plane Independent" }, 176 { BFD_FLAG_AUTH, "Authentication Present" }, 177 { 0x02, "Demand" }, 178 { 0x01, "Multipoint" }, 179 { 0, NULL } 180}; 181 182static const struct tok bfd_v1_state_values[] = { 183 { 0, "AdminDown" }, 184 { 1, "Down" }, 185 { 2, "Init" }, 186 { 3, "Up" }, 187 { 0, NULL } 188}; 189 190static void 191auth_print(netdissect_options *ndo, const u_char *pptr) 192{ 193 const struct bfd_auth_header_t *bfd_auth_header; 194 uint8_t auth_type, auth_len; 195 int i; 196 197 pptr += sizeof (struct bfd_header_t); 198 bfd_auth_header = (const struct bfd_auth_header_t *)pptr; 199 ND_TCHECK_SIZE(bfd_auth_header); 200 auth_type = GET_U_1(bfd_auth_header->auth_type); 201 auth_len = GET_U_1(bfd_auth_header->auth_len); 202 ND_PRINT("\n\tAuthentication: %s (%u), length: %u", 203 tok2str(bfd_v1_authentication_values,"Unknown",auth_type), 204 auth_type, auth_len); 205 pptr += 2; 206 ND_PRINT("\n\t Auth Key ID: %u", GET_U_1(pptr)); 207 208 switch(auth_type) { 209 case AUTH_PASSWORD: 210/* 211 * Simple Password Authentication Section Format 212 * 213 * 0 1 2 3 214 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 215 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 216 * | Auth Type | Auth Len | Auth Key ID | Password... | 217 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 218 * | ... | 219 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 220 */ 221 if (auth_len < AUTH_PASSWORD_FIELD_MIN_LEN || 222 auth_len > AUTH_PASSWORD_FIELD_MAX_LEN) { 223 ND_PRINT("[invalid length %u]", 224 auth_len); 225 break; 226 } 227 pptr++; 228 ND_PRINT(", Password: "); 229 /* the length is equal to the password length plus three */ 230 (void)nd_printn(ndo, pptr, auth_len - 3, NULL); 231 break; 232 case AUTH_MD5: 233 case AUTH_MET_MD5: 234/* 235 * Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format 236 * 237 * 0 1 2 3 238 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 239 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 240 * | Auth Type | Auth Len | Auth Key ID | Reserved | 241 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 242 * | Sequence Number | 243 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 244 * | Auth Key/Digest... | 245 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 246 * | ... | 247 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 248 */ 249 if (auth_len != AUTH_MD5_FIELD_LEN) { 250 ND_PRINT("[invalid length %u]", 251 auth_len); 252 break; 253 } 254 pptr += 2; 255 ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr)); 256 pptr += 4; 257 ND_TCHECK_LEN(pptr, AUTH_MD5_HASH_LEN); 258 ND_PRINT("\n\t Digest: "); 259 for(i = 0; i < AUTH_MD5_HASH_LEN; i++) 260 ND_PRINT("%02x", GET_U_1(pptr + i)); 261 break; 262 case AUTH_SHA1: 263 case AUTH_MET_SHA1: 264/* 265 * Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format 266 * 267 * 0 1 2 3 268 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 269 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 270 * | Auth Type | Auth Len | Auth Key ID | Reserved | 271 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 272 * | Sequence Number | 273 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 274 * | Auth Key/Hash... | 275 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 276 * | ... | 277 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 278 */ 279 if (auth_len != AUTH_SHA1_FIELD_LEN) { 280 ND_PRINT("[invalid length %u]", 281 auth_len); 282 break; 283 } 284 pptr += 2; 285 ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr)); 286 pptr += 4; 287 ND_TCHECK_LEN(pptr, AUTH_SHA1_HASH_LEN); 288 ND_PRINT("\n\t Hash: "); 289 for(i = 0; i < AUTH_SHA1_HASH_LEN; i++) 290 ND_PRINT("%02x", GET_U_1(pptr + i)); 291 break; 292 } 293} 294 295void 296bfd_print(netdissect_options *ndo, const u_char *pptr, 297 u_int len, u_int port) 298{ 299 ndo->ndo_protocol = "bfd"; 300 if (port == BFD_CONTROL_PORT || 301 port == BFD_MULTIHOP_PORT || 302 port == BFD_LAG_PORT) { 303 /* 304 * Control packet. 305 */ 306 const struct bfd_header_t *bfd_header; 307 uint8_t version_diag; 308 uint8_t version = 0; 309 uint8_t flags; 310 311 bfd_header = (const struct bfd_header_t *)pptr; 312 ND_TCHECK_SIZE(bfd_header); 313 version_diag = GET_U_1(bfd_header->version_diag); 314 version = BFD_EXTRACT_VERSION(version_diag); 315 flags = GET_U_1(bfd_header->flags); 316 317 switch (version) { 318 319 /* BFDv0 */ 320 case 0: 321 if (ndo->ndo_vflag < 1) 322 { 323 ND_PRINT("BFDv0, Control, Flags: [%s], length: %u", 324 bittok2str(bfd_v0_flag_values, "none", flags), 325 len); 326 return; 327 } 328 329 ND_PRINT("BFDv0, length: %u\n\tControl, Flags: [%s], Diagnostic: %s (0x%02x)", 330 len, 331 bittok2str(bfd_v0_flag_values, "none", flags), 332 tok2str(bfd_diag_values,"unknown",BFD_EXTRACT_DIAG(version_diag)), 333 BFD_EXTRACT_DIAG(version_diag)); 334 335 ND_PRINT("\n\tDetection Timer Multiplier: %u (%u ms Detection time), BFD Length: %u", 336 GET_U_1(bfd_header->detect_time_multiplier), 337 GET_U_1(bfd_header->detect_time_multiplier) * GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000, 338 GET_U_1(bfd_header->length)); 339 340 341 ND_PRINT("\n\tMy Discriminator: 0x%08x", 342 GET_BE_U_4(bfd_header->my_discriminator)); 343 ND_PRINT(", Your Discriminator: 0x%08x", 344 GET_BE_U_4(bfd_header->your_discriminator)); 345 ND_PRINT("\n\t Desired min Tx Interval: %4u ms", 346 GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000); 347 ND_PRINT("\n\t Required min Rx Interval: %4u ms", 348 GET_BE_U_4(bfd_header->required_min_rx_interval)/1000); 349 ND_PRINT("\n\t Required min Echo Interval: %4u ms", 350 GET_BE_U_4(bfd_header->required_min_echo_interval)/1000); 351 352 if (flags & BFD_FLAG_AUTH) { 353 auth_print(ndo, pptr); 354 } 355 break; 356 357 /* BFDv1 */ 358 case 1: 359 if (ndo->ndo_vflag < 1) 360 { 361 ND_PRINT("BFDv1, %s, State %s, Flags: [%s], length: %u", 362 tok2str(bfd_port_values, "unknown (%u)", port), 363 tok2str(bfd_v1_state_values, "unknown (%u)", (flags & 0xc0) >> 6), 364 bittok2str(bfd_v1_flag_values, "none", flags & 0x3f), 365 len); 366 return; 367 } 368 369 ND_PRINT("BFDv1, length: %u\n\t%s, State %s, Flags: [%s], Diagnostic: %s (0x%02x)", 370 len, 371 tok2str(bfd_port_values, "unknown (%u)", port), 372 tok2str(bfd_v1_state_values, "unknown (%u)", (flags & 0xc0) >> 6), 373 bittok2str(bfd_v1_flag_values, "none", flags & 0x3f), 374 tok2str(bfd_diag_values,"unknown",BFD_EXTRACT_DIAG(version_diag)), 375 BFD_EXTRACT_DIAG(version_diag)); 376 377 ND_PRINT("\n\tDetection Timer Multiplier: %u (%u ms Detection time), BFD Length: %u", 378 GET_U_1(bfd_header->detect_time_multiplier), 379 GET_U_1(bfd_header->detect_time_multiplier) * GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000, 380 GET_U_1(bfd_header->length)); 381 382 383 ND_PRINT("\n\tMy Discriminator: 0x%08x", 384 GET_BE_U_4(bfd_header->my_discriminator)); 385 ND_PRINT(", Your Discriminator: 0x%08x", 386 GET_BE_U_4(bfd_header->your_discriminator)); 387 ND_PRINT("\n\t Desired min Tx Interval: %4u ms", 388 GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000); 389 ND_PRINT("\n\t Required min Rx Interval: %4u ms", 390 GET_BE_U_4(bfd_header->required_min_rx_interval)/1000); 391 ND_PRINT("\n\t Required min Echo Interval: %4u ms", 392 GET_BE_U_4(bfd_header->required_min_echo_interval)/1000); 393 394 if (flags & BFD_FLAG_AUTH) { 395 auth_print(ndo, pptr); 396 } 397 break; 398 399 default: 400 ND_PRINT("BFDv%u, Control, length: %u", 401 version, 402 len); 403 if (ndo->ndo_vflag >= 1) { 404 if(!print_unknown_data(ndo, pptr,"\n\t",len)) 405 return; 406 } 407 break; 408 } 409 } else if (port == BFD_ECHO_PORT) { 410 /* 411 * Echo packet. 412 */ 413 ND_PRINT("BFD, Echo, length: %u", 414 len); 415 if (ndo->ndo_vflag >= 1) { 416 if(!print_unknown_data(ndo, pptr,"\n\t",len)) 417 return; 418 } 419 } else { 420 /* 421 * Unknown packet type. 422 */ 423 ND_PRINT("BFD, unknown (%u), length: %u", 424 port, 425 len); 426 if (ndo->ndo_vflag >= 1) { 427 if(!print_unknown_data(ndo, pptr,"\n\t",len)) 428 return; 429 } 430 } 431} 432