• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Parag Bhide
5
6"""
7BFD - Bidirectional Forwarding Detection - RFC 5880, 5881, 7130, 7881
8"""
9
10# scapy.contrib.description = BFD
11# scapy.contrib.status = loads
12
13
14from scapy.packet import Packet, bind_layers, bind_bottom_up
15from scapy.fields import (
16    BitField,
17    BitEnumField,
18    ByteEnumField,
19    XNBytesField,
20    XByteField,
21    MultipleTypeField,
22    IntField,
23    FieldLenField,
24    FlagsField,
25    ByteField,
26    PacketField,
27    ConditionalField,
28    StrFixedLenField,
29)
30from scapy.layers.inet import UDP
31
32_sta_names = {
33    0: "AdminDown",
34    1: "Down",
35    2: "Init",
36    3: "Up",
37}
38
39
40# https://www.iana.org/assignments/bfd-parameters/bfd-parameters.xhtml
41_diagnostics = {
42    0: "No Diagnostic",
43    1: "Control Detection Time Expired",
44    2: "Echo Function Failed",
45    3: "Neighbor Signaled Session Down",
46    4: "Forwarding Plane Reset",
47    5: "Path Down",
48    6: "Concatenated Path Down",
49    7: "Administratively Down",
50    8: "Reverse Concatenated Path Down",
51    9: "Mis-Connectivity Defect",
52}
53
54
55# https://www.rfc-editor.org/rfc/rfc5880 [Page 10]
56_authentification_type = {
57    0: "Reserved",
58    1: "Simple Password",
59    2: "Keyed MD5",
60    3: "Meticulous Keyed MD5",
61    4: "Keyed SHA1",
62    5: "Meticulous Keyed SHA1",
63}
64
65
66class OptionalAuth(Packet):
67    name = "Optional Auth"
68    fields_desc = [
69        ByteEnumField("auth_type", 1, _authentification_type),
70        FieldLenField(
71            "auth_len",
72            None,
73            fmt="B",
74            length_of="auth_key",
75            adjust=lambda pkt, x: x + 3 if pkt.auth_type <= 1 else x + 8,
76        ),
77        ByteField("auth_keyid", 1),
78        ConditionalField(
79            XByteField("reserved", 0),
80            lambda pkt: pkt.auth_type > 1,
81        ),
82        ConditionalField(
83            IntField("sequence_number", 0),
84            lambda pkt: pkt.auth_type > 1,
85        ),
86        MultipleTypeField(
87            [
88                (
89                    StrFixedLenField(
90                        "auth_key", "", length_from=lambda pkt: pkt.auth_len
91                    ),
92                    lambda pkt: pkt.auth_type == 0,
93                ),
94                (
95                    XNBytesField("auth_key", 0x5F4DCC3B5AA765D61D8327DEB882CF99, 16),
96                    lambda pkt: pkt.auth_type == 2 or pkt.auth_type == 3,
97                ),
98                (
99                    XNBytesField(
100                        "auth_key", 0x5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8, 20
101                    ),
102                    lambda pkt: pkt.auth_type == 4 or pkt.auth_type == 5,
103                ),
104            ],
105            StrFixedLenField(
106                "auth_key", "password", length_from=lambda pkt: pkt.auth_len
107            ),
108        ),
109    ]
110
111
112class BFD(Packet):
113    name = "BFD"
114    fields_desc = [
115        BitField("version", 1, 3),
116        BitEnumField("diag", 0, 5, _diagnostics),
117        BitEnumField("sta", 3, 2, _sta_names),
118        FlagsField("flags", 0, 6, "MDACFP"),
119        ByteField("detect_mult", 3),
120        FieldLenField(
121            "len",
122            None,
123            fmt="B",
124            length_of="optional_auth",
125            adjust=lambda pkt, x: x + 24,
126        ),
127        BitField("my_discriminator", 0x11111111, 32),
128        BitField("your_discriminator", 0x22222222, 32),
129        BitField("min_tx_interval", 1000000000, 32),
130        BitField("min_rx_interval", 1000000000, 32),
131        BitField("echo_rx_interval", 1000000000, 32),
132        ConditionalField(
133            PacketField("optional_auth", None, OptionalAuth),
134            lambda pkt: pkt.flags.names[2] == "A",
135        ),
136    ]
137
138    def mysummary(self):
139        return self.sprintf(
140            "BFD (my_disc=%BFD.my_discriminator%,"
141            "your_disc=%BFD.your_discriminator%,"
142            "state=%BFD.sta%)"
143        )
144
145
146for _bfd_port in [
147    3784,  # single-hop BFD
148    4784,  # multi-hop BFD
149    6784,  # BFD for LAG a.k.a micro-BFD
150    7784,  # seamless BFD
151]:
152    bind_bottom_up(UDP, BFD, dport=_bfd_port)
153    bind_bottom_up(UDP, BFD, sport=_bfd_port)
154    bind_layers(UDP, BFD, dport=_bfd_port, sport=_bfd_port)
155