blob: d047b5a3b99da23615c6017082ee5d715938b368 [file] [log] [blame]
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001#!/usr/bin/env python
2
3import unittest
4import time
5from random import randint
6from bfd import *
7from framework import *
8from util import ppp
9
Klement Sekera3e0a3562016-12-19 09:05:21 +010010us_in_sec = 1000000
11
Klement Sekera0e3c0de2016-09-29 14:43:44 +020012
Klement Sekerae4504c62016-12-08 10:16:41 +010013class BFDAPITestCase(VppTestCase):
14 """Bidirectional Forwarding Detection (BFD) - API"""
Klement Sekera0e3c0de2016-09-29 14:43:44 +020015
16 @classmethod
17 def setUpClass(cls):
Klement Sekerae4504c62016-12-08 10:16:41 +010018 super(BFDAPITestCase, cls).setUpClass()
Klement Sekera0e3c0de2016-09-29 14:43:44 +020019
20 try:
Klement Sekera10db26f2017-01-11 08:16:53 +010021 cls.create_pg_interfaces(range(2))
22 for i in cls.pg_interfaces:
23 i.config_ip4()
24 i.resolve_arp()
Klement Sekera0e3c0de2016-09-29 14:43:44 +020025
26 except Exception:
Klement Sekerae4504c62016-12-08 10:16:41 +010027 super(BFDAPITestCase, cls).tearDownClass()
Klement Sekera0e3c0de2016-09-29 14:43:44 +020028 raise
29
30 def test_add_bfd(self):
31 """ create a BFD session """
32 session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
33 session.add_vpp_config()
34 self.logger.debug("Session state is %s" % str(session.state))
35 session.remove_vpp_config()
36 session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
37 session.add_vpp_config()
38 self.logger.debug("Session state is %s" % str(session.state))
39 session.remove_vpp_config()
40
41 def test_double_add(self):
42 """ create the same BFD session twice (negative case) """
43 session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
44 session.add_vpp_config()
Klement Sekerae0545ef2017-01-25 08:00:40 +010045
46 with self.vapi.expect_negative_api_retval():
Klement Sekera0e3c0de2016-09-29 14:43:44 +020047 session.add_vpp_config()
Klement Sekerae0545ef2017-01-25 08:00:40 +010048
Klement Sekera0e3c0de2016-09-29 14:43:44 +020049 session.remove_vpp_config()
Klement Sekera0e3c0de2016-09-29 14:43:44 +020050
Klement Sekera10db26f2017-01-11 08:16:53 +010051 def test_add_two(self):
52 """ create two BFD sessions """
53 session1 = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
54 session1.add_vpp_config()
55 session2 = VppBFDUDPSession(self, self.pg1, self.pg1.remote_ip4)
56 session2.add_vpp_config()
57 self.assertNotEqual(session1.bs_index, session2.bs_index,
58 "Different BFD sessions share bs_index (%s)" %
59 session1.bs_index)
60
Klement Sekera0e3c0de2016-09-29 14:43:44 +020061
Klement Sekera0e3c0de2016-09-29 14:43:44 +020062class BFDTestSession(object):
Klement Sekera3e0a3562016-12-19 09:05:21 +010063 """ BFD session as seen from test framework side """
Klement Sekera0e3c0de2016-09-29 14:43:44 +020064
Klement Sekera46a87ad2017-01-02 08:22:23 +010065 def __init__(self, test, interface, af, detect_mult=3):
Klement Sekera0e3c0de2016-09-29 14:43:44 +020066 self.test = test
Klement Sekera46a87ad2017-01-02 08:22:23 +010067 self.af = af
Klement Sekera0e3c0de2016-09-29 14:43:44 +020068 self.interface = interface
Klement Sekera46a87ad2017-01-02 08:22:23 +010069 self.udp_sport = 50000
Klement Sekera0e3c0de2016-09-29 14:43:44 +020070 self.bfd_values = {
71 'my_discriminator': 0,
Klement Sekerae4504c62016-12-08 10:16:41 +010072 'desired_min_tx_interval': 100000,
Klement Sekera0e3c0de2016-09-29 14:43:44 +020073 'detect_mult': detect_mult,
74 'diag': BFDDiagCode.no_diagnostic,
75 }
76
77 def update(self, **kwargs):
78 self.bfd_values.update(kwargs)
79
80 def create_packet(self):
Klement Sekera46a87ad2017-01-02 08:22:23 +010081 if self.af == AF_INET6:
82 packet = (Ether(src=self.interface.remote_mac,
83 dst=self.interface.local_mac) /
84 IPv6(src=self.interface.remote_ip6,
85 dst=self.interface.local_ip6,
86 hlim=255) /
87 UDP(sport=self.udp_sport, dport=BFD.udp_dport) /
88 BFD())
89 else:
90 packet = (Ether(src=self.interface.remote_mac,
91 dst=self.interface.local_mac) /
92 IP(src=self.interface.remote_ip4,
93 dst=self.interface.local_ip4,
94 ttl=255) /
95 UDP(sport=self.udp_sport, dport=BFD.udp_dport) /
96 BFD())
Klement Sekera3e0a3562016-12-19 09:05:21 +010097 self.test.logger.debug("BFD: Creating packet")
Klement Sekera0e3c0de2016-09-29 14:43:44 +020098 for name, value in self.bfd_values.iteritems():
Klement Sekera3e0a3562016-12-19 09:05:21 +010099 self.test.logger.debug("BFD: setting packet.%s=%s", name, value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200100 packet[BFD].setfieldval(name, value)
101 return packet
102
103 def send_packet(self):
104 p = self.create_packet()
105 self.test.logger.debug(ppp("Sending packet:", p))
106 self.test.pg0.add_stream([p])
Klement Sekera9225dee2016-12-12 08:36:58 +0100107 self.test.pg_start()
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200108
109 def verify_packet(self, packet):
110 """ Verify correctness of BFD layer. """
111 bfd = packet[BFD]
112 self.test.assert_equal(bfd.version, 1, "BFD version")
113 self.test.assert_equal(bfd.your_discriminator,
114 self.bfd_values['my_discriminator'],
115 "BFD - your discriminator")
116
117
Klement Sekera46a87ad2017-01-02 08:22:23 +0100118class BFDCommonCode:
119 """Common code used by both IPv4 and IPv6 Test Cases"""
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200120
121 def tearDown(self):
Klement Sekerae4504c62016-12-08 10:16:41 +0100122 self.vapi.collect_events() # clear the event queue
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200123 if not self.vpp_dead:
Klement Sekera3e0a3562016-12-19 09:05:21 +0100124 self.vapi.want_bfd_events(enable_disable=0)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200125 self.vpp_session.remove_vpp_config()
Klement Sekera46a87ad2017-01-02 08:22:23 +0100126
127 def bfd_session_up(self):
128 self.pg_enable_capture([self.pg0])
129 self.logger.info("BFD: Waiting for slow hello")
130 p, timeout = self.wait_for_bfd_packet(2)
131 self.logger.info("BFD: Sending Init")
132 self.test_session.update(my_discriminator=randint(0, 40000000),
133 your_discriminator=p[BFD].my_discriminator,
134 state=BFDState.init,
135 required_min_rx_interval=100000)
136 self.test_session.send_packet()
137 self.logger.info("BFD: Waiting for event")
138 e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
139 self.verify_event(e, expected_state=BFDState.up)
140 self.logger.info("BFD: Session is Up")
141 self.test_session.update(state=BFDState.up)
142
143 def verify_ip(self, packet):
144 """ Verify correctness of IP layer. """
145 if self.vpp_session.af == AF_INET6:
146 ip = packet[IPv6]
147 local_ip = self.pg0.local_ip6
148 remote_ip = self.pg0.remote_ip6
149 self.assert_equal(ip.hlim, 255, "IPv6 hop limit")
150 else:
151 ip = packet[IP]
152 local_ip = self.pg0.local_ip4
153 remote_ip = self.pg0.remote_ip4
154 self.assert_equal(ip.ttl, 255, "IPv4 TTL")
155 self.assert_equal(ip.src, local_ip, "IP source address")
156 self.assert_equal(ip.dst, remote_ip, "IP destination address")
157
158 def verify_udp(self, packet):
159 """ Verify correctness of UDP layer. """
160 udp = packet[UDP]
161 self.assert_equal(udp.dport, BFD.udp_dport, "UDP destination port")
162 self.assert_in_range(udp.sport, BFD.udp_sport_min, BFD.udp_sport_max,
163 "UDP source port")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200164
165 def verify_event(self, event, expected_state):
166 """ Verify correctness of event values. """
167 e = event
Klement Sekerae4504c62016-12-08 10:16:41 +0100168 self.logger.debug("BFD: Event: %s" % repr(e))
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200169 self.assert_equal(e.bs_index, self.vpp_session.bs_index,
170 "BFD session index")
Klement Sekera10db26f2017-01-11 08:16:53 +0100171 self.assert_equal(
172 e.sw_if_index,
173 self.vpp_session.interface.sw_if_index,
174 "BFD interface index")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200175 is_ipv6 = 0
176 if self.vpp_session.af == AF_INET6:
177 is_ipv6 = 1
178 self.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6")
179 if self.vpp_session.af == AF_INET:
180 self.assert_equal(e.local_addr[:4], self.vpp_session.local_addr_n,
181 "Local IPv4 address")
182 self.assert_equal(e.peer_addr[:4], self.vpp_session.peer_addr_n,
183 "Peer IPv4 address")
184 else:
185 self.assert_equal(e.local_addr, self.vpp_session.local_addr_n,
186 "Local IPv6 address")
187 self.assert_equal(e.peer_addr, self.vpp_session.peer_addr_n,
188 "Peer IPv6 address")
189 self.assert_equal(e.state, expected_state, BFDState)
190
191 def wait_for_bfd_packet(self, timeout=1):
Klement Sekera3e0a3562016-12-19 09:05:21 +0100192 """ wait for BFD packet
193
194 :param timeout: how long to wait max
195
196 :returns: tuple (packet, time spent waiting for packet)
197 """
Klement Sekerae4504c62016-12-08 10:16:41 +0100198 self.logger.info("BFD: Waiting for BFD packet")
Klement Sekera3e0a3562016-12-19 09:05:21 +0100199 before = time.time()
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200200 p = self.pg0.wait_for_packet(timeout=timeout)
Klement Sekera3e0a3562016-12-19 09:05:21 +0100201 after = time.time()
Klement Sekera46a87ad2017-01-02 08:22:23 +0100202 self.logger.debug(ppp("Got packet:", p))
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200203 bfd = p[BFD]
204 if bfd is None:
205 raise Exception(ppp("Unexpected or invalid BFD packet:", p))
206 if bfd.payload:
207 raise Exception(ppp("Unexpected payload in BFD packet:", bfd))
Klement Sekera46a87ad2017-01-02 08:22:23 +0100208 self.verify_ip(p)
209 self.verify_udp(p)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200210 self.test_session.verify_packet(p)
Klement Sekera3e0a3562016-12-19 09:05:21 +0100211 return p, after - before
212
Klement Sekera3e0a3562016-12-19 09:05:21 +0100213 def test_session_up(self):
214 """ bring BFD session up """
215 self.bfd_session_up()
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200216
Klement Sekera46a87ad2017-01-02 08:22:23 +0100217 def test_hold_up(self):
218 """ hold BFD session up """
219 self.bfd_session_up()
220 for i in range(5):
221 self.wait_for_bfd_packet()
222 self.test_session.send_packet()
223
224
225class BFD4TestCase(VppTestCase, BFDCommonCode):
226 """Bidirectional Forwarding Detection (BFD)"""
227
228 @classmethod
229 def setUpClass(cls):
230 super(BFD4TestCase, cls).setUpClass()
231 try:
232 cls.create_pg_interfaces([0])
233 cls.pg0.config_ip4()
234 cls.pg0.generate_remote_hosts()
235 cls.pg0.configure_ipv4_neighbors()
236 cls.pg0.admin_up()
237 cls.pg0.resolve_arp()
238
239 except Exception:
240 super(BFD4TestCase, cls).tearDownClass()
241 raise
242
243 def setUp(self):
244 super(BFD4TestCase, self).setUp()
245 self.vapi.want_bfd_events()
246 try:
247 self.vpp_session = VppBFDUDPSession(self, self.pg0,
248 self.pg0.remote_ip4)
249 self.vpp_session.add_vpp_config()
250 self.vpp_session.admin_up()
251 self.test_session = BFDTestSession(self, self.pg0, AF_INET)
252 except:
253 self.vapi.want_bfd_events(enable_disable=0)
254 raise
255
256 def tearDown(self):
257 BFDCommonCode.tearDown(self)
258 super(BFD4TestCase, self).tearDown()
259
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200260 def test_slow_timer(self):
Klement Sekerae4504c62016-12-08 10:16:41 +0100261 """ verify slow periodic control frames while session down """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200262 self.pg_enable_capture([self.pg0])
Klement Sekerae4504c62016-12-08 10:16:41 +0100263 expected_packets = 3
264 self.logger.info("BFD: Waiting for %d BFD packets" % expected_packets)
Klement Sekeradab231a2016-12-21 08:50:14 +0100265 self.wait_for_bfd_packet(2)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200266 for i in range(expected_packets):
267 before = time.time()
Klement Sekeradab231a2016-12-21 08:50:14 +0100268 self.wait_for_bfd_packet(2)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200269 after = time.time()
Klement Sekerae4504c62016-12-08 10:16:41 +0100270 # spec says the range should be <0.75, 1>, allow extra 0.05 margin
271 # to work around timing issues
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200272 self.assert_in_range(
Klement Sekerae4504c62016-12-08 10:16:41 +0100273 after - before, 0.70, 1.05, "time between slow packets")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200274 before = after
275
276 def test_zero_remote_min_rx(self):
Klement Sekerae4504c62016-12-08 10:16:41 +0100277 """ no packets when zero BFD RemoteMinRxInterval """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200278 self.pg_enable_capture([self.pg0])
Klement Sekera3e0a3562016-12-19 09:05:21 +0100279 p, timeout = self.wait_for_bfd_packet(2)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200280 self.test_session.update(my_discriminator=randint(0, 40000000),
281 your_discriminator=p[BFD].my_discriminator,
282 state=BFDState.init,
283 required_min_rx_interval=0)
284 self.test_session.send_packet()
285 e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
286 self.verify_event(e, expected_state=BFDState.up)
287
288 try:
289 p = self.pg0.wait_for_packet(timeout=1)
290 except:
291 return
292 raise Exception(ppp("Received unexpected BFD packet:", p))
293
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200294 def test_conn_down(self):
Klement Sekerae4504c62016-12-08 10:16:41 +0100295 """ verify session goes down after inactivity """
296 self.bfd_session_up()
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200297 self.wait_for_bfd_packet()
Klement Sekerae4504c62016-12-08 10:16:41 +0100298 self.assert_equal(len(self.vapi.collect_events()), 0,
299 "number of bfd events")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200300 self.wait_for_bfd_packet()
Klement Sekerae4504c62016-12-08 10:16:41 +0100301 self.assert_equal(len(self.vapi.collect_events()), 0,
302 "number of bfd events")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200303 e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
304 self.verify_event(e, expected_state=BFDState.down)
305
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200306 def test_large_required_min_rx(self):
Klement Sekera637b9c42016-12-08 05:19:14 +0100307 """ large remote RequiredMinRxInterval """
Klement Sekerae4504c62016-12-08 10:16:41 +0100308 self.bfd_session_up()
Klement Sekera637b9c42016-12-08 05:19:14 +0100309 interval = 3000000
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200310 self.test_session.update(required_min_rx_interval=interval)
311 self.test_session.send_packet()
312 now = time.time()
Klement Sekera637b9c42016-12-08 05:19:14 +0100313 count = 0
Klement Sekera3e0a3562016-12-19 09:05:21 +0100314 while time.time() < now + interval / us_in_sec:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200315 try:
316 p = self.wait_for_bfd_packet()
317 if count > 1:
318 self.logger.error(ppp("Received unexpected packet:", p))
319 count += 1
320 except:
321 pass
Klement Sekera8e8b8112016-12-13 08:14:07 +0100322 self.assert_in_range(count, 0, 1, "number of packets received")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200323
Klement Sekera3e0a3562016-12-19 09:05:21 +0100324 def test_immediate_remote_min_rx_reduce(self):
325 """ immediately honor remote min rx reduction """
326 self.vpp_session.remove_vpp_config()
Klement Sekera10db26f2017-01-11 08:16:53 +0100327 self.vpp_session = VppBFDUDPSession(
328 self, self.pg0, self.pg0.remote_ip4, desired_min_tx=10000)
Klement Sekera3e0a3562016-12-19 09:05:21 +0100329 self.vpp_session.add_vpp_config()
330 self.test_session.update(desired_min_tx_interval=1000000,
331 required_min_rx_interval=1000000)
332 self.bfd_session_up()
333 self.wait_for_bfd_packet()
334 interval = 100000
335 self.test_session.update(required_min_rx_interval=interval)
336 self.test_session.send_packet()
337 p, ttp = self.wait_for_bfd_packet()
338 # allow extra 10% to work around timing issues, first packet is special
339 self.assert_in_range(ttp, 0, 1.10 * interval / us_in_sec,
340 "time between BFD packets")
341 p, ttp = self.wait_for_bfd_packet()
342 self.assert_in_range(ttp, .9 * .75 * interval / us_in_sec,
343 1.10 * interval / us_in_sec,
344 "time between BFD packets")
345 p, ttp = self.wait_for_bfd_packet()
346 self.assert_in_range(ttp, .9 * .75 * interval / us_in_sec,
347 1.10 * interval / us_in_sec,
348 "time between BFD packets")
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200349
Klement Sekera46a87ad2017-01-02 08:22:23 +0100350
351class BFD6TestCase(VppTestCase, BFDCommonCode):
352 """Bidirectional Forwarding Detection (BFD) (IPv6) """
353
354 @classmethod
355 def setUpClass(cls):
356 super(BFD6TestCase, cls).setUpClass()
357 try:
358 cls.create_pg_interfaces([0])
359 cls.pg0.config_ip6()
360 cls.pg0.configure_ipv6_neighbors()
361 cls.pg0.admin_up()
362 cls.pg0.resolve_ndp()
363
364 except Exception:
365 super(BFD6TestCase, cls).tearDownClass()
366 raise
367
368 def setUp(self):
369 super(BFD6TestCase, self).setUp()
370 self.vapi.want_bfd_events()
371 try:
372 self.vpp_session = VppBFDUDPSession(self, self.pg0,
373 self.pg0.remote_ip6,
374 af=AF_INET6)
375 self.vpp_session.add_vpp_config()
376 self.vpp_session.admin_up()
377 self.test_session = BFDTestSession(self, self.pg0, AF_INET6)
378 self.logger.debug(self.vapi.cli("show adj nbr"))
379 except:
380 self.vapi.want_bfd_events(enable_disable=0)
381 raise
382
383 def tearDown(self):
384 BFDCommonCode.tearDown(self)
385 super(BFD6TestCase, self).tearDown()
386
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200387if __name__ == '__main__':
388 unittest.main(testRunner=VppTestRunner)