BFD: echo function
[vpp.git] / test / test_bfd.py
1 #!/usr/bin/env python
2 """ BFD tests """
3
4 from __future__ import division
5 import unittest
6 import hashlib
7 import binascii
8 import time
9 from random import randint, shuffle, getrandbits
10 from socket import AF_INET, AF_INET6
11 from scapy.packet import Raw
12 from scapy.layers.l2 import Ether
13 from scapy.layers.inet import UDP, IP
14 from scapy.layers.inet6 import IPv6
15 from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \
16     BFDDiagCode, BFDState, BFD_vpp_echo
17 from framework import VppTestCase, VppTestRunner
18 from vpp_pg_interface import CaptureTimeoutError
19 from util import ppp
20
21 USEC_IN_SEC = 1000000
22
23
24 class AuthKeyFactory(object):
25     """Factory class for creating auth keys with unique conf key ID"""
26
27     def __init__(self):
28         self._conf_key_ids = {}
29
30     def create_random_key(self, test, auth_type=BFDAuthType.keyed_sha1):
31         """ create a random key with unique conf key id """
32         conf_key_id = randint(0, 0xFFFFFFFF)
33         while conf_key_id in self._conf_key_ids:
34             conf_key_id = randint(0, 0xFFFFFFFF)
35         self._conf_key_ids[conf_key_id] = 1
36         key = str(bytearray([randint(0, 255) for _ in range(randint(1, 20))]))
37         return VppBFDAuthKey(test=test, auth_type=auth_type,
38                              conf_key_id=conf_key_id, key=key)
39
40
41 class BFDAPITestCase(VppTestCase):
42     """Bidirectional Forwarding Detection (BFD) - API"""
43
44     pg0 = None
45     pg1 = None
46
47     @classmethod
48     def setUpClass(cls):
49         super(BFDAPITestCase, cls).setUpClass()
50
51         try:
52             cls.create_pg_interfaces(range(2))
53             for i in cls.pg_interfaces:
54                 i.config_ip4()
55                 i.config_ip6()
56                 i.resolve_arp()
57
58         except Exception:
59             super(BFDAPITestCase, cls).tearDownClass()
60             raise
61
62     def setUp(self):
63         super(BFDAPITestCase, self).setUp()
64         self.factory = AuthKeyFactory()
65
66     def test_add_bfd(self):
67         """ create a BFD session """
68         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
69         session.add_vpp_config()
70         self.logger.debug("Session state is %s", session.state)
71         session.remove_vpp_config()
72         session.add_vpp_config()
73         self.logger.debug("Session state is %s", session.state)
74         session.remove_vpp_config()
75
76     def test_double_add(self):
77         """ create the same BFD session twice (negative case) """
78         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
79         session.add_vpp_config()
80
81         with self.vapi.expect_negative_api_retval():
82             session.add_vpp_config()
83
84         session.remove_vpp_config()
85
86     def test_add_bfd6(self):
87         """ create IPv6 BFD session """
88         session = VppBFDUDPSession(
89             self, self.pg0, self.pg0.remote_ip6, af=AF_INET6)
90         session.add_vpp_config()
91         self.logger.debug("Session state is %s", session.state)
92         session.remove_vpp_config()
93         session.add_vpp_config()
94         self.logger.debug("Session state is %s", session.state)
95         session.remove_vpp_config()
96
97     def test_mod_bfd(self):
98         """ modify BFD session parameters """
99         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
100                                    desired_min_tx=50000,
101                                    required_min_rx=10000,
102                                    detect_mult=1)
103         session.add_vpp_config()
104         s = session.get_bfd_udp_session_dump_entry()
105         self.assert_equal(session.desired_min_tx,
106                           s.desired_min_tx,
107                           "desired min transmit interval")
108         self.assert_equal(session.required_min_rx,
109                           s.required_min_rx,
110                           "required min receive interval")
111         self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
112         session.modify_parameters(desired_min_tx=session.desired_min_tx * 2,
113                                   required_min_rx=session.required_min_rx * 2,
114                                   detect_mult=session.detect_mult * 2)
115         s = session.get_bfd_udp_session_dump_entry()
116         self.assert_equal(session.desired_min_tx,
117                           s.desired_min_tx,
118                           "desired min transmit interval")
119         self.assert_equal(session.required_min_rx,
120                           s.required_min_rx,
121                           "required min receive interval")
122         self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
123
124     def test_add_sha1_keys(self):
125         """ add SHA1 keys """
126         key_count = 10
127         keys = [self.factory.create_random_key(
128             self) for i in range(0, key_count)]
129         for key in keys:
130             self.assertFalse(key.query_vpp_config())
131         for key in keys:
132             key.add_vpp_config()
133         for key in keys:
134             self.assertTrue(key.query_vpp_config())
135         # remove randomly
136         indexes = range(key_count)
137         shuffle(indexes)
138         removed = []
139         for i in indexes:
140             key = keys[i]
141             key.remove_vpp_config()
142             removed.append(i)
143             for j in range(key_count):
144                 key = keys[j]
145                 if j in removed:
146                     self.assertFalse(key.query_vpp_config())
147                 else:
148                     self.assertTrue(key.query_vpp_config())
149         # should be removed now
150         for key in keys:
151             self.assertFalse(key.query_vpp_config())
152         # add back and remove again
153         for key in keys:
154             key.add_vpp_config()
155         for key in keys:
156             self.assertTrue(key.query_vpp_config())
157         for key in keys:
158             key.remove_vpp_config()
159         for key in keys:
160             self.assertFalse(key.query_vpp_config())
161
162     def test_add_bfd_sha1(self):
163         """ create a BFD session (SHA1) """
164         key = self.factory.create_random_key(self)
165         key.add_vpp_config()
166         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
167                                    sha1_key=key)
168         session.add_vpp_config()
169         self.logger.debug("Session state is %s", session.state)
170         session.remove_vpp_config()
171         session.add_vpp_config()
172         self.logger.debug("Session state is %s", session.state)
173         session.remove_vpp_config()
174
175     def test_double_add_sha1(self):
176         """ create the same BFD session twice (negative case) (SHA1) """
177         key = self.factory.create_random_key(self)
178         key.add_vpp_config()
179         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
180                                    sha1_key=key)
181         session.add_vpp_config()
182         with self.assertRaises(Exception):
183             session.add_vpp_config()
184
185     def test_add_auth_nonexistent_key(self):
186         """ create BFD session using non-existent SHA1 (negative case) """
187         session = VppBFDUDPSession(
188             self, self.pg0, self.pg0.remote_ip4,
189             sha1_key=self.factory.create_random_key(self))
190         with self.assertRaises(Exception):
191             session.add_vpp_config()
192
193     def test_shared_sha1_key(self):
194         """ share single SHA1 key between multiple BFD sessions """
195         key = self.factory.create_random_key(self)
196         key.add_vpp_config()
197         sessions = [
198             VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
199                              sha1_key=key),
200             VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip6,
201                              sha1_key=key, af=AF_INET6),
202             VppBFDUDPSession(self, self.pg1, self.pg1.remote_ip4,
203                              sha1_key=key),
204             VppBFDUDPSession(self, self.pg1, self.pg1.remote_ip6,
205                              sha1_key=key, af=AF_INET6)]
206         for s in sessions:
207             s.add_vpp_config()
208         removed = 0
209         for s in sessions:
210             e = key.get_bfd_auth_keys_dump_entry()
211             self.assert_equal(e.use_count, len(sessions) - removed,
212                               "Use count for shared key")
213             s.remove_vpp_config()
214             removed += 1
215         e = key.get_bfd_auth_keys_dump_entry()
216         self.assert_equal(e.use_count, len(sessions) - removed,
217                           "Use count for shared key")
218
219     def test_activate_auth(self):
220         """ activate SHA1 authentication """
221         key = self.factory.create_random_key(self)
222         key.add_vpp_config()
223         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
224         session.add_vpp_config()
225         session.activate_auth(key)
226
227     def test_deactivate_auth(self):
228         """ deactivate SHA1 authentication """
229         key = self.factory.create_random_key(self)
230         key.add_vpp_config()
231         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
232         session.add_vpp_config()
233         session.activate_auth(key)
234         session.deactivate_auth()
235
236     def test_change_key(self):
237         """ change SHA1 key """
238         key1 = self.factory.create_random_key(self)
239         key2 = self.factory.create_random_key(self)
240         while key2.conf_key_id == key1.conf_key_id:
241             key2 = self.factory.create_random_key(self)
242         key1.add_vpp_config()
243         key2.add_vpp_config()
244         session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
245                                    sha1_key=key1)
246         session.add_vpp_config()
247         session.activate_auth(key2)
248
249
250 class BFDTestSession(object):
251     """ BFD session as seen from test framework side """
252
253     def __init__(self, test, interface, af, detect_mult=3, sha1_key=None,
254                  bfd_key_id=None, our_seq_number=None):
255         self.test = test
256         self.af = af
257         self.sha1_key = sha1_key
258         self.bfd_key_id = bfd_key_id
259         self.interface = interface
260         self.udp_sport = randint(49152, 65535)
261         if our_seq_number is None:
262             self.our_seq_number = randint(0, 40000000)
263         else:
264             self.our_seq_number = our_seq_number
265         self.vpp_seq_number = None
266         self.my_discriminator = 0
267         self.desired_min_tx = 100000
268         self.required_min_rx = 100000
269         self.required_min_echo_rx = None
270         self.detect_mult = detect_mult
271         self.diag = BFDDiagCode.no_diagnostic
272         self.your_discriminator = None
273         self.state = BFDState.down
274         self.auth_type = BFDAuthType.no_auth
275
276     def inc_seq_num(self):
277         """ increment sequence number, wrapping if needed """
278         if self.our_seq_number == 0xFFFFFFFF:
279             self.our_seq_number = 0
280         else:
281             self.our_seq_number += 1
282
283     def update(self, my_discriminator=None, your_discriminator=None,
284                desired_min_tx=None, required_min_rx=None,
285                required_min_echo_rx=None, detect_mult=None,
286                diag=None, state=None, auth_type=None):
287         """ update BFD parameters associated with session """
288         if my_discriminator is not None:
289             self.my_discriminator = my_discriminator
290         if your_discriminator is not None:
291             self.your_discriminator = your_discriminator
292         if required_min_rx is not None:
293             self.required_min_rx = required_min_rx
294         if required_min_echo_rx is not None:
295             self.required_min_echo_rx = required_min_echo_rx
296         if desired_min_tx is not None:
297             self.desired_min_tx = desired_min_tx
298         if detect_mult is not None:
299             self.detect_mult = detect_mult
300         if diag is not None:
301             self.diag = diag
302         if state is not None:
303             self.state = state
304         if auth_type is not None:
305             self.auth_type = auth_type
306
307     def fill_packet_fields(self, packet):
308         """ set packet fields with known values in packet """
309         bfd = packet[BFD]
310         if self.my_discriminator:
311             self.test.logger.debug("BFD: setting packet.my_discriminator=%s",
312                                    self.my_discriminator)
313             bfd.my_discriminator = self.my_discriminator
314         if self.your_discriminator:
315             self.test.logger.debug("BFD: setting packet.your_discriminator=%s",
316                                    self.your_discriminator)
317             bfd.your_discriminator = self.your_discriminator
318         if self.required_min_rx:
319             self.test.logger.debug(
320                 "BFD: setting packet.required_min_rx_interval=%s",
321                 self.required_min_rx)
322             bfd.required_min_rx_interval = self.required_min_rx
323         if self.required_min_echo_rx:
324             self.test.logger.debug(
325                 "BFD: setting packet.required_min_echo_rx=%s",
326                 self.required_min_echo_rx)
327             bfd.required_min_echo_rx_interval = self.required_min_echo_rx
328         if self.desired_min_tx:
329             self.test.logger.debug(
330                 "BFD: setting packet.desired_min_tx_interval=%s",
331                 self.desired_min_tx)
332             bfd.desired_min_tx_interval = self.desired_min_tx
333         if self.detect_mult:
334             self.test.logger.debug(
335                 "BFD: setting packet.detect_mult=%s", self.detect_mult)
336             bfd.detect_mult = self.detect_mult
337         if self.diag:
338             self.test.logger.debug("BFD: setting packet.diag=%s", self.diag)
339             bfd.diag = self.diag
340         if self.state:
341             self.test.logger.debug("BFD: setting packet.state=%s", self.state)
342             bfd.state = self.state
343         if self.auth_type:
344             # this is used by a negative test-case
345             self.test.logger.debug("BFD: setting packet.auth_type=%s",
346                                    self.auth_type)
347             bfd.auth_type = self.auth_type
348
349     def create_packet(self):
350         """ create a BFD packet, reflecting the current state of session """
351         if self.sha1_key:
352             bfd = BFD(flags="A")
353             bfd.auth_type = self.sha1_key.auth_type
354             bfd.auth_len = BFD.sha1_auth_len
355             bfd.auth_key_id = self.bfd_key_id
356             bfd.auth_seq_num = self.our_seq_number
357             bfd.length = BFD.sha1_auth_len + BFD.bfd_pkt_len
358         else:
359             bfd = BFD()
360         if self.af == AF_INET6:
361             packet = (Ether(src=self.interface.remote_mac,
362                             dst=self.interface.local_mac) /
363                       IPv6(src=self.interface.remote_ip6,
364                            dst=self.interface.local_ip6,
365                            hlim=255) /
366                       UDP(sport=self.udp_sport, dport=BFD.udp_dport) /
367                       bfd)
368         else:
369             packet = (Ether(src=self.interface.remote_mac,
370                             dst=self.interface.local_mac) /
371                       IP(src=self.interface.remote_ip4,
372                          dst=self.interface.local_ip4,
373                          ttl=255) /
374                       UDP(sport=self.udp_sport, dport=BFD.udp_dport) /
375                       bfd)
376         self.test.logger.debug("BFD: Creating packet")
377         self.fill_packet_fields(packet)
378         if self.sha1_key:
379             hash_material = str(packet[BFD])[:32] + self.sha1_key.key + \
380                 "\0" * (20 - len(self.sha1_key.key))
381             self.test.logger.debug("BFD: Calculated SHA1 hash: %s" %
382                                    hashlib.sha1(hash_material).hexdigest())
383             packet[BFD].auth_key_hash = hashlib.sha1(hash_material).digest()
384         return packet
385
386     def send_packet(self, packet=None, interface=None):
387         """ send packet on interface, creating the packet if needed """
388         if packet is None:
389             packet = self.create_packet()
390         if interface is None:
391             interface = self.test.pg0
392         self.test.logger.debug(ppp("Sending packet:", packet))
393         interface.add_stream(packet)
394         self.test.pg_start()
395
396     def verify_sha1_auth(self, packet):
397         """ Verify correctness of authentication in BFD layer. """
398         bfd = packet[BFD]
399         self.test.assert_equal(bfd.auth_len, 28, "Auth section length")
400         self.test.assert_equal(bfd.auth_type, self.sha1_key.auth_type,
401                                BFDAuthType)
402         self.test.assert_equal(bfd.auth_key_id, self.bfd_key_id, "Key ID")
403         self.test.assert_equal(bfd.auth_reserved, 0, "Reserved")
404         if self.vpp_seq_number is None:
405             self.vpp_seq_number = bfd.auth_seq_num
406             self.test.logger.debug("Received initial sequence number: %s" %
407                                    self.vpp_seq_number)
408         else:
409             recvd_seq_num = bfd.auth_seq_num
410             self.test.logger.debug("Received followup sequence number: %s" %
411                                    recvd_seq_num)
412             if self.vpp_seq_number < 0xffffffff:
413                 if self.sha1_key.auth_type == \
414                         BFDAuthType.meticulous_keyed_sha1:
415                     self.test.assert_equal(recvd_seq_num,
416                                            self.vpp_seq_number + 1,
417                                            "BFD sequence number")
418                 else:
419                     self.test.assert_in_range(recvd_seq_num,
420                                               self.vpp_seq_number,
421                                               self.vpp_seq_number + 1,
422                                               "BFD sequence number")
423             else:
424                 if self.sha1_key.auth_type == \
425                         BFDAuthType.meticulous_keyed_sha1:
426                     self.test.assert_equal(recvd_seq_num, 0,
427                                            "BFD sequence number")
428                 else:
429                     self.test.assertIn(recvd_seq_num, (self.vpp_seq_number, 0),
430                                        "BFD sequence number not one of "
431                                        "(%s, 0)" % self.vpp_seq_number)
432             self.vpp_seq_number = recvd_seq_num
433         # last 20 bytes represent the hash - so replace them with the key,
434         # pad the result with zeros and hash the result
435         hash_material = bfd.original[:-20] + self.sha1_key.key + \
436             "\0" * (20 - len(self.sha1_key.key))
437         expected_hash = hashlib.sha1(hash_material).hexdigest()
438         self.test.assert_equal(binascii.hexlify(bfd.auth_key_hash),
439                                expected_hash, "Auth key hash")
440
441     def verify_bfd(self, packet):
442         """ Verify correctness of BFD layer. """
443         bfd = packet[BFD]
444         self.test.assert_equal(bfd.version, 1, "BFD version")
445         self.test.assert_equal(bfd.your_discriminator,
446                                self.my_discriminator,
447                                "BFD - your discriminator")
448         if self.sha1_key:
449             self.verify_sha1_auth(packet)
450
451
452 def bfd_session_up(test):
453     """ Bring BFD session up """
454     test.logger.info("BFD: Waiting for slow hello")
455     p = wait_for_bfd_packet(test, 2)
456     old_offset = None
457     if hasattr(test, 'vpp_clock_offset'):
458         old_offset = test.vpp_clock_offset
459     test.vpp_clock_offset = time.time() - p.time
460     test.logger.debug("BFD: Calculated vpp clock offset: %s",
461                       test.vpp_clock_offset)
462     if old_offset:
463         test.assertAlmostEqual(
464             old_offset, test.vpp_clock_offset, delta=0.1,
465             msg="vpp clock offset not stable (new: %s, old: %s)" %
466             (test.vpp_clock_offset, old_offset))
467     test.logger.info("BFD: Sending Init")
468     test.test_session.update(my_discriminator=randint(0, 40000000),
469                              your_discriminator=p[BFD].my_discriminator,
470                              state=BFDState.init)
471     test.test_session.send_packet()
472     test.logger.info("BFD: Waiting for event")
473     e = test.vapi.wait_for_event(1, "bfd_udp_session_details")
474     verify_event(test, e, expected_state=BFDState.up)
475     test.logger.info("BFD: Session is Up")
476     test.test_session.update(state=BFDState.up)
477     test.test_session.send_packet()
478     test.assert_equal(test.vpp_session.state, BFDState.up, BFDState)
479
480
481 def bfd_session_down(test):
482     """ Bring BFD session down """
483     test.assert_equal(test.vpp_session.state, BFDState.up, BFDState)
484     test.test_session.update(state=BFDState.down)
485     test.test_session.send_packet()
486     test.logger.info("BFD: Waiting for event")
487     e = test.vapi.wait_for_event(1, "bfd_udp_session_details")
488     verify_event(test, e, expected_state=BFDState.down)
489     test.logger.info("BFD: Session is Down")
490     test.assert_equal(test.vpp_session.state, BFDState.down, BFDState)
491
492
493 def verify_ip(test, packet):
494     """ Verify correctness of IP layer. """
495     if test.vpp_session.af == AF_INET6:
496         ip = packet[IPv6]
497         local_ip = test.pg0.local_ip6
498         remote_ip = test.pg0.remote_ip6
499         test.assert_equal(ip.hlim, 255, "IPv6 hop limit")
500     else:
501         ip = packet[IP]
502         local_ip = test.pg0.local_ip4
503         remote_ip = test.pg0.remote_ip4
504         test.assert_equal(ip.ttl, 255, "IPv4 TTL")
505     test.assert_equal(ip.src, local_ip, "IP source address")
506     test.assert_equal(ip.dst, remote_ip, "IP destination address")
507
508
509 def verify_udp(test, packet):
510     """ Verify correctness of UDP layer. """
511     udp = packet[UDP]
512     test.assert_equal(udp.dport, BFD.udp_dport, "UDP destination port")
513     test.assert_in_range(udp.sport, BFD.udp_sport_min, BFD.udp_sport_max,
514                          "UDP source port")
515
516
517 def verify_event(test, event, expected_state):
518     """ Verify correctness of event values. """
519     e = event
520     test.logger.debug("BFD: Event: %s" % repr(e))
521     test.assert_equal(e.sw_if_index,
522                       test.vpp_session.interface.sw_if_index,
523                       "BFD interface index")
524     is_ipv6 = 0
525     if test.vpp_session.af == AF_INET6:
526         is_ipv6 = 1
527     test.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6")
528     if test.vpp_session.af == AF_INET:
529         test.assert_equal(e.local_addr[:4], test.vpp_session.local_addr_n,
530                           "Local IPv4 address")
531         test.assert_equal(e.peer_addr[:4], test.vpp_session.peer_addr_n,
532                           "Peer IPv4 address")
533     else:
534         test.assert_equal(e.local_addr, test.vpp_session.local_addr_n,
535                           "Local IPv6 address")
536         test.assert_equal(e.peer_addr, test.vpp_session.peer_addr_n,
537                           "Peer IPv6 address")
538     test.assert_equal(e.state, expected_state, BFDState)
539
540
541 def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None):
542     """ wait for BFD packet and verify its correctness
543
544     :param timeout: how long to wait
545     :param pcap_time_min: ignore packets with pcap timestamp lower than this
546
547     :returns: tuple (packet, time spent waiting for packet)
548     """
549     test.logger.info("BFD: Waiting for BFD packet")
550     deadline = time.time() + timeout
551     counter = 0
552     while True:
553         counter += 1
554         # sanity check
555         test.assert_in_range(counter, 0, 100, "number of packets ignored")
556         time_left = deadline - time.time()
557         if time_left < 0:
558             raise CaptureTimeoutError("Packet did not arrive within timeout")
559         p = test.pg0.wait_for_packet(timeout=time_left)
560         test.logger.debug(ppp("BFD: Got packet:", p))
561         if pcap_time_min is not None and p.time < pcap_time_min:
562             test.logger.debug(ppp("BFD: ignoring packet (pcap time %s < "
563                                   "pcap time min %s):" %
564                                   (p.time, pcap_time_min), p))
565         else:
566             break
567     bfd = p[BFD]
568     if bfd is None:
569         raise Exception(ppp("Unexpected or invalid BFD packet:", p))
570     if bfd.payload:
571         raise Exception(ppp("Unexpected payload in BFD packet:", bfd))
572     verify_ip(test, p)
573     verify_udp(test, p)
574     test.test_session.verify_bfd(p)
575     return p
576
577
578 class BFD4TestCase(VppTestCase):
579     """Bidirectional Forwarding Detection (BFD)"""
580
581     pg0 = None
582     vpp_clock_offset = None
583     vpp_session = None
584     test_session = None
585
586     @classmethod
587     def setUpClass(cls):
588         super(BFD4TestCase, cls).setUpClass()
589         try:
590             cls.create_pg_interfaces([0])
591             cls.create_loopback_interfaces([0])
592             cls.loopback0 = cls.lo_interfaces[0]
593             cls.loopback0.config_ip4()
594             cls.loopback0.admin_up()
595             cls.pg0.config_ip4()
596             cls.pg0.configure_ipv4_neighbors()
597             cls.pg0.admin_up()
598             cls.pg0.resolve_arp()
599
600         except Exception:
601             super(BFD4TestCase, cls).tearDownClass()
602             raise
603
604     def setUp(self):
605         super(BFD4TestCase, self).setUp()
606         self.factory = AuthKeyFactory()
607         self.vapi.want_bfd_events()
608         self.pg0.enable_capture()
609         try:
610             self.vpp_session = VppBFDUDPSession(self, self.pg0,
611                                                 self.pg0.remote_ip4)
612             self.vpp_session.add_vpp_config()
613             self.vpp_session.admin_up()
614             self.test_session = BFDTestSession(self, self.pg0, AF_INET)
615         except:
616             self.vapi.want_bfd_events(enable_disable=0)
617             raise
618
619     def tearDown(self):
620         if not self.vpp_dead:
621             self.vapi.want_bfd_events(enable_disable=0)
622         self.vapi.collect_events()  # clear the event queue
623         super(BFD4TestCase, self).tearDown()
624
625     def test_session_up(self):
626         """ bring BFD session up """
627         bfd_session_up(self)
628
629     def test_session_down(self):
630         """ bring BFD session down """
631         bfd_session_up(self)
632         bfd_session_down(self)
633
634     def test_hold_up(self):
635         """ hold BFD session up """
636         bfd_session_up(self)
637         for dummy in range(self.test_session.detect_mult * 2):
638             wait_for_bfd_packet(self)
639             self.test_session.send_packet()
640         self.assert_equal(len(self.vapi.collect_events()), 0,
641                           "number of bfd events")
642
643     def test_slow_timer(self):
644         """ verify slow periodic control frames while session down """
645         packet_count = 3
646         self.logger.info("BFD: Waiting for %d BFD packets", packet_count)
647         prev_packet = wait_for_bfd_packet(self, 2)
648         for dummy in range(packet_count):
649             next_packet = wait_for_bfd_packet(self, 2)
650             time_diff = next_packet.time - prev_packet.time
651             # spec says the range should be <0.75, 1>, allow extra 0.05 margin
652             # to work around timing issues
653             self.assert_in_range(
654                 time_diff, 0.70, 1.05, "time between slow packets")
655             prev_packet = next_packet
656
657     def test_zero_remote_min_rx(self):
658         """ no packets when zero remote required min rx interval """
659         bfd_session_up(self)
660         self.test_session.update(required_min_rx=0)
661         self.test_session.send_packet()
662         for dummy in range(self.test_session.detect_mult):
663             self.sleep(self.vpp_session.required_min_rx / USEC_IN_SEC,
664                        "sleep before transmitting bfd packet")
665             self.test_session.send_packet()
666             try:
667                 p = wait_for_bfd_packet(self, timeout=0)
668                 self.logger.error(ppp("Received unexpected packet:", p))
669             except CaptureTimeoutError:
670                 pass
671         self.assert_equal(
672             len(self.vapi.collect_events()), 0, "number of bfd events")
673         self.test_session.update(required_min_rx=100000)
674         for dummy in range(3):
675             self.test_session.send_packet()
676             wait_for_bfd_packet(
677                 self, timeout=self.test_session.required_min_rx / USEC_IN_SEC)
678         self.assert_equal(
679             len(self.vapi.collect_events()), 0, "number of bfd events")
680
681     def test_conn_down(self):
682         """ verify session goes down after inactivity """
683         bfd_session_up(self)
684         detection_time = self.test_session.detect_mult *\
685             self.vpp_session.required_min_rx / USEC_IN_SEC
686         self.sleep(detection_time, "waiting for BFD session time-out")
687         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
688         verify_event(self, e, expected_state=BFDState.down)
689
690     def test_large_required_min_rx(self):
691         """ large remote required min rx interval """
692         bfd_session_up(self)
693         p = wait_for_bfd_packet(self)
694         interval = 3000000
695         self.test_session.update(required_min_rx=interval)
696         self.test_session.send_packet()
697         time_mark = time.time()
698         count = 0
699         # busy wait here, trying to collect a packet or event, vpp is not
700         # allowed to send packets and the session will timeout first - so the
701         # Up->Down event must arrive before any packets do
702         while time.time() < time_mark + interval / USEC_IN_SEC:
703             try:
704                 p = wait_for_bfd_packet(self, timeout=0)
705                 # if vpp managed to send a packet before we did the session
706                 # session update, then that's fine, ignore it
707                 if p.time < time_mark - self.vpp_clock_offset:
708                     continue
709                 self.logger.error(ppp("Received unexpected packet:", p))
710                 count += 1
711             except CaptureTimeoutError:
712                 pass
713             events = self.vapi.collect_events()
714             if len(events) > 0:
715                 verify_event(self, events[0], BFDState.down)
716                 break
717         self.assert_equal(count, 0, "number of packets received")
718
719     def test_immediate_remote_min_rx_reduction(self):
720         """ immediately honor remote required min rx reduction """
721         self.vpp_session.remove_vpp_config()
722         self.vpp_session = VppBFDUDPSession(
723             self, self.pg0, self.pg0.remote_ip4, desired_min_tx=10000)
724         self.pg0.enable_capture()
725         self.vpp_session.add_vpp_config()
726         self.test_session.update(desired_min_tx=1000000,
727                                  required_min_rx=1000000)
728         bfd_session_up(self)
729         reference_packet = wait_for_bfd_packet(self)
730         time_mark = time.time()
731         interval = 300000
732         self.test_session.update(required_min_rx=interval)
733         self.test_session.send_packet()
734         extra_time = time.time() - time_mark
735         p = wait_for_bfd_packet(self)
736         # first packet is allowed to be late by time we spent doing the update
737         # calculated in extra_time
738         self.assert_in_range(p.time - reference_packet.time,
739                              .95 * 0.75 * interval / USEC_IN_SEC,
740                              1.05 * interval / USEC_IN_SEC + extra_time,
741                              "time between BFD packets")
742         reference_packet = p
743         for dummy in range(3):
744             p = wait_for_bfd_packet(self)
745             diff = p.time - reference_packet.time
746             self.assert_in_range(diff, .95 * .75 * interval / USEC_IN_SEC,
747                                  1.05 * interval / USEC_IN_SEC,
748                                  "time between BFD packets")
749             reference_packet = p
750
751     def test_modify_req_min_rx_double(self):
752         """ modify session - double required min rx """
753         bfd_session_up(self)
754         p = wait_for_bfd_packet(self)
755         self.test_session.update(desired_min_tx=10000,
756                                  required_min_rx=10000)
757         self.test_session.send_packet()
758         # double required min rx
759         self.vpp_session.modify_parameters(
760             required_min_rx=2 * self.vpp_session.required_min_rx)
761         p = wait_for_bfd_packet(
762             self, pcap_time_min=time.time() - self.vpp_clock_offset)
763         # poll bit needs to be set
764         self.assertIn("P", p.sprintf("%BFD.flags%"),
765                       "Poll bit not set in BFD packet")
766         # finish poll sequence with final packet
767         final = self.test_session.create_packet()
768         final[BFD].flags = "F"
769         timeout = self.test_session.detect_mult * \
770             max(self.test_session.desired_min_tx,
771                 self.vpp_session.required_min_rx) / USEC_IN_SEC
772         self.test_session.send_packet(final)
773         time_mark = time.time()
774         e = self.vapi.wait_for_event(2 * timeout, "bfd_udp_session_details")
775         verify_event(self, e, expected_state=BFDState.down)
776         time_to_event = time.time() - time_mark
777         self.assert_in_range(time_to_event, .9 * timeout,
778                              1.1 * timeout, "session timeout")
779
780     def test_modify_req_min_rx_halve(self):
781         """ modify session - halve required min rx """
782         self.vpp_session.modify_parameters(
783             required_min_rx=2 * self.vpp_session.required_min_rx)
784         bfd_session_up(self)
785         p = wait_for_bfd_packet(self)
786         self.test_session.update(desired_min_tx=10000,
787                                  required_min_rx=10000)
788         self.test_session.send_packet()
789         p = wait_for_bfd_packet(
790             self, pcap_time_min=time.time() - self.vpp_clock_offset)
791         # halve required min rx
792         old_required_min_rx = self.vpp_session.required_min_rx
793         self.vpp_session.modify_parameters(
794             required_min_rx=0.5 * self.vpp_session.required_min_rx)
795         # now we wait 0.8*3*old-req-min-rx and the session should still be up
796         self.sleep(0.8 * self.vpp_session.detect_mult *
797                    old_required_min_rx / USEC_IN_SEC)
798         self.assert_equal(len(self.vapi.collect_events()), 0,
799                           "number of bfd events")
800         p = wait_for_bfd_packet(self)
801         # poll bit needs to be set
802         self.assertIn("P", p.sprintf("%BFD.flags%"),
803                       "Poll bit not set in BFD packet")
804         # finish poll sequence with final packet
805         final = self.test_session.create_packet()
806         final[BFD].flags = "F"
807         self.test_session.send_packet(final)
808         # now the session should time out under new conditions
809         before = time.time()
810         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
811         after = time.time()
812         detection_time = self.test_session.detect_mult *\
813             self.vpp_session.required_min_rx / USEC_IN_SEC
814         self.assert_in_range(after - before,
815                              0.9 * detection_time,
816                              1.1 * detection_time,
817                              "time before bfd session goes down")
818         verify_event(self, e, expected_state=BFDState.down)
819
820     def test_modify_detect_mult(self):
821         """ modify detect multiplier """
822         bfd_session_up(self)
823         p = wait_for_bfd_packet(self)
824         self.vpp_session.modify_parameters(detect_mult=1)
825         p = wait_for_bfd_packet(
826             self, pcap_time_min=time.time() - self.vpp_clock_offset)
827         self.assert_equal(self.vpp_session.detect_mult,
828                           p[BFD].detect_mult,
829                           "detect mult")
830         # poll bit must not be set
831         self.assertNotIn("P", p.sprintf("%BFD.flags%"),
832                          "Poll bit not set in BFD packet")
833         self.vpp_session.modify_parameters(detect_mult=10)
834         p = wait_for_bfd_packet(
835             self, pcap_time_min=time.time() - self.vpp_clock_offset)
836         self.assert_equal(self.vpp_session.detect_mult,
837                           p[BFD].detect_mult,
838                           "detect mult")
839         # poll bit must not be set
840         self.assertNotIn("P", p.sprintf("%BFD.flags%"),
841                          "Poll bit not set in BFD packet")
842
843     def test_queued_poll(self):
844         """ test poll sequence queueing """
845         bfd_session_up(self)
846         p = wait_for_bfd_packet(self)
847         self.vpp_session.modify_parameters(
848             required_min_rx=2 * self.vpp_session.required_min_rx)
849         p = wait_for_bfd_packet(self)
850         poll_sequence_start = time.time()
851         poll_sequence_length_min = 0.5
852         send_final_after = time.time() + poll_sequence_length_min
853         # poll bit needs to be set
854         self.assertIn("P", p.sprintf("%BFD.flags%"),
855                       "Poll bit not set in BFD packet")
856         self.assert_equal(p[BFD].required_min_rx_interval,
857                           self.vpp_session.required_min_rx,
858                           "BFD required min rx interval")
859         self.vpp_session.modify_parameters(
860             required_min_rx=2 * self.vpp_session.required_min_rx)
861         # 2nd poll sequence should be queued now
862         # don't send the reply back yet, wait for some time to emulate
863         # longer round-trip time
864         packet_count = 0
865         while time.time() < send_final_after:
866             self.test_session.send_packet()
867             p = wait_for_bfd_packet(self)
868             self.assert_equal(len(self.vapi.collect_events()), 0,
869                               "number of bfd events")
870             self.assert_equal(p[BFD].required_min_rx_interval,
871                               self.vpp_session.required_min_rx,
872                               "BFD required min rx interval")
873             packet_count += 1
874             # poll bit must be set
875             self.assertIn("P", p.sprintf("%BFD.flags%"),
876                           "Poll bit not set in BFD packet")
877         final = self.test_session.create_packet()
878         final[BFD].flags = "F"
879         self.test_session.send_packet(final)
880         # finish 1st with final
881         poll_sequence_length = time.time() - poll_sequence_start
882         # vpp must wait for some time before starting new poll sequence
883         poll_no_2_started = False
884         for dummy in range(2 * packet_count):
885             p = wait_for_bfd_packet(self)
886             self.assert_equal(len(self.vapi.collect_events()), 0,
887                               "number of bfd events")
888             if "P" in p.sprintf("%BFD.flags%"):
889                 poll_no_2_started = True
890                 if time.time() < poll_sequence_start + poll_sequence_length:
891                     raise Exception("VPP started 2nd poll sequence too soon")
892                 final = self.test_session.create_packet()
893                 final[BFD].flags = "F"
894                 self.test_session.send_packet(final)
895                 break
896             else:
897                 self.test_session.send_packet()
898         self.assertTrue(poll_no_2_started, "2nd poll sequence not performed")
899         # finish 2nd with final
900         final = self.test_session.create_packet()
901         final[BFD].flags = "F"
902         self.test_session.send_packet(final)
903         p = wait_for_bfd_packet(self)
904         # poll bit must not be set
905         self.assertNotIn("P", p.sprintf("%BFD.flags%"),
906                          "Poll bit set in BFD packet")
907
908     def test_no_periodic_if_remote_demand(self):
909         """ no periodic frames outside poll sequence if remote demand set """
910         bfd_session_up(self)
911         demand = self.test_session.create_packet()
912         demand[BFD].flags = "D"
913         self.test_session.send_packet(demand)
914         transmit_time = 0.9 \
915             * max(self.vpp_session.required_min_rx,
916                   self.test_session.desired_min_tx) \
917             / USEC_IN_SEC
918         count = 0
919         for dummy in range(self.test_session.detect_mult * 2):
920             time.sleep(transmit_time)
921             self.test_session.send_packet(demand)
922             try:
923                 p = wait_for_bfd_packet(self, timeout=0)
924                 self.logger.error(ppp("Received unexpected packet:", p))
925                 count += 1
926             except CaptureTimeoutError:
927                 pass
928         events = self.vapi.collect_events()
929         for e in events:
930             self.logger.error("Received unexpected event: %s", e)
931         self.assert_equal(count, 0, "number of packets received")
932         self.assert_equal(len(events), 0, "number of events received")
933
934     def test_echo_looped_back(self):
935         """ echo packets looped back """
936         # don't need a session in this case..
937         self.vpp_session.remove_vpp_config()
938         self.pg0.enable_capture()
939         echo_packet_count = 10
940         # random source port low enough to increment a few times..
941         udp_sport_tx = randint(1, 50000)
942         udp_sport_rx = udp_sport_tx
943         echo_packet = (Ether(src=self.pg0.remote_mac,
944                              dst=self.pg0.local_mac) /
945                        IP(src=self.pg0.remote_ip4,
946                           dst=self.pg0.remote_ip4) /
947                        UDP(dport=BFD.udp_dport_echo) /
948                        Raw("this should be looped back"))
949         for dummy in range(echo_packet_count):
950             self.sleep(.01, "delay between echo packets")
951             echo_packet[UDP].sport = udp_sport_tx
952             udp_sport_tx += 1
953             self.logger.debug(ppp("Sending packet:", echo_packet))
954             self.pg0.add_stream(echo_packet)
955             self.pg_start()
956         for dummy in range(echo_packet_count):
957             p = self.pg0.wait_for_packet(1)
958             self.logger.debug(ppp("Got packet:", p))
959             ether = p[Ether]
960             self.assert_equal(self.pg0.remote_mac,
961                               ether.dst, "Destination MAC")
962             self.assert_equal(self.pg0.local_mac, ether.src, "Source MAC")
963             ip = p[IP]
964             self.assert_equal(self.pg0.remote_ip4, ip.dst, "Destination IP")
965             self.assert_equal(self.pg0.remote_ip4, ip.src, "Destination IP")
966             udp = p[UDP]
967             self.assert_equal(udp.dport, BFD.udp_dport_echo,
968                               "UDP destination port")
969             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
970             udp_sport_rx += 1
971             # need to compare the hex payload here, otherwise BFD_vpp_echo
972             # gets in way
973             self.assertEqual(str(p[UDP].payload),
974                              str(echo_packet[UDP].payload),
975                              "Received packet is not the echo packet sent")
976         self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== "
977                           "ECHO packet identifier for test purposes)")
978
979     def test_echo(self):
980         """ echo function """
981         bfd_session_up(self)
982         self.test_session.update(required_min_echo_rx=50000)
983         self.test_session.send_packet()
984         detection_time = self.test_session.detect_mult *\
985             self.vpp_session.required_min_rx / USEC_IN_SEC
986         # echo shouldn't work without echo source set
987         for dummy in range(3):
988             sleep = 0.75 * detection_time
989             self.sleep(sleep, "delay before sending bfd packet")
990             self.test_session.send_packet()
991         p = wait_for_bfd_packet(
992             self, pcap_time_min=time.time() - self.vpp_clock_offset)
993         self.assert_equal(p[BFD].required_min_rx_interval,
994                           self.vpp_session.required_min_rx,
995                           "BFD required min rx interval")
996         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
997         # should be turned on - loopback echo packets
998         for dummy in range(3):
999             loop_until = time.time() + 0.75 * detection_time
1000             while time.time() < loop_until:
1001                 p = self.pg0.wait_for_packet(1)
1002                 self.logger.debug(ppp("Got packet:", p))
1003                 if p[UDP].dport == BFD.udp_dport_echo:
1004                     self.assert_equal(
1005                         p[IP].dst, self.pg0.local_ip4, "BFD ECHO dst IP")
1006                     self.assertNotEqual(p[IP].src, self.loopback0.local_ip4,
1007                                         "BFD ECHO src IP equal to loopback IP")
1008                     self.logger.debug(ppp("Looping back packet:", p))
1009                     self.pg0.add_stream(p)
1010                     self.pg_start()
1011                 elif p.haslayer(BFD):
1012                     self.assertGreaterEqual(p[BFD].required_min_rx_interval,
1013                                             1000000)
1014                     if "P" in p.sprintf("%BFD.flags%"):
1015                         final = self.test_session.create_packet()
1016                         final[BFD].flags = "F"
1017                         self.test_session.send_packet(final)
1018                 else:
1019                     raise Exception(ppp("Received unknown packet:", p))
1020
1021                 self.assert_equal(len(self.vapi.collect_events()), 0,
1022                                   "number of bfd events")
1023             self.test_session.send_packet()
1024
1025     def test_echo_fail(self):
1026         """ session goes down if echo function fails """
1027         bfd_session_up(self)
1028         self.test_session.update(required_min_echo_rx=50000)
1029         self.test_session.send_packet()
1030         detection_time = self.test_session.detect_mult *\
1031             self.vpp_session.required_min_rx / USEC_IN_SEC
1032         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
1033         # echo function should be used now, but we will drop the echo packets
1034         verified_diag = False
1035         for dummy in range(3):
1036             loop_until = time.time() + 0.75 * detection_time
1037             while time.time() < loop_until:
1038                 p = self.pg0.wait_for_packet(1)
1039                 self.logger.debug(ppp("Got packet:", p))
1040                 if p[UDP].dport == BFD.udp_dport_echo:
1041                     # dropped
1042                     pass
1043                 elif p.haslayer(BFD):
1044                     if "P" in p.sprintf("%BFD.flags%"):
1045                         self.assertGreaterEqual(
1046                             p[BFD].required_min_rx_interval,
1047                             1000000)
1048                         final = self.test_session.create_packet()
1049                         final[BFD].flags = "F"
1050                         self.test_session.send_packet(final)
1051                     if p[BFD].state == BFDState.down:
1052                         self.assert_equal(p[BFD].diag,
1053                                           BFDDiagCode.echo_function_failed,
1054                                           BFDDiagCode)
1055                         verified_diag = True
1056                 else:
1057                     raise Exception(ppp("Received unknown packet:", p))
1058             self.test_session.send_packet()
1059         events = self.vapi.collect_events()
1060         self.assert_equal(len(events), 1, "number of bfd events")
1061         self.assert_equal(events[0].state, BFDState.down, BFDState)
1062         self.assertTrue(verified_diag, "Incorrect diagnostics code received")
1063
1064     def test_echo_stop(self):
1065         """ echo function stops if peer sets required min echo rx zero """
1066         bfd_session_up(self)
1067         self.test_session.update(required_min_echo_rx=50000)
1068         self.test_session.send_packet()
1069         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
1070         # wait for first echo packet
1071         while True:
1072             p = self.pg0.wait_for_packet(1)
1073             self.logger.debug(ppp("Got packet:", p))
1074             if p[UDP].dport == BFD.udp_dport_echo:
1075                 self.logger.debug(ppp("Looping back packet:", p))
1076                 self.pg0.add_stream(p)
1077                 self.pg_start()
1078                 break
1079             elif p.haslayer(BFD):
1080                 # ignore BFD
1081                 pass
1082             else:
1083                 raise Exception(ppp("Received unknown packet:", p))
1084         self.test_session.update(required_min_echo_rx=0)
1085         self.test_session.send_packet()
1086         # echo packets shouldn't arrive anymore
1087         for dummy in range(5):
1088             wait_for_bfd_packet(
1089                 self, pcap_time_min=time.time() - self.vpp_clock_offset)
1090             self.test_session.send_packet()
1091             events = self.vapi.collect_events()
1092             self.assert_equal(len(events), 0, "number of bfd events")
1093
1094     def test_stale_echo(self):
1095         """ stale echo packets don't keep a session up """
1096         bfd_session_up(self)
1097         self.test_session.update(required_min_echo_rx=50000)
1098         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
1099         self.test_session.send_packet()
1100         # should be turned on - loopback echo packets
1101         echo_packet = None
1102         timeout_at = None
1103         timeout_ok = False
1104         for dummy in range(10 * self.vpp_session.detect_mult):
1105             p = self.pg0.wait_for_packet(1)
1106             if p[UDP].dport == BFD.udp_dport_echo:
1107                 if echo_packet is None:
1108                     self.logger.debug(ppp("Got first echo packet:", p))
1109                     echo_packet = p
1110                     timeout_at = time.time() + self.vpp_session.detect_mult * \
1111                         self.test_session.required_min_echo_rx / USEC_IN_SEC
1112                 else:
1113                     self.logger.debug(ppp("Got followup echo packet:", p))
1114                 self.logger.debug(ppp("Looping back first echo packet:", p))
1115                 self.pg0.add_stream(echo_packet)
1116                 self.pg_start()
1117             elif p.haslayer(BFD):
1118                 self.logger.debug(ppp("Got packet:", p))
1119                 if "P" in p.sprintf("%BFD.flags%"):
1120                     final = self.test_session.create_packet()
1121                     final[BFD].flags = "F"
1122                     self.test_session.send_packet(final)
1123                 if p[BFD].state == BFDState.down:
1124                     self.assertIsNotNone(
1125                         timeout_at,
1126                         "Session went down before first echo packet received")
1127                     now = time.time()
1128                     self.assertGreaterEqual(
1129                         now, timeout_at,
1130                         "Session timeout at %s, but is expected at %s" %
1131                         (now, timeout_at))
1132                     self.assert_equal(p[BFD].diag,
1133                                       BFDDiagCode.echo_function_failed,
1134                                       BFDDiagCode)
1135                     events = self.vapi.collect_events()
1136                     self.assert_equal(len(events), 1, "number of bfd events")
1137                     self.assert_equal(events[0].state, BFDState.down, BFDState)
1138                     timeout_ok = True
1139                     break
1140             else:
1141                 raise Exception(ppp("Received unknown packet:", p))
1142             self.test_session.send_packet()
1143         self.assertTrue(timeout_ok, "Expected timeout event didn't occur")
1144
1145     def test_invalid_echo_checksum(self):
1146         """ echo packets with invalid checksum don't keep a session up """
1147         bfd_session_up(self)
1148         self.test_session.update(required_min_echo_rx=50000)
1149         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
1150         self.test_session.send_packet()
1151         # should be turned on - loopback echo packets
1152         timeout_at = None
1153         timeout_ok = False
1154         for dummy in range(10 * self.vpp_session.detect_mult):
1155             p = self.pg0.wait_for_packet(1)
1156             if p[UDP].dport == BFD.udp_dport_echo:
1157                 self.logger.debug(ppp("Got echo packet:", p))
1158                 if timeout_at is None:
1159                     timeout_at = time.time() + self.vpp_session.detect_mult * \
1160                         self.test_session.required_min_echo_rx / USEC_IN_SEC
1161                 p[BFD_vpp_echo].checksum = getrandbits(64)
1162                 self.logger.debug(ppp("Looping back modified echo packet:", p))
1163                 self.pg0.add_stream(p)
1164                 self.pg_start()
1165             elif p.haslayer(BFD):
1166                 self.logger.debug(ppp("Got packet:", p))
1167                 if "P" in p.sprintf("%BFD.flags%"):
1168                     final = self.test_session.create_packet()
1169                     final[BFD].flags = "F"
1170                     self.test_session.send_packet(final)
1171                 if p[BFD].state == BFDState.down:
1172                     self.assertIsNotNone(
1173                         timeout_at,
1174                         "Session went down before first echo packet received")
1175                     now = time.time()
1176                     self.assertGreaterEqual(
1177                         now, timeout_at,
1178                         "Session timeout at %s, but is expected at %s" %
1179                         (now, timeout_at))
1180                     self.assert_equal(p[BFD].diag,
1181                                       BFDDiagCode.echo_function_failed,
1182                                       BFDDiagCode)
1183                     events = self.vapi.collect_events()
1184                     self.assert_equal(len(events), 1, "number of bfd events")
1185                     self.assert_equal(events[0].state, BFDState.down, BFDState)
1186                     timeout_ok = True
1187                     break
1188             else:
1189                 raise Exception(ppp("Received unknown packet:", p))
1190             self.test_session.send_packet()
1191         self.assertTrue(timeout_ok, "Expected timeout event didn't occur")
1192
1193     def test_admin_up_down(self):
1194         """ put session admin-up and admin-down """
1195         bfd_session_up(self)
1196         self.vpp_session.admin_down()
1197         self.pg0.enable_capture()
1198         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
1199         verify_event(self, e, expected_state=BFDState.admin_down)
1200         for dummy in range(2):
1201             p = wait_for_bfd_packet(self)
1202             self.assert_equal(BFDState.admin_down, p[BFD].state, BFDState)
1203         # try to bring session up - shouldn't be possible
1204         self.test_session.update(state=BFDState.init)
1205         self.test_session.send_packet()
1206         for dummy in range(2):
1207             p = wait_for_bfd_packet(self)
1208             self.assert_equal(BFDState.admin_down, p[BFD].state, BFDState)
1209         self.vpp_session.admin_up()
1210         self.test_session.update(state=BFDState.down)
1211         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
1212         verify_event(self, e, expected_state=BFDState.down)
1213         p = wait_for_bfd_packet(self)
1214         self.assert_equal(BFDState.down, p[BFD].state, BFDState)
1215         self.test_session.send_packet()
1216         p = wait_for_bfd_packet(self)
1217         self.assert_equal(BFDState.init, p[BFD].state, BFDState)
1218         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
1219         verify_event(self, e, expected_state=BFDState.init)
1220         self.test_session.update(state=BFDState.up)
1221         self.test_session.send_packet()
1222         p = wait_for_bfd_packet(self)
1223         self.assert_equal(BFDState.up, p[BFD].state, BFDState)
1224         e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
1225         verify_event(self, e, expected_state=BFDState.up)
1226
1227     def test_config_change_remote_demand(self):
1228         """ configuration change while peer in demand mode """
1229         bfd_session_up(self)
1230         demand = self.test_session.create_packet()
1231         demand[BFD].flags = "D"
1232         self.test_session.send_packet(demand)
1233         self.vpp_session.modify_parameters(
1234             required_min_rx=2 * self.vpp_session.required_min_rx)
1235         p = wait_for_bfd_packet(self)
1236         # poll bit must be set
1237         self.assertIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set")
1238         # terminate poll sequence
1239         final = self.test_session.create_packet()
1240         final[BFD].flags = "D+F"
1241         self.test_session.send_packet(final)
1242         # vpp should be quiet now again
1243         transmit_time = 0.9 \
1244             * max(self.vpp_session.required_min_rx,
1245                   self.test_session.desired_min_tx) \
1246             / USEC_IN_SEC
1247         count = 0
1248         for dummy in range(self.test_session.detect_mult * 2):
1249             time.sleep(transmit_time)
1250             self.test_session.send_packet(demand)
1251             try:
1252                 p = wait_for_bfd_packet(self, timeout=0)
1253                 self.logger.error(ppp("Received unexpected packet:", p))
1254                 count += 1
1255             except CaptureTimeoutError:
1256                 pass
1257         events = self.vapi.collect_events()
1258         for e in events:
1259             self.logger.error("Received unexpected event: %s", e)
1260         self.assert_equal(count, 0, "number of packets received")
1261         self.assert_equal(len(events), 0, "number of events received")
1262
1263
1264 class BFD6TestCase(VppTestCase):
1265     """Bidirectional Forwarding Detection (BFD) (IPv6) """
1266
1267     pg0 = None
1268     vpp_clock_offset = None
1269     vpp_session = None
1270     test_session = None
1271
1272     @classmethod
1273     def setUpClass(cls):
1274         super(BFD6TestCase, cls).setUpClass()
1275         try:
1276             cls.create_pg_interfaces([0])
1277             cls.pg0.config_ip6()
1278             cls.pg0.configure_ipv6_neighbors()
1279             cls.pg0.admin_up()
1280             cls.pg0.resolve_ndp()
1281             cls.create_loopback_interfaces([0])
1282             cls.loopback0 = cls.lo_interfaces[0]
1283             cls.loopback0.config_ip6()
1284             cls.loopback0.admin_up()
1285
1286         except Exception:
1287             super(BFD6TestCase, cls).tearDownClass()
1288             raise
1289
1290     def setUp(self):
1291         super(BFD6TestCase, self).setUp()
1292         self.factory = AuthKeyFactory()
1293         self.vapi.want_bfd_events()
1294         self.pg0.enable_capture()
1295         try:
1296             self.vpp_session = VppBFDUDPSession(self, self.pg0,
1297                                                 self.pg0.remote_ip6,
1298                                                 af=AF_INET6)
1299             self.vpp_session.add_vpp_config()
1300             self.vpp_session.admin_up()
1301             self.test_session = BFDTestSession(self, self.pg0, AF_INET6)
1302             self.logger.debug(self.vapi.cli("show adj nbr"))
1303         except:
1304             self.vapi.want_bfd_events(enable_disable=0)
1305             raise
1306
1307     def tearDown(self):
1308         if not self.vpp_dead:
1309             self.vapi.want_bfd_events(enable_disable=0)
1310         self.vapi.collect_events()  # clear the event queue
1311         super(BFD6TestCase, self).tearDown()
1312
1313     def test_session_up(self):
1314         """ bring BFD session up """
1315         bfd_session_up(self)
1316
1317     def test_hold_up(self):
1318         """ hold BFD session up """
1319         bfd_session_up(self)
1320         for dummy in range(self.test_session.detect_mult * 2):
1321             wait_for_bfd_packet(self)
1322             self.test_session.send_packet()
1323         self.assert_equal(len(self.vapi.collect_events()), 0,
1324                           "number of bfd events")
1325         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1326
1327     def test_echo_looped_back(self):
1328         """ echo packets looped back """
1329         # don't need a session in this case..
1330         self.vpp_session.remove_vpp_config()
1331         self.pg0.enable_capture()
1332         echo_packet_count = 10
1333         # random source port low enough to increment a few times..
1334         udp_sport_tx = randint(1, 50000)
1335         udp_sport_rx = udp_sport_tx
1336         echo_packet = (Ether(src=self.pg0.remote_mac,
1337                              dst=self.pg0.local_mac) /
1338                        IPv6(src=self.pg0.remote_ip6,
1339                             dst=self.pg0.remote_ip6) /
1340                        UDP(dport=BFD.udp_dport_echo) /
1341                        Raw("this should be looped back"))
1342         for dummy in range(echo_packet_count):
1343             self.sleep(.01, "delay between echo packets")
1344             echo_packet[UDP].sport = udp_sport_tx
1345             udp_sport_tx += 1
1346             self.logger.debug(ppp("Sending packet:", echo_packet))
1347             self.pg0.add_stream(echo_packet)
1348             self.pg_start()
1349         for dummy in range(echo_packet_count):
1350             p = self.pg0.wait_for_packet(1)
1351             self.logger.debug(ppp("Got packet:", p))
1352             ether = p[Ether]
1353             self.assert_equal(self.pg0.remote_mac,
1354                               ether.dst, "Destination MAC")
1355             self.assert_equal(self.pg0.local_mac, ether.src, "Source MAC")
1356             ip = p[IPv6]
1357             self.assert_equal(self.pg0.remote_ip6, ip.dst, "Destination IP")
1358             self.assert_equal(self.pg0.remote_ip6, ip.src, "Destination IP")
1359             udp = p[UDP]
1360             self.assert_equal(udp.dport, BFD.udp_dport_echo,
1361                               "UDP destination port")
1362             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
1363             udp_sport_rx += 1
1364             # need to compare the hex payload here, otherwise BFD_vpp_echo
1365             # gets in way
1366             self.assertEqual(str(p[UDP].payload),
1367                              str(echo_packet[UDP].payload),
1368                              "Received packet is not the echo packet sent")
1369         self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== "
1370                           "ECHO packet identifier for test purposes)")
1371         self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== "
1372                           "ECHO packet identifier for test purposes)")
1373
1374     def test_echo(self):
1375         """ echo function used """
1376         bfd_session_up(self)
1377         self.test_session.update(required_min_echo_rx=50000)
1378         self.test_session.send_packet()
1379         detection_time = self.test_session.detect_mult *\
1380             self.vpp_session.required_min_rx / USEC_IN_SEC
1381         # echo shouldn't work without echo source set
1382         for dummy in range(3):
1383             sleep = 0.75 * detection_time
1384             self.sleep(sleep, "delay before sending bfd packet")
1385             self.test_session.send_packet()
1386         p = wait_for_bfd_packet(
1387             self, pcap_time_min=time.time() - self.vpp_clock_offset)
1388         self.assert_equal(p[BFD].required_min_rx_interval,
1389                           self.vpp_session.required_min_rx,
1390                           "BFD required min rx interval")
1391         self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
1392         # should be turned on - loopback echo packets
1393         for dummy in range(3):
1394             loop_until = time.time() + 0.75 * detection_time
1395             while time.time() < loop_until:
1396                 p = self.pg0.wait_for_packet(1)
1397                 self.logger.debug(ppp("Got packet:", p))
1398                 if p[UDP].dport == BFD.udp_dport_echo:
1399                     self.assert_equal(
1400                         p[IPv6].dst, self.pg0.local_ip6, "BFD ECHO dst IP")
1401                     self.assertNotEqual(p[IPv6].src, self.loopback0.local_ip6,
1402                                         "BFD ECHO src IP equal to loopback IP")
1403                     self.logger.debug(ppp("Looping back packet:", p))
1404                     self.pg0.add_stream(p)
1405                     self.pg_start()
1406                 elif p.haslayer(BFD):
1407                     self.assertGreaterEqual(p[BFD].required_min_rx_interval,
1408                                             1000000)
1409                     if "P" in p.sprintf("%BFD.flags%"):
1410                         final = self.test_session.create_packet()
1411                         final[BFD].flags = "F"
1412                         self.test_session.send_packet(final)
1413                 else:
1414                     raise Exception(ppp("Received unknown packet:", p))
1415
1416                 self.assert_equal(len(self.vapi.collect_events()), 0,
1417                                   "number of bfd events")
1418             self.test_session.send_packet()
1419
1420
1421 class BFDSHA1TestCase(VppTestCase):
1422     """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
1423
1424     pg0 = None
1425     vpp_clock_offset = None
1426     vpp_session = None
1427     test_session = None
1428
1429     @classmethod
1430     def setUpClass(cls):
1431         super(BFDSHA1TestCase, cls).setUpClass()
1432         try:
1433             cls.create_pg_interfaces([0])
1434             cls.pg0.config_ip4()
1435             cls.pg0.admin_up()
1436             cls.pg0.resolve_arp()
1437
1438         except Exception:
1439             super(BFDSHA1TestCase, cls).tearDownClass()
1440             raise
1441
1442     def setUp(self):
1443         super(BFDSHA1TestCase, self).setUp()
1444         self.factory = AuthKeyFactory()
1445         self.vapi.want_bfd_events()
1446         self.pg0.enable_capture()
1447
1448     def tearDown(self):
1449         if not self.vpp_dead:
1450             self.vapi.want_bfd_events(enable_disable=0)
1451         self.vapi.collect_events()  # clear the event queue
1452         super(BFDSHA1TestCase, self).tearDown()
1453
1454     def test_session_up(self):
1455         """ bring BFD session up """
1456         key = self.factory.create_random_key(self)
1457         key.add_vpp_config()
1458         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1459                                             self.pg0.remote_ip4,
1460                                             sha1_key=key)
1461         self.vpp_session.add_vpp_config()
1462         self.vpp_session.admin_up()
1463         self.test_session = BFDTestSession(
1464             self, self.pg0, AF_INET, sha1_key=key,
1465             bfd_key_id=self.vpp_session.bfd_key_id)
1466         bfd_session_up(self)
1467
1468     def test_hold_up(self):
1469         """ hold BFD session up """
1470         key = self.factory.create_random_key(self)
1471         key.add_vpp_config()
1472         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1473                                             self.pg0.remote_ip4,
1474                                             sha1_key=key)
1475         self.vpp_session.add_vpp_config()
1476         self.vpp_session.admin_up()
1477         self.test_session = BFDTestSession(
1478             self, self.pg0, AF_INET, sha1_key=key,
1479             bfd_key_id=self.vpp_session.bfd_key_id)
1480         bfd_session_up(self)
1481         for dummy in range(self.test_session.detect_mult * 2):
1482             wait_for_bfd_packet(self)
1483             self.test_session.send_packet()
1484         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1485
1486     def test_hold_up_meticulous(self):
1487         """ hold BFD session up - meticulous auth """
1488         key = self.factory.create_random_key(
1489             self, BFDAuthType.meticulous_keyed_sha1)
1490         key.add_vpp_config()
1491         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1492                                             self.pg0.remote_ip4, sha1_key=key)
1493         self.vpp_session.add_vpp_config()
1494         self.vpp_session.admin_up()
1495         # specify sequence number so that it wraps
1496         self.test_session = BFDTestSession(
1497             self, self.pg0, AF_INET, sha1_key=key,
1498             bfd_key_id=self.vpp_session.bfd_key_id,
1499             our_seq_number=0xFFFFFFFF - 4)
1500         bfd_session_up(self)
1501         for dummy in range(30):
1502             wait_for_bfd_packet(self)
1503             self.test_session.inc_seq_num()
1504             self.test_session.send_packet()
1505         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1506
1507     def test_send_bad_seq_number(self):
1508         """ session is not kept alive by msgs with bad sequence numbers"""
1509         key = self.factory.create_random_key(
1510             self, BFDAuthType.meticulous_keyed_sha1)
1511         key.add_vpp_config()
1512         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1513                                             self.pg0.remote_ip4, sha1_key=key)
1514         self.vpp_session.add_vpp_config()
1515         self.vpp_session.admin_up()
1516         self.test_session = BFDTestSession(
1517             self, self.pg0, AF_INET, sha1_key=key,
1518             bfd_key_id=self.vpp_session.bfd_key_id)
1519         bfd_session_up(self)
1520         detection_time = self.test_session.detect_mult *\
1521             self.vpp_session.required_min_rx / USEC_IN_SEC
1522         send_until = time.time() + 2 * detection_time
1523         while time.time() < send_until:
1524             self.test_session.send_packet()
1525             self.sleep(0.7 * self.vpp_session.required_min_rx / USEC_IN_SEC,
1526                        "time between bfd packets")
1527         e = self.vapi.collect_events()
1528         # session should be down now, because the sequence numbers weren't
1529         # updated
1530         self.assert_equal(len(e), 1, "number of bfd events")
1531         verify_event(self, e[0], expected_state=BFDState.down)
1532
1533     def execute_rogue_session_scenario(self, vpp_bfd_udp_session,
1534                                        legitimate_test_session,
1535                                        rogue_test_session,
1536                                        rogue_bfd_values=None):
1537         """ execute a rogue session interaction scenario
1538
1539         1. create vpp session, add config
1540         2. bring the legitimate session up
1541         3. copy the bfd values from legitimate session to rogue session
1542         4. apply rogue_bfd_values to rogue session
1543         5. set rogue session state to down
1544         6. send message to take the session down from the rogue session
1545         7. assert that the legitimate session is unaffected
1546         """
1547
1548         self.vpp_session = vpp_bfd_udp_session
1549         self.vpp_session.add_vpp_config()
1550         self.vpp_session.admin_up()
1551         self.test_session = legitimate_test_session
1552         # bring vpp session up
1553         bfd_session_up(self)
1554         # send packet from rogue session
1555         rogue_test_session.update(
1556             my_discriminator=self.test_session.my_discriminator,
1557             your_discriminator=self.test_session.your_discriminator,
1558             desired_min_tx=self.test_session.desired_min_tx,
1559             required_min_rx=self.test_session.required_min_rx,
1560             detect_mult=self.test_session.detect_mult,
1561             diag=self.test_session.diag,
1562             state=self.test_session.state,
1563             auth_type=self.test_session.auth_type)
1564         if rogue_bfd_values:
1565             rogue_test_session.update(**rogue_bfd_values)
1566         rogue_test_session.update(state=BFDState.down)
1567         rogue_test_session.send_packet()
1568         wait_for_bfd_packet(self)
1569         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1570
1571     def test_mismatch_auth(self):
1572         """ session is not brought down by unauthenticated msg """
1573         key = self.factory.create_random_key(self)
1574         key.add_vpp_config()
1575         vpp_session = VppBFDUDPSession(
1576             self, self.pg0, self.pg0.remote_ip4, sha1_key=key)
1577         legitimate_test_session = BFDTestSession(
1578             self, self.pg0, AF_INET, sha1_key=key,
1579             bfd_key_id=vpp_session.bfd_key_id)
1580         rogue_test_session = BFDTestSession(self, self.pg0, AF_INET)
1581         self.execute_rogue_session_scenario(vpp_session,
1582                                             legitimate_test_session,
1583                                             rogue_test_session)
1584
1585     def test_mismatch_bfd_key_id(self):
1586         """ session is not brought down by msg with non-existent key-id """
1587         key = self.factory.create_random_key(self)
1588         key.add_vpp_config()
1589         vpp_session = VppBFDUDPSession(
1590             self, self.pg0, self.pg0.remote_ip4, sha1_key=key)
1591         # pick a different random bfd key id
1592         x = randint(0, 255)
1593         while x == vpp_session.bfd_key_id:
1594             x = randint(0, 255)
1595         legitimate_test_session = BFDTestSession(
1596             self, self.pg0, AF_INET, sha1_key=key,
1597             bfd_key_id=vpp_session.bfd_key_id)
1598         rogue_test_session = BFDTestSession(
1599             self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=x)
1600         self.execute_rogue_session_scenario(vpp_session,
1601                                             legitimate_test_session,
1602                                             rogue_test_session)
1603
1604     def test_mismatched_auth_type(self):
1605         """ session is not brought down by msg with wrong auth type """
1606         key = self.factory.create_random_key(self)
1607         key.add_vpp_config()
1608         vpp_session = VppBFDUDPSession(
1609             self, self.pg0, self.pg0.remote_ip4, sha1_key=key)
1610         legitimate_test_session = BFDTestSession(
1611             self, self.pg0, AF_INET, sha1_key=key,
1612             bfd_key_id=vpp_session.bfd_key_id)
1613         rogue_test_session = BFDTestSession(
1614             self, self.pg0, AF_INET, sha1_key=key,
1615             bfd_key_id=vpp_session.bfd_key_id)
1616         self.execute_rogue_session_scenario(
1617             vpp_session, legitimate_test_session, rogue_test_session,
1618             {'auth_type': BFDAuthType.keyed_md5})
1619
1620     def test_restart(self):
1621         """ simulate remote peer restart and resynchronization """
1622         key = self.factory.create_random_key(
1623             self, BFDAuthType.meticulous_keyed_sha1)
1624         key.add_vpp_config()
1625         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1626                                             self.pg0.remote_ip4, sha1_key=key)
1627         self.vpp_session.add_vpp_config()
1628         self.vpp_session.admin_up()
1629         self.test_session = BFDTestSession(
1630             self, self.pg0, AF_INET, sha1_key=key,
1631             bfd_key_id=self.vpp_session.bfd_key_id, our_seq_number=0)
1632         bfd_session_up(self)
1633         # don't send any packets for 2*detection_time
1634         detection_time = self.test_session.detect_mult *\
1635             self.vpp_session.required_min_rx / USEC_IN_SEC
1636         self.sleep(detection_time, "simulating peer restart")
1637         events = self.vapi.collect_events()
1638         self.assert_equal(len(events), 1, "number of bfd events")
1639         verify_event(self, events[0], expected_state=BFDState.down)
1640         self.test_session.update(state=BFDState.down)
1641         # reset sequence number
1642         self.test_session.our_seq_number = 0
1643         self.test_session.vpp_seq_number = None
1644         # now throw away any pending packets
1645         self.pg0.enable_capture()
1646         bfd_session_up(self)
1647
1648
1649 class BFDAuthOnOffTestCase(VppTestCase):
1650     """Bidirectional Forwarding Detection (BFD) (changing auth) """
1651
1652     pg0 = None
1653     vpp_session = None
1654     test_session = None
1655
1656     @classmethod
1657     def setUpClass(cls):
1658         super(BFDAuthOnOffTestCase, cls).setUpClass()
1659         try:
1660             cls.create_pg_interfaces([0])
1661             cls.pg0.config_ip4()
1662             cls.pg0.admin_up()
1663             cls.pg0.resolve_arp()
1664
1665         except Exception:
1666             super(BFDAuthOnOffTestCase, cls).tearDownClass()
1667             raise
1668
1669     def setUp(self):
1670         super(BFDAuthOnOffTestCase, self).setUp()
1671         self.factory = AuthKeyFactory()
1672         self.vapi.want_bfd_events()
1673         self.pg0.enable_capture()
1674
1675     def tearDown(self):
1676         if not self.vpp_dead:
1677             self.vapi.want_bfd_events(enable_disable=0)
1678         self.vapi.collect_events()  # clear the event queue
1679         super(BFDAuthOnOffTestCase, self).tearDown()
1680
1681     def test_auth_on_immediate(self):
1682         """ turn auth on without disturbing session state (immediate) """
1683         key = self.factory.create_random_key(self)
1684         key.add_vpp_config()
1685         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1686                                             self.pg0.remote_ip4)
1687         self.vpp_session.add_vpp_config()
1688         self.vpp_session.admin_up()
1689         self.test_session = BFDTestSession(self, self.pg0, AF_INET)
1690         bfd_session_up(self)
1691         for dummy in range(self.test_session.detect_mult * 2):
1692             p = wait_for_bfd_packet(self)
1693             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1694             self.test_session.send_packet()
1695         self.vpp_session.activate_auth(key)
1696         self.test_session.bfd_key_id = self.vpp_session.bfd_key_id
1697         self.test_session.sha1_key = key
1698         for dummy in range(self.test_session.detect_mult * 2):
1699             p = wait_for_bfd_packet(self)
1700             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1701             self.test_session.send_packet()
1702         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1703         self.assert_equal(len(self.vapi.collect_events()), 0,
1704                           "number of bfd events")
1705
1706     def test_auth_off_immediate(self):
1707         """ turn auth off without disturbing session state (immediate) """
1708         key = self.factory.create_random_key(self)
1709         key.add_vpp_config()
1710         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1711                                             self.pg0.remote_ip4, sha1_key=key)
1712         self.vpp_session.add_vpp_config()
1713         self.vpp_session.admin_up()
1714         self.test_session = BFDTestSession(
1715             self, self.pg0, AF_INET, sha1_key=key,
1716             bfd_key_id=self.vpp_session.bfd_key_id)
1717         bfd_session_up(self)
1718         # self.vapi.want_bfd_events(enable_disable=0)
1719         for dummy in range(self.test_session.detect_mult * 2):
1720             p = wait_for_bfd_packet(self)
1721             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1722             self.test_session.inc_seq_num()
1723             self.test_session.send_packet()
1724         self.vpp_session.deactivate_auth()
1725         self.test_session.bfd_key_id = None
1726         self.test_session.sha1_key = None
1727         for dummy in range(self.test_session.detect_mult * 2):
1728             p = wait_for_bfd_packet(self)
1729             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1730             self.test_session.inc_seq_num()
1731             self.test_session.send_packet()
1732         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1733         self.assert_equal(len(self.vapi.collect_events()), 0,
1734                           "number of bfd events")
1735
1736     def test_auth_change_key_immediate(self):
1737         """ change auth key without disturbing session state (immediate) """
1738         key1 = self.factory.create_random_key(self)
1739         key1.add_vpp_config()
1740         key2 = self.factory.create_random_key(self)
1741         key2.add_vpp_config()
1742         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1743                                             self.pg0.remote_ip4, sha1_key=key1)
1744         self.vpp_session.add_vpp_config()
1745         self.vpp_session.admin_up()
1746         self.test_session = BFDTestSession(
1747             self, self.pg0, AF_INET, sha1_key=key1,
1748             bfd_key_id=self.vpp_session.bfd_key_id)
1749         bfd_session_up(self)
1750         for dummy in range(self.test_session.detect_mult * 2):
1751             p = wait_for_bfd_packet(self)
1752             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1753             self.test_session.send_packet()
1754         self.vpp_session.activate_auth(key2)
1755         self.test_session.bfd_key_id = self.vpp_session.bfd_key_id
1756         self.test_session.sha1_key = key2
1757         for dummy in range(self.test_session.detect_mult * 2):
1758             p = wait_for_bfd_packet(self)
1759             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1760             self.test_session.send_packet()
1761         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1762         self.assert_equal(len(self.vapi.collect_events()), 0,
1763                           "number of bfd events")
1764
1765     def test_auth_on_delayed(self):
1766         """ turn auth on without disturbing session state (delayed) """
1767         key = self.factory.create_random_key(self)
1768         key.add_vpp_config()
1769         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1770                                             self.pg0.remote_ip4)
1771         self.vpp_session.add_vpp_config()
1772         self.vpp_session.admin_up()
1773         self.test_session = BFDTestSession(self, self.pg0, AF_INET)
1774         bfd_session_up(self)
1775         for dummy in range(self.test_session.detect_mult * 2):
1776             wait_for_bfd_packet(self)
1777             self.test_session.send_packet()
1778         self.vpp_session.activate_auth(key, delayed=True)
1779         for dummy in range(self.test_session.detect_mult * 2):
1780             p = wait_for_bfd_packet(self)
1781             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1782             self.test_session.send_packet()
1783         self.test_session.bfd_key_id = self.vpp_session.bfd_key_id
1784         self.test_session.sha1_key = key
1785         self.test_session.send_packet()
1786         for dummy in range(self.test_session.detect_mult * 2):
1787             p = wait_for_bfd_packet(self)
1788             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1789             self.test_session.send_packet()
1790         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1791         self.assert_equal(len(self.vapi.collect_events()), 0,
1792                           "number of bfd events")
1793
1794     def test_auth_off_delayed(self):
1795         """ turn auth off without disturbing session state (delayed) """
1796         key = self.factory.create_random_key(self)
1797         key.add_vpp_config()
1798         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1799                                             self.pg0.remote_ip4, sha1_key=key)
1800         self.vpp_session.add_vpp_config()
1801         self.vpp_session.admin_up()
1802         self.test_session = BFDTestSession(
1803             self, self.pg0, AF_INET, sha1_key=key,
1804             bfd_key_id=self.vpp_session.bfd_key_id)
1805         bfd_session_up(self)
1806         for dummy in range(self.test_session.detect_mult * 2):
1807             p = wait_for_bfd_packet(self)
1808             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1809             self.test_session.send_packet()
1810         self.vpp_session.deactivate_auth(delayed=True)
1811         for dummy in range(self.test_session.detect_mult * 2):
1812             p = wait_for_bfd_packet(self)
1813             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1814             self.test_session.send_packet()
1815         self.test_session.bfd_key_id = None
1816         self.test_session.sha1_key = None
1817         self.test_session.send_packet()
1818         for dummy in range(self.test_session.detect_mult * 2):
1819             p = wait_for_bfd_packet(self)
1820             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1821             self.test_session.send_packet()
1822         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1823         self.assert_equal(len(self.vapi.collect_events()), 0,
1824                           "number of bfd events")
1825
1826     def test_auth_change_key_delayed(self):
1827         """ change auth key without disturbing session state (delayed) """
1828         key1 = self.factory.create_random_key(self)
1829         key1.add_vpp_config()
1830         key2 = self.factory.create_random_key(self)
1831         key2.add_vpp_config()
1832         self.vpp_session = VppBFDUDPSession(self, self.pg0,
1833                                             self.pg0.remote_ip4, sha1_key=key1)
1834         self.vpp_session.add_vpp_config()
1835         self.vpp_session.admin_up()
1836         self.test_session = BFDTestSession(
1837             self, self.pg0, AF_INET, sha1_key=key1,
1838             bfd_key_id=self.vpp_session.bfd_key_id)
1839         bfd_session_up(self)
1840         for dummy in range(self.test_session.detect_mult * 2):
1841             p = wait_for_bfd_packet(self)
1842             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1843             self.test_session.send_packet()
1844         self.vpp_session.activate_auth(key2, delayed=True)
1845         for dummy in range(self.test_session.detect_mult * 2):
1846             p = wait_for_bfd_packet(self)
1847             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1848             self.test_session.send_packet()
1849         self.test_session.bfd_key_id = self.vpp_session.bfd_key_id
1850         self.test_session.sha1_key = key2
1851         self.test_session.send_packet()
1852         for dummy in range(self.test_session.detect_mult * 2):
1853             p = wait_for_bfd_packet(self)
1854             self.assert_equal(p[BFD].state, BFDState.up, BFDState)
1855             self.test_session.send_packet()
1856         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
1857         self.assert_equal(len(self.vapi.collect_events()), 0,
1858                           "number of bfd events")
1859
1860 if __name__ == '__main__':
1861     unittest.main(testRunner=VppTestRunner)