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