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