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