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