Localize the timeout variable within the W message macro.
[vpp.git] / test / bfd.py
1 from random import randint
2 from socket import AF_INET, AF_INET6
3 from scapy.all import *
4 from scapy.packet import *
5 from scapy.fields import *
6 from framework import *
7 from vpp_object import *
8 from util import NumericConstant
9
10
11 class BFDDiagCode(NumericConstant):
12     """ BFD Diagnostic Code """
13     no_diagnostic = 0
14     control_detection_time_expired = 1
15     echo_function_failed = 2
16     neighbor_signaled_session_down = 3
17     forwarding_plane_reset = 4
18     path_down = 5
19     concatenated_path_down = 6
20     administratively_down = 7
21     reverse_concatenated_path_down = 8
22
23     desc_dict = {
24         no_diagnostic: "No diagnostic",
25         control_detection_time_expired: "Control Detection Time Expired",
26         echo_function_failed: "Echo Function Failed",
27         neighbor_signaled_session_down: "Neighbor Signaled Session Down",
28         forwarding_plane_reset: "Forwarding Plane Reset",
29         path_down: "Path Down",
30         concatenated_path_down: "Concatenated Path Down",
31         administratively_down: "Administratively Down",
32         reverse_concatenated_path_down: "Reverse Concatenated Path Down",
33     }
34
35     def __init__(self, value):
36         NumericConstant.__init__(self, value)
37
38
39 class BFDState(NumericConstant):
40     """ BFD State """
41     admin_down = 0
42     down = 1
43     init = 2
44     up = 3
45
46     desc_dict = {
47         admin_down: "AdminDown",
48         down: "Down",
49         init: "Init",
50         up: "Up",
51     }
52
53     def __init__(self, value):
54         NumericConstant.__init__(self, value)
55
56
57 class BFDAuthType(NumericConstant):
58     """ BFD Authentication Type """
59     no_auth = 0
60     simple_pwd = 1
61     keyed_md5 = 2
62     meticulous_keyed_md5 = 3
63     keyed_sha1 = 4
64     meticulous_keyed_sha1 = 5
65
66     desc_dict = {
67         no_auth: "No authentication",
68         simple_pwd: "Simple Password",
69         keyed_md5: "Keyed MD5",
70         meticulous_keyed_md5: "Meticulous Keyed MD5",
71         keyed_sha1: "Keyed SHA1",
72         meticulous_keyed_sha1: "Meticulous Keyed SHA1",
73     }
74
75     def __init__(self, value):
76         NumericConstant.__init__(self, value)
77
78
79 def bfd_is_auth_used(pkt):
80     return "A" in pkt.sprintf("%BFD.flags%")
81
82
83 def bfd_is_simple_pwd_used(pkt):
84     return bfd_is_auth_used(pkt) and pkt.auth_type == BFDAuthType.simple_pwd
85
86
87 def bfd_is_sha1_used(pkt):
88     return bfd_is_auth_used(pkt) and pkt.auth_type in \
89         (BFDAuthType.keyed_sha1, BFDAuthType.meticulous_keyed_sha1)
90
91
92 def bfd_is_md5_used(pkt):
93     return bfd_is_auth_used(pkt) and pkt.auth_type in \
94         (BFDAuthType.keyed_md5, BFDAuthType.meticulous_keyed_md5)
95
96
97 def bfd_is_md5_or_sha1_used(pkt):
98     return bfd_is_md5_used(pkt) or bfd_is_sha1_used(pkt)
99
100
101 class BFD(Packet):
102
103     udp_dport = 3784  #: BFD destination port per RFC 5881
104     udp_sport_min = 49152  #: BFD source port min value per RFC 5881
105     udp_sport_max = 65535  #: BFD source port max value per RFC 5881
106     bfd_pkt_len = 24  # : length of BFD pkt without authentication section
107     sha1_auth_len = 28  # : length of authentication section if SHA1 used
108
109     name = "BFD"
110
111     fields_desc = [
112         BitField("version", 1, 3),
113         BitEnumField("diag", 0, 5, BFDDiagCode.desc_dict),
114         BitEnumField("state", 0, 2, BFDState.desc_dict),
115         FlagsField("flags", 0, 6, ['M', 'D', 'A', 'C', 'F', 'P']),
116         XByteField("detect_mult", 0),
117         BitField("length", bfd_pkt_len, 8),
118         BitField("my_discriminator", 0, 32),
119         BitField("your_discriminator", 0, 32),
120         BitField("desired_min_tx_interval", 0, 32),
121         BitField("required_min_rx_interval", 0, 32),
122         BitField("required_min_echo_rx_interval", 0, 32),
123         ConditionalField(
124             BitEnumField("auth_type", 0, 8, BFDAuthType.desc_dict),
125             bfd_is_auth_used),
126         ConditionalField(BitField("auth_len", 0, 8), bfd_is_auth_used),
127         ConditionalField(BitField("auth_key_id", 0, 8), bfd_is_auth_used),
128         ConditionalField(BitField("auth_reserved", 0, 8),
129                          bfd_is_md5_or_sha1_used),
130         ConditionalField(
131             BitField("auth_seq_num", 0, 32), bfd_is_md5_or_sha1_used),
132         ConditionalField(StrField("auth_key_hash", "0" * 16), bfd_is_md5_used),
133         ConditionalField(
134             StrField("auth_key_hash", "0" * 20), bfd_is_sha1_used),
135     ]
136
137     def mysummary(self):
138         return self.sprintf("BFD(my_disc=%BFD.my_discriminator%,"
139                             "your_disc=%BFD.your_discriminator%)")
140
141 # glue the BFD packet class to scapy parser
142 bind_layers(UDP, BFD, dport=BFD.udp_dport)
143
144
145 class VppBFDAuthKey(VppObject):
146     """ Represents BFD authentication key in VPP """
147
148     def __init__(self, test, conf_key_id, auth_type, key):
149         self._test = test
150         self._key = key
151         self._auth_type = auth_type
152         test.assertIn(auth_type, BFDAuthType.desc_dict)
153         self._conf_key_id = conf_key_id
154
155     @property
156     def test(self):
157         """ Test which created this key """
158         return self._test
159
160     @property
161     def auth_type(self):
162         """ Authentication type for this key """
163         return self._auth_type
164
165     @property
166     def key(self):
167         return self._key
168
169     @property
170     def conf_key_id(self):
171         return self._conf_key_id
172
173     def add_vpp_config(self):
174         self.test.vapi.bfd_auth_set_key(
175             self._conf_key_id, self._auth_type, self._key)
176         self._test.registry.register(self, self.test.logger)
177
178     def get_bfd_auth_keys_dump_entry(self):
179         """ get the entry in the auth keys dump corresponding to this key """
180         result = self.test.vapi.bfd_auth_keys_dump()
181         for k in result:
182             if k.conf_key_id == self._conf_key_id:
183                 return k
184         return None
185
186     def query_vpp_config(self):
187         return self.get_bfd_auth_keys_dump_entry() is not None
188
189     def remove_vpp_config(self):
190         self.test.vapi.bfd_auth_del_key(self._conf_key_id)
191
192     def object_id(self):
193         return "bfd-auth-key-%s" % self._conf_key_id
194
195     def __str__(self):
196         return self.object_id()
197
198
199 class VppBFDUDPSession(VppObject):
200     """ Represents BFD UDP session in VPP """
201
202     def __init__(self, test, interface, peer_addr, local_addr=None, af=AF_INET,
203                  desired_min_tx=100000, required_min_rx=100000, detect_mult=3,
204                  sha1_key=None, bfd_key_id=None):
205         self._test = test
206         self._interface = interface
207         self._af = af
208         self._local_addr = local_addr
209         self._peer_addr = peer_addr
210         self._peer_addr_n = socket.inet_pton(af, peer_addr)
211         self._desired_min_tx = desired_min_tx
212         self._required_min_rx = required_min_rx
213         self._detect_mult = detect_mult
214         self._sha1_key = sha1_key
215         self._bfd_key_id = bfd_key_id if bfd_key_id else randint(0, 255)
216
217     @property
218     def test(self):
219         """ Test which created this session """
220         return self._test
221
222     @property
223     def interface(self):
224         """ Interface on which this session lives """
225         return self._interface
226
227     @property
228     def af(self):
229         """ Address family - AF_INET or AF_INET6 """
230         return self._af
231
232     @property
233     def local_addr(self):
234         """ BFD session local address (VPP address) """
235         if self._local_addr is None:
236             if self.af == AF_INET:
237                 return self._interface.local_ip4
238             elif self.af == AF_INET6:
239                 return self._interface.local_ip6
240             else:
241                 raise Exception("Unexpected af %s' % af" % self.af)
242         return self._local_addr
243
244     @property
245     def local_addr_n(self):
246         """ BFD session local address (VPP address) - raw, suitable for API """
247         if self._local_addr is None:
248             if self.af == AF_INET:
249                 return self._interface.local_ip4n
250             elif self.af == AF_INET6:
251                 return self._interface.local_ip6n
252             else:
253                 raise Exception("Unexpected af %s' % af" % self.af)
254         return self._local_addr_n
255
256     @property
257     def peer_addr(self):
258         """ BFD session peer address """
259         return self._peer_addr
260
261     @property
262     def peer_addr_n(self):
263         """ BFD session peer address - raw, suitable for API """
264         return self._peer_addr_n
265
266     def get_bfd_udp_session_dump_entry(self):
267         result = self.test.vapi.bfd_udp_session_dump()
268         for s in result:
269             self.test.logger.debug("session entry: %s" % str(s))
270             if s.sw_if_index == self.interface.sw_if_index:
271                 if self.af == AF_INET \
272                         and s.is_ipv6 == 0 \
273                         and self.interface.local_ip4n == s.local_addr[:4] \
274                         and self.interface.remote_ip4n == s.peer_addr[:4]:
275                     return s
276                 if self.af == AF_INET6 \
277                         and s.is_ipv6 == 1 \
278                         and self.interface.local_ip6n == s.local_addr \
279                         and self.interface.remote_ip6n == s.peer_addr:
280                     return s
281         return None
282
283     @property
284     def state(self):
285         """ BFD session state """
286         session = self.get_bfd_udp_session_dump_entry()
287         if session is None:
288             raise Exception("Could not find BFD session in VPP response: %s" %
289                             repr(result))
290         return session.state
291
292     @property
293     def desired_min_tx(self):
294         return self._desired_min_tx
295
296     @property
297     def required_min_rx(self):
298         return self._required_min_rx
299
300     @property
301     def detect_mult(self):
302         return self._detect_mult
303
304     @property
305     def sha1_key(self):
306         return self._sha1_key
307
308     @property
309     def bfd_key_id(self):
310         return self._bfd_key_id
311
312     def activate_auth(self, key, bfd_key_id=None, delayed=False):
313         self._bfd_key_id = bfd_key_id if bfd_key_id else randint(0, 255)
314         self._sha1_key = key
315         is_ipv6 = 1 if AF_INET6 == self.af else 0
316         conf_key_id = self._sha1_key.conf_key_id
317         is_delayed = 1 if delayed else 0
318         self.test.vapi.bfd_udp_auth_activate(self._interface.sw_if_index,
319                                              self.local_addr_n,
320                                              self.peer_addr_n,
321                                              is_ipv6=is_ipv6,
322                                              bfd_key_id=self._bfd_key_id,
323                                              conf_key_id=conf_key_id,
324                                              is_delayed=is_delayed)
325
326     def deactivate_auth(self, delayed=False):
327         self._bfd_key_id = None
328         self._sha1_key = None
329         is_delayed = 1 if delayed else 0
330         is_ipv6 = 1 if AF_INET6 == self.af else 0
331         self.test.vapi.bfd_udp_auth_deactivate(self._interface.sw_if_index,
332                                                self.local_addr_n,
333                                                self.peer_addr_n,
334                                                is_ipv6=is_ipv6,
335                                                is_delayed=is_delayed)
336
337     def add_vpp_config(self):
338         is_ipv6 = 1 if AF_INET6 == self.af else 0
339         bfd_key_id = self._bfd_key_id if self._sha1_key else None
340         conf_key_id = self._sha1_key.conf_key_id if self._sha1_key else None
341         self.test.vapi.bfd_udp_add(self._interface.sw_if_index,
342                                    self.desired_min_tx,
343                                    self.required_min_rx,
344                                    self.detect_mult,
345                                    self.local_addr_n,
346                                    self.peer_addr_n,
347                                    is_ipv6=is_ipv6,
348                                    bfd_key_id=bfd_key_id,
349                                    conf_key_id=conf_key_id)
350         self._test.registry.register(self, self.test.logger)
351
352     def query_vpp_config(self):
353         session = self.get_bfd_udp_session_dump_entry()
354         return session is not None
355
356     def remove_vpp_config(self):
357         is_ipv6 = 1 if AF_INET6 == self._af else 0
358         self.test.vapi.bfd_udp_del(self._interface.sw_if_index,
359                                    self.local_addr_n,
360                                    self.peer_addr_n,
361                                    is_ipv6=is_ipv6)
362
363     def object_id(self):
364         return "bfd-udp-%s-%s-%s-%s" % (self._interface.sw_if_index,
365                                         self.local_addr,
366                                         self.peer_addr,
367                                         self.af)
368
369     def __str__(self):
370         return self.object_id()
371
372     def admin_up(self):
373         is_ipv6 = 1 if AF_INET6 == self._af else 0
374         self.test.vapi.bfd_udp_session_set_flags(1,
375                                                  self._interface.sw_if_index,
376                                                  self.local_addr_n,
377                                                  self.peer_addr_n,
378                                                  is_ipv6=is_ipv6)