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