Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 1 | from socket import AF_INET, AF_INET6 |
| 2 | from scapy.all import * |
| 3 | from scapy.packet import * |
| 4 | from scapy.fields import * |
| 5 | from framework import * |
| 6 | from vpp_object import * |
| 7 | from util import NumericConstant |
| 8 | |
| 9 | |
| 10 | class BFDDiagCode(NumericConstant): |
| 11 | """ BFD Diagnostic Code """ |
| 12 | no_diagnostic = 0 |
| 13 | control_detection_time_expired = 1 |
| 14 | echo_function_failed = 2 |
| 15 | neighbor_signaled_session_down = 3 |
| 16 | forwarding_plane_reset = 4 |
| 17 | path_down = 5 |
| 18 | concatenated_path_down = 6 |
| 19 | administratively_down = 7 |
| 20 | reverse_concatenated_path_down = 8 |
| 21 | |
| 22 | desc_dict = { |
| 23 | no_diagnostic: "No diagnostic", |
| 24 | control_detection_time_expired: "Control Detection Time Expired", |
| 25 | echo_function_failed: "Echo Function Failed", |
| 26 | neighbor_signaled_session_down: "Neighbor Signaled Session Down", |
| 27 | forwarding_plane_reset: "Forwarding Plane Reset", |
| 28 | path_down: "Path Down", |
| 29 | concatenated_path_down: "Concatenated Path Down", |
| 30 | administratively_down: "Administratively Down", |
| 31 | reverse_concatenated_path_down: "Reverse Concatenated Path Down", |
| 32 | } |
| 33 | |
| 34 | def __init__(self, value): |
| 35 | NumericConstant.__init__(self, value) |
| 36 | |
| 37 | |
| 38 | class BFDState(NumericConstant): |
| 39 | """ BFD State """ |
| 40 | admin_down = 0 |
| 41 | down = 1 |
| 42 | init = 2 |
| 43 | up = 3 |
| 44 | |
| 45 | desc_dict = { |
| 46 | admin_down: "AdminDown", |
| 47 | down: "Down", |
| 48 | init: "Init", |
| 49 | up: "Up", |
| 50 | } |
| 51 | |
| 52 | def __init__(self, value): |
| 53 | NumericConstant.__init__(self, value) |
| 54 | |
| 55 | |
| 56 | class BFD(Packet): |
| 57 | |
| 58 | udp_dport = 3784 #: BFD destination port per RFC 5881 |
| 59 | udp_sport_min = 49152 #: BFD source port min value per RFC 5881 |
| 60 | udp_sport_max = 65535 #: BFD source port max value per RFC 5881 |
| 61 | |
| 62 | name = "BFD" |
| 63 | |
| 64 | fields_desc = [ |
| 65 | BitField("version", 1, 3), |
| 66 | BitEnumField("diag", 0, 5, BFDDiagCode.desc_dict), |
| 67 | BitEnumField("state", 0, 2, BFDState.desc_dict), |
| 68 | FlagsField("flags", 0, 6, ['P', 'F', 'C', 'A', 'D', 'M']), |
| 69 | XByteField("detect_mult", 0), |
| 70 | XByteField("length", 24), |
| 71 | BitField("my_discriminator", 0, 32), |
| 72 | BitField("your_discriminator", 0, 32), |
| 73 | BitField("desired_min_tx_interval", 0, 32), |
| 74 | BitField("required_min_rx_interval", 0, 32), |
| 75 | BitField("required_min_echo_rx_interval", 0, 32)] |
| 76 | |
| 77 | def mysummary(self): |
| 78 | return self.sprintf("BFD(my_disc=%BFD.my_discriminator%," |
| 79 | "your_disc=%BFD.your_discriminator%)") |
| 80 | |
| 81 | # glue the BFD packet class to scapy parser |
| 82 | bind_layers(UDP, BFD, dport=BFD.udp_dport) |
| 83 | |
| 84 | |
| 85 | class VppBFDUDPSession(VppObject): |
| 86 | """ Represents BFD UDP session in VPP """ |
| 87 | |
| 88 | @property |
| 89 | def test(self): |
| 90 | """ Test which created this session """ |
| 91 | return self._test |
| 92 | |
| 93 | @property |
| 94 | def interface(self): |
| 95 | """ Interface on which this session lives """ |
| 96 | return self._interface |
| 97 | |
| 98 | @property |
| 99 | def af(self): |
| 100 | """ Address family - AF_INET or AF_INET6 """ |
| 101 | return self._af |
| 102 | |
| 103 | @property |
| 104 | def bs_index(self): |
| 105 | """ BFD session index from VPP """ |
| 106 | if self._bs_index is not None: |
| 107 | return self._bs_index |
| 108 | raise NotConfiguredException("not configured") |
| 109 | |
| 110 | @property |
| 111 | def local_addr(self): |
| 112 | """ BFD session local address (VPP address) """ |
| 113 | if self._local_addr is None: |
Klement Sekera | 46a87ad | 2017-01-02 08:22:23 +0100 | [diff] [blame] | 114 | if self.af == AF_INET: |
| 115 | return self._interface.local_ip4 |
| 116 | elif self.af == AF_INET6: |
| 117 | return self._interface.local_ip6 |
| 118 | else: |
| 119 | raise Exception("Unexpected af %s' % af" % self.af) |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 120 | return self._local_addr |
| 121 | |
| 122 | @property |
| 123 | def local_addr_n(self): |
| 124 | """ BFD session local address (VPP address) - raw, suitable for API """ |
| 125 | if self._local_addr is None: |
Klement Sekera | 46a87ad | 2017-01-02 08:22:23 +0100 | [diff] [blame] | 126 | if self.af == AF_INET: |
| 127 | return self._interface.local_ip4n |
| 128 | elif self.af == AF_INET6: |
| 129 | return self._interface.local_ip6n |
| 130 | else: |
| 131 | raise Exception("Unexpected af %s' % af" % self.af) |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 132 | return self._local_addr_n |
| 133 | |
| 134 | @property |
| 135 | def peer_addr(self): |
| 136 | """ BFD session peer address """ |
| 137 | return self._peer_addr |
| 138 | |
| 139 | @property |
| 140 | def peer_addr_n(self): |
| 141 | """ BFD session peer address - raw, suitable for API """ |
| 142 | return self._peer_addr_n |
| 143 | |
| 144 | @property |
| 145 | def state(self): |
| 146 | """ BFD session state """ |
| 147 | result = self.test.vapi.bfd_udp_session_dump() |
| 148 | session = None |
| 149 | for s in result: |
| 150 | if s.sw_if_index == self.interface.sw_if_index: |
| 151 | if self.af == AF_INET \ |
| 152 | and s.is_ipv6 == 0 \ |
| 153 | and self.interface.local_ip4n == s.local_addr[:4] \ |
| 154 | and self.interface.remote_ip4n == s.peer_addr[:4]: |
| 155 | session = s |
| 156 | break |
| 157 | if session is None: |
Klement Sekera | 10db26f | 2017-01-11 08:16:53 +0100 | [diff] [blame] | 158 | raise Exception("Could not find BFD session in VPP response: %s" % |
| 159 | repr(result)) |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 160 | return session.state |
| 161 | |
| 162 | @property |
| 163 | def desired_min_tx(self): |
| 164 | return self._desired_min_tx |
| 165 | |
| 166 | @property |
| 167 | def required_min_rx(self): |
| 168 | return self._required_min_rx |
| 169 | |
| 170 | @property |
| 171 | def detect_mult(self): |
| 172 | return self._detect_mult |
| 173 | |
Klement Sekera | e4504c6 | 2016-12-08 10:16:41 +0100 | [diff] [blame] | 174 | def __init__(self, test, interface, peer_addr, local_addr=None, af=AF_INET, |
| 175 | desired_min_tx=100000, required_min_rx=100000, detect_mult=3): |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 176 | self._test = test |
| 177 | self._interface = interface |
| 178 | self._af = af |
| 179 | self._local_addr = local_addr |
| 180 | self._peer_addr = peer_addr |
| 181 | self._peer_addr_n = socket.inet_pton(af, peer_addr) |
| 182 | self._bs_index = None |
Klement Sekera | e4504c6 | 2016-12-08 10:16:41 +0100 | [diff] [blame] | 183 | self._desired_min_tx = desired_min_tx |
| 184 | self._required_min_rx = required_min_rx |
| 185 | self._detect_mult = detect_mult |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 186 | |
| 187 | def add_vpp_config(self): |
| 188 | is_ipv6 = 1 if AF_INET6 == self.af else 0 |
| 189 | result = self.test.vapi.bfd_udp_add( |
| 190 | self._interface.sw_if_index, |
| 191 | self.desired_min_tx, |
| 192 | self.required_min_rx, |
| 193 | self.detect_mult, |
| 194 | self.local_addr_n, |
| 195 | self.peer_addr_n, |
| 196 | is_ipv6=is_ipv6) |
| 197 | self._bs_index = result.bs_index |
Klement Sekera | 10db26f | 2017-01-11 08:16:53 +0100 | [diff] [blame] | 198 | self._test.registry.register(self, self.test.logger) |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 199 | |
| 200 | def query_vpp_config(self): |
| 201 | result = self.test.vapi.bfd_udp_session_dump() |
| 202 | session = None |
| 203 | for s in result: |
| 204 | if s.sw_if_index == self.interface.sw_if_index: |
| 205 | if self.af == AF_INET \ |
| 206 | and s.is_ipv6 == 0 \ |
| 207 | and self.interface.local_ip4n == s.local_addr[:4] \ |
| 208 | and self.interface.remote_ip4n == s.peer_addr[:4]: |
| 209 | session = s |
| 210 | break |
| 211 | if session is None: |
| 212 | return False |
| 213 | return True |
| 214 | |
| 215 | def remove_vpp_config(self): |
Klement Sekera | 10db26f | 2017-01-11 08:16:53 +0100 | [diff] [blame] | 216 | if self._bs_index is not None: |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 217 | is_ipv6 = 1 if AF_INET6 == self._af else 0 |
| 218 | self.test.vapi.bfd_udp_del( |
| 219 | self._interface.sw_if_index, |
| 220 | self.local_addr_n, |
| 221 | self.peer_addr_n, |
| 222 | is_ipv6=is_ipv6) |
| 223 | |
| 224 | def object_id(self): |
| 225 | return "bfd-udp-%d" % self.bs_index |
| 226 | |
Klement Sekera | 10db26f | 2017-01-11 08:16:53 +0100 | [diff] [blame] | 227 | def __str__(self): |
| 228 | return self.object_id() |
| 229 | |
Klement Sekera | 0e3c0de | 2016-09-29 14:43:44 +0200 | [diff] [blame] | 230 | def admin_up(self): |
| 231 | self.test.vapi.bfd_session_set_flags(self.bs_index, 1) |