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
auth_print(netdissect_options * ndo,const u_char * pptr)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
bfd_print(netdissect_options * ndo,const u_char * pptr,u_int len,u_int port)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