blob: f7a0662bf015e8734283b24516c85950899569d1 [file] [log] [blame]
Matthew Smith39e94282020-02-11 11:25:32 -06001#!/usr/bin/env python3
2
3#
4# Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
5#
6# SPDX-License-Identifier: Apache-2.0
7#
8
9import unittest
10import time
Paul Vinciguerra582eac52020-04-03 12:18:40 -040011import socket
12from socket import inet_pton, inet_ntop
Matthew Smith39e94282020-02-11 11:25:32 -060013
14from vpp_object import VppObject
15from vpp_papi import VppEnum
16
Paul Vinciguerra582eac52020-04-03 12:18:40 -040017from scapy.packet import raw
Matthew Smith39e94282020-02-11 11:25:32 -060018from scapy.layers.l2 import Ether, ARP
19from scapy.layers.inet import IP, ICMP, icmptypes
20from scapy.layers.inet6 import IPv6, ipv6nh, IPv6ExtHdrHopByHop, \
21 ICMPv6MLReport2, ICMPv6ND_NA, ICMPv6ND_NS, ICMPv6NDOptDstLLAddr, \
22 ICMPv6NDOptSrcLLAddr, ICMPv6EchoRequest, ICMPv6EchoReply
23from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mr, IGMPv3gr
24from scapy.layers.vrrp import IPPROTO_VRRP, VRRPv3
25from scapy.utils6 import in6_getnsma, in6_getnsmac
Klement Sekerab23ffd72021-05-31 16:08:53 +020026from config import config
27from framework import VppTestCase, VppTestRunner
Matthew Smith39e94282020-02-11 11:25:32 -060028from util import ip6_normalize
29
30VRRP_VR_FLAG_PREEMPT = 1
31VRRP_VR_FLAG_ACCEPT = 2
32VRRP_VR_FLAG_UNICAST = 4
33VRRP_VR_FLAG_IPV6 = 8
34
35VRRP_VR_STATE_INIT = 0
36VRRP_VR_STATE_BACKUP = 1
37VRRP_VR_STATE_MASTER = 2
38VRRP_VR_STATE_INTF_DOWN = 3
39
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +020040VRRP_INDEX_INVALID = 0xffffffff
41
Matthew Smith39e94282020-02-11 11:25:32 -060042
43def is_non_arp(p):
44 """ Want to filter out advertisements, igmp, etc"""
45 if p.haslayer(ARP):
46 return False
47
48 return True
49
50
51def is_not_adv(p):
52 """ Filter out everything but advertisements. E.g. multicast RD/ND """
53 if p.haslayer(VRRPv3):
54 return False
55
56 return True
57
58
59def is_not_echo_reply(p):
60 """ filter out advertisements and other while waiting for echo reply """
61 if p.haslayer(IP) and p.haslayer(ICMP):
62 if icmptypes[p[ICMP].type] == "echo-reply":
63 return False
64 elif p.haslayer(IPv6) and p.haslayer(ICMPv6EchoReply):
65 return False
66
67 return True
68
69
70class VppVRRPVirtualRouter(VppObject):
71
72 def __init__(self,
73 test,
74 intf,
75 vr_id,
76 prio=100,
77 intvl=100,
78 flags=VRRP_VR_FLAG_PREEMPT,
79 vips=None):
80 self._test = test
81 self._intf = intf
82 self._sw_if_index = self._intf.sw_if_index
83 self._vr_id = vr_id
84 self._prio = prio
85 self._intvl = intvl
86 self._flags = flags
87 if (flags & VRRP_VR_FLAG_IPV6):
88 self._is_ipv6 = 1
89 self._adv_dest_mac = "33:33:00:00:00:12"
90 self._virtual_mac = "00:00:5e:00:02:%02x" % vr_id
91 self._adv_dest_ip = "ff02::12"
92 self._vips = ([intf.local_ip6] if vips is None else vips)
93 else:
94 self._is_ipv6 = 0
95 self._adv_dest_mac = "01:00:5e:00:00:12"
96 self._virtual_mac = "00:00:5e:00:01:%02x" % vr_id
97 self._adv_dest_ip = "224.0.0.18"
98 self._vips = ([intf.local_ip4] if vips is None else vips)
99 self._tracked_ifs = []
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200100 self._vrrp_index = VRRP_INDEX_INVALID
Matthew Smith39e94282020-02-11 11:25:32 -0600101
102 def add_vpp_config(self):
103 self._test.vapi.vrrp_vr_add_del(is_add=1,
104 sw_if_index=self._intf.sw_if_index,
105 vr_id=self._vr_id,
106 priority=self._prio,
107 interval=self._intvl,
108 flags=self._flags,
109 n_addrs=len(self._vips),
110 addrs=self._vips)
111
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200112 def update_vpp_config(self):
113 r = self._test.vapi.vrrp_vr_update(vrrp_index=self._vrrp_index,
114 sw_if_index=self._intf.sw_if_index,
115 vr_id=self._vr_id,
116 priority=self._prio,
117 interval=self._intvl,
118 flags=self._flags,
119 n_addrs=len(self._vips),
120 addrs=self._vips)
121 self._vrrp_index = r.vrrp_index
122
123 def delete_vpp_config(self):
124 self._test.vapi.vrrp_vr_del(vrrp_index=self._vrrp_index)
125
Matthew Smith39e94282020-02-11 11:25:32 -0600126 def query_vpp_config(self):
127 vrs = self._test.vapi.vrrp_vr_dump(sw_if_index=self._intf.sw_if_index)
128 for vr in vrs:
129 if vr.config.vr_id != self._vr_id:
130 continue
131
132 is_ipv6 = (1 if (vr.config.flags & VRRP_VR_FLAG_IPV6) else 0)
133 if is_ipv6 != self._is_ipv6:
134 continue
135
136 return vr
137
138 return None
139
140 def remove_vpp_config(self):
141 self._test.vapi.vrrp_vr_add_del(is_add=0,
142 sw_if_index=self._intf.sw_if_index,
143 vr_id=self._vr_id,
144 priority=self._prio,
145 interval=self._intvl,
146 flags=self._flags,
147 n_addrs=len(self._vips),
148 addrs=self._vips)
149
150 def start_stop(self, is_start):
151 self._test.vapi.vrrp_vr_start_stop(is_start=is_start,
152 sw_if_index=self._intf.sw_if_index,
153 vr_id=self._vr_id,
154 is_ipv6=self._is_ipv6)
155 self._start_time = (time.time() if is_start else None)
156
157 def add_del_tracked_interface(self, is_add, sw_if_index, prio):
158 args = {
159 'sw_if_index': self._intf.sw_if_index,
160 'is_ipv6': self._is_ipv6,
161 'vr_id': self._vr_id,
162 'is_add': is_add,
163 'n_ifs': 1,
164 'ifs': [{'sw_if_index': sw_if_index, 'priority': prio}]
165 }
166 self._test.vapi.vrrp_vr_track_if_add_del(**args)
167 self._tracked_ifs.append(args['ifs'][0])
168
169 def set_unicast_peers(self, addrs):
170 args = {
171 'sw_if_index': self._intf.sw_if_index,
172 'is_ipv6': self._is_ipv6,
173 'vr_id': self._vr_id,
174 'n_addrs': len(addrs),
175 'addrs': addrs
176 }
177 self._test.vapi.vrrp_vr_set_peers(**args)
178 self._unicast_peers = addrs
179
Matthew Smith39e94282020-02-11 11:25:32 -0600180 def start_time(self):
181 return self._start_time
182
183 def virtual_mac(self):
184 return self._virtual_mac
185
186 def virtual_ips(self):
187 return self._vips
188
189 def adv_dest_mac(self):
190 return self._adv_dest_mac
191
192 def adv_dest_ip(self):
193 return self._adv_dest_ip
194
195 def priority(self):
196 return self._prio
197
198 def vr_id(self):
199 return self._vr_id
200
201 def adv_interval(self):
202 return self._intvl
203
204 def interface(self):
205 return self._intf
206
207 def assert_state_equals(self, state):
208 vr_details = self.query_vpp_config()
209 self._test.assertEqual(vr_details.runtime.state, state)
210
211 def master_down_seconds(self):
212 vr_details = self.query_vpp_config()
213 return (vr_details.runtime.master_down_int * 0.01)
214
Paul Vinciguerraa5dd6d72021-04-15 11:37:44 -0400215 def vrrp_adv_packet(self, prio=None, src_ip=None):
216 dst_ip = self._adv_dest_ip
217 if prio is None:
218 prio = self._prio
219 eth = Ether(dst=self._adv_dest_mac, src=self._virtual_mac)
220 vrrp = VRRPv3(vrid=self._vr_id, priority=prio,
221 ipcount=len(self._vips), adv=self._intvl)
222 if self._is_ipv6:
223 src_ip = (self._intf.local_ip6_ll if src_ip is None else src_ip)
224 ip = IPv6(src=src_ip, dst=dst_ip, nh=IPPROTO_VRRP, hlim=255)
225 vrrp.addrlist = self._vips
226 else:
227 src_ip = (self._intf.local_ip4 if src_ip is None else src_ip)
228 ip = IP(src=src_ip, dst=dst_ip, proto=IPPROTO_VRRP, ttl=255, id=0)
229 vrrp.addrlist = self._vips
230
231 # Fill in default values & checksums
232 pkt = Ether(raw(eth / ip / vrrp))
233 return pkt
234
235
Klement Sekerab23ffd72021-05-31 16:08:53 +0200236@unittest.skipUnless(config.extended, "part of extended tests")
Rajaselvam8cc66b52021-06-30 11:20:20 +0530237class TestVRRP4(VppTestCase):
Matthew Smith39e94282020-02-11 11:25:32 -0600238 """ IPv4 VRRP Test Case """
239
240 @classmethod
241 def setUpClass(cls):
242 super(TestVRRP4, cls).setUpClass()
243
244 @classmethod
245 def tearDownClass(cls):
246 super(TestVRRP4, cls).tearDownClass()
247
248 def setUp(self):
249 super(TestVRRP4, self).setUp()
250
251 self.create_pg_interfaces(range(2))
252
253 for i in self.pg_interfaces:
254 i.admin_up()
255 i.config_ip4()
256 i.generate_remote_hosts(5)
257 i.configure_ipv4_neighbors()
258
259 self._vrs = []
260 self._default_flags = VRRP_VR_FLAG_PREEMPT
261 self._default_adv = 100
262
263 def tearDown(self):
264 for vr in self._vrs:
265 try:
266 vr_api = vr.query_vpp_config()
267 if vr_api.runtime.state != VRRP_VR_STATE_INIT:
268 vr.start_stop(is_start=0)
269 vr.remove_vpp_config()
270 except:
271 self.logger.error("Error cleaning up")
272
273 for i in self.pg_interfaces:
274 i.admin_down()
275 i.unconfig_ip4()
276 i.unconfig_ip6()
277
278 self._vrs = []
279
280 super(TestVRRP4, self).tearDown()
281
282 def verify_vrrp4_igmp(self, pkt):
283 ip = pkt[IP]
284 self.assertEqual(ip.dst, "224.0.0.22")
285 self.assertEqual(ip.proto, 2)
286
287 igmp = pkt[IGMPv3]
288 self.assertEqual(IGMPv3.igmpv3types[igmp.type],
289 "Version 3 Membership Report")
290
291 igmpmr = pkt[IGMPv3mr]
292 self.assertEqual(igmpmr.numgrp, 1)
293 self.assertEqual(igmpmr.records[0].maddr, "224.0.0.18")
294
295 def verify_vrrp4_garp(self, pkt, vip, vmac):
296 arp = pkt[ARP]
297
298 # ARP "who-has" op == 1
299 self.assertEqual(arp.op, 1)
300 self.assertEqual(arp.pdst, arp.psrc)
301 self.assertEqual(arp.pdst, vip)
302 self.assertEqual(arp.hwsrc, vmac)
303
304 def verify_vrrp4_adv(self, rx_pkt, vr, prio=None):
305 vips = vr.virtual_ips()
306 eth = rx_pkt[Ether]
307 ip = rx_pkt[IP]
308 vrrp = rx_pkt[VRRPv3]
309
Rajaselvam8cc66b52021-06-30 11:20:20 +0530310 pkt = vr.vrrp_adv_packet(prio=prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600311
312 # Source MAC is virtual MAC, destination is multicast MAC
313 self.assertEqual(eth.src, vr.virtual_mac())
314 self.assertEqual(eth.dst, vr.adv_dest_mac())
315
316 self.assertEqual(ip.dst, "224.0.0.18")
317 self.assertEqual(ip.ttl, 255)
318 self.assertEqual(ip.proto, IPPROTO_VRRP)
319
320 self.assertEqual(vrrp.version, 3)
321 self.assertEqual(vrrp.type, 1)
322 self.assertEqual(vrrp.vrid, vr.vr_id())
323 if prio is None:
324 prio = vr.priority()
325 self.assertEqual(vrrp.priority, prio)
326 self.assertEqual(vrrp.ipcount, len(vips))
327 self.assertEqual(vrrp.adv, vr.adv_interval())
328 self.assertListEqual(vrrp.addrlist, vips)
329
330 # VR with priority 255 owns the virtual address and should
331 # become master and start advertising immediately.
332 def test_vrrp4_master_adv(self):
333 """ IPv4 Master VR advertises """
334 self.pg_enable_capture(self.pg_interfaces)
335 self.pg_start()
336
337 prio = 255
338 intvl = self._default_adv
339 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
340 prio=prio, intvl=intvl,
341 flags=self._default_flags)
342
343 vr.add_vpp_config()
344 vr.start_stop(is_start=1)
345 self.logger.info(self.vapi.cli("show vrrp vr"))
346 vr.start_stop(is_start=0)
347 self.logger.info(self.vapi.cli("show vrrp vr"))
348
349 pkts = self.pg0.get_capture(4)
350
351 # Init -> Master: IGMP Join, VRRP adv, gratuitous ARP are sent
352 self.verify_vrrp4_igmp(pkts[0])
353 self.verify_vrrp4_adv(pkts[1], vr, prio=prio)
354 self.verify_vrrp4_garp(pkts[2], vr.virtual_ips()[0], vr.virtual_mac())
355 # Master -> Init: Adv with priority 0 sent to force an election
356 self.verify_vrrp4_adv(pkts[3], vr, prio=0)
357
358 vr.remove_vpp_config()
359 self._vrs = []
360
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200361 # Same as above but with the update API, and add a change
362 # of parameters to test that too
363 def test_vrrp4_master_adv_update(self):
364 """ IPv4 Master VR adv + Update to Backup """
365 self.pg_enable_capture(self.pg_interfaces)
366 self.pg_start()
367
368 prio = 255
369 intvl = self._default_adv
370 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
371 prio=prio, intvl=intvl,
372 flags=self._default_flags)
373
374 vr.update_vpp_config()
375 vr.start_stop(is_start=1)
376 self.logger.info(self.vapi.cli("show vrrp vr"))
377 # Update VR with lower prio and larger interval
378 # we need to keep old VR for the adv checks
379 upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100,
380 prio=100, intvl=2*intvl,
381 flags=self._default_flags,
382 vips=[self.pg0.remote_ip4])
383 upd_vr._vrrp_index = vr._vrrp_index
384 upd_vr.update_vpp_config()
385 start_time = time.time()
386 self.logger.info(self.vapi.cli("show vrrp vr"))
387 upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
388 self._vrs = [upd_vr]
389
390 pkts = self.pg0.get_capture(5)
391 # Init -> Master: IGMP Join, VRRP adv, gratuitous ARP are sent
392 self.verify_vrrp4_igmp(pkts[0])
393 self.verify_vrrp4_adv(pkts[1], vr, prio=prio)
394 self.verify_vrrp4_garp(pkts[2], vr.virtual_ips()[0], vr.virtual_mac())
395 # Master -> Init: Adv with priority 0 sent to force an election
396 self.verify_vrrp4_adv(pkts[3], vr, prio=0)
397 # Init -> Backup: An IGMP join should be sent
398 self.verify_vrrp4_igmp(pkts[4])
399
400 # send higher prio advertisements, should not receive any
401 end_time = start_time + 2 * upd_vr.master_down_seconds()
402 src_ip = self.pg0.remote_ip4
403 pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)]
404 while time.time() < end_time:
405 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl*0.01)
406 self.logger.info(self.vapi.cli("show trace"))
407
408 upd_vr.start_stop(is_start=0)
409 self.logger.info(self.vapi.cli("show vrrp vr"))
410
Matthew Smith39e94282020-02-11 11:25:32 -0600411 # VR with priority < 255 enters backup state and does not advertise as
412 # long as it receives higher priority advertisements
413 def test_vrrp4_backup_noadv(self):
414 """ IPv4 Backup VR does not advertise """
415 self.pg_enable_capture(self.pg_interfaces)
416 self.pg_start()
417
418 vr_id = 100
419 prio = 100
420 intvl = self._default_adv
421 intvl_s = intvl * 0.01
422 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
423 prio=prio, intvl=intvl,
424 flags=self._default_flags,
425 vips=[self.pg0.remote_ip4])
426 self._vrs.append(vr)
427 vr.add_vpp_config()
428
429 vr.start_stop(is_start=1)
430
431 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
432 # watch for advertisements for 2x the master down preemption timeout
433 end_time = vr.start_time() + 2 * vr.master_down_seconds()
434
435 # Init -> Backup: An IGMP join should be sent
436 pkts = self.pg0.get_capture(1)
437 self.verify_vrrp4_igmp(pkts[0])
438
439 # send higher prio advertisements, should not receive any
440 src_ip = self.pg0.remote_ip4
Rajaselvam8cc66b52021-06-30 11:20:20 +0530441 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -0600442 while time.time() < end_time:
443 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
444 self.logger.info(self.vapi.cli("show trace"))
445
446 vr.start_stop(is_start=0)
447 self.logger.info(self.vapi.cli("show vrrp vr"))
448 vr.remove_vpp_config()
449 self._vrs = []
450
451 def test_vrrp4_master_arp(self):
452 """ IPv4 Master VR replies to ARP """
453 self.pg_start()
454
455 # VR virtual IP is the default, which is the pg local IP
456 vr_id = 100
457 prio = 255
458 intvl = self._default_adv
459 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
460 prio=prio, intvl=intvl,
461 flags=self._default_flags)
462 self._vrs.append(vr)
463
464 vr.add_vpp_config()
465
466 # before the VR is up, ARP should resolve to interface MAC
467 self.pg0.resolve_arp()
468 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
469
470 # start the VR, ARP should now resolve to virtual MAC
471 vr.start_stop(is_start=1)
472 self.pg0.resolve_arp()
473 self.assertEqual(self.pg0.local_mac, vr.virtual_mac())
474
475 # stop the VR, ARP should resolve to interface MAC again
476 vr.start_stop(is_start=0)
477 self.pg0.resolve_arp()
478 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
479
480 vr.remove_vpp_config()
481 self._vrs = []
482
483 def test_vrrp4_backup_noarp(self):
484 """ IPv4 Backup VR ignores ARP """
485 # We need an address for a virtual IP that is not the IP that
486 # ARP requests will originate from
487
488 vr_id = 100
489 prio = 100
490 intvl = self._default_adv
491 vip = self.pg0.remote_hosts[1].ip4
492 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
493 prio=prio, intvl=intvl,
494 flags=self._default_flags,
495 vips=[vip])
496 self._vrs.append(vr)
497 vr.add_vpp_config()
498
499 arp_req = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
500 ARP(op=ARP.who_has, pdst=vip,
501 psrc=self.pg0.remote_ip4, hwsrc=self.pg0.remote_mac))
502
503 # Before the VR is started make sure no reply to request for VIP
504 self.pg_start()
505 self.pg_enable_capture(self.pg_interfaces)
506 self.send_and_assert_no_replies(self.pg0, [arp_req], timeout=1)
507
508 # VR should start in backup state and still should not reply to ARP
509 # send a higher priority adv to make sure it does not become master
Rajaselvam8cc66b52021-06-30 11:20:20 +0530510 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip4)
Matthew Smith39e94282020-02-11 11:25:32 -0600511 vr.start_stop(is_start=1)
512 self.send_and_assert_no_replies(self.pg0, [adv, arp_req], timeout=1)
513
514 vr.start_stop(is_start=0)
515 vr.remove_vpp_config()
516 self._vrs = []
517
518 def test_vrrp4_election(self):
519 """ IPv4 Backup VR becomes master if no advertisements received """
520
521 vr_id = 100
522 prio = 100
523 intvl = self._default_adv
524 intvl_s = intvl * 0.01
525 vip = self.pg0.remote_ip4
526 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
527 prio=prio, intvl=intvl,
528 flags=self._default_flags,
529 vips=[vip])
530 self._vrs.append(vr)
531 vr.add_vpp_config()
532
533 # After adding the VR, it should be in the init state
534 vr.assert_state_equals(VRRP_VR_STATE_INIT)
535
536 self.pg_start()
537 vr.start_stop(is_start=1)
538
539 # VR should be in backup state after starting
540 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
541 end_time = vr.start_time() + vr.master_down_seconds()
542
543 # should not receive adverts until timer expires & state transition
544 self.pg_enable_capture(self.pg_interfaces)
545 while (time.time() + intvl_s) < end_time:
546 time.sleep(intvl_s)
547 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
548
549 # VR should be in master state, should send an adv
550 self.pg0.enable_capture()
551 self.pg0.wait_for_packet(intvl_s, is_not_adv)
552 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
553
554 def test_vrrp4_backup_preempts(self):
555 """ IPv4 Backup VR preempts lower priority master """
556
557 vr_id = 100
558 prio = 100
559 intvl = self._default_adv
560 intvl_s = intvl * 0.01
561 vip = self.pg0.remote_ip4
562 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
563 prio=prio, intvl=intvl,
564 flags=self._default_flags,
565 vips=[vip])
566 self._vrs.append(vr)
567 vr.add_vpp_config()
568
569 # After adding the VR, it should be in the init state
570 vr.assert_state_equals(VRRP_VR_STATE_INIT)
571
572 self.pg_start()
573 vr.start_stop(is_start=1)
574
575 # VR should be in backup state after starting
576 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
577 end_time = vr.start_time() + vr.master_down_seconds()
578
579 # send lower prio advertisements until timer expires
580 src_ip = self.pg0.remote_ip4
Rajaselvam8cc66b52021-06-30 11:20:20 +0530581 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -0600582 while time.time() + intvl_s < end_time:
583 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
584 self.logger.info(self.vapi.cli("show trace"))
585
586 # when timer expires, VR should take over as master
587 self.pg0.enable_capture()
588 self.pg0.wait_for_packet(timeout=intvl_s, filter_out_fn=is_not_adv)
589 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
590
591 def test_vrrp4_master_preempted(self):
592 """ IPv4 Master VR preempted by higher priority backup """
593
594 # A prio 255 VR cannot be preempted so the prio has to be lower and
595 # we have to wait for it to take over
596 vr_id = 100
597 prio = 100
598 intvl = self._default_adv
599 vip = self.pg0.remote_ip4
600 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
601 prio=prio, intvl=intvl,
602 flags=self._default_flags,
603 vips=[vip])
604 self._vrs.append(vr)
605 vr.add_vpp_config()
606
607 # After adding the VR, it should be in the init state
608 vr.assert_state_equals(VRRP_VR_STATE_INIT)
609
610 # start VR
611 vr.start_stop(is_start=1)
612 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
613
614 # wait for VR to take over as master
615 end_time = vr.start_time() + vr.master_down_seconds()
616 sleep_s = end_time - time.time()
617 time.sleep(sleep_s)
618 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
619
620 # Build advertisement packet and send it
Rajaselvam8cc66b52021-06-30 11:20:20 +0530621 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip4)]
Matthew Smith39e94282020-02-11 11:25:32 -0600622 self.pg_send(self.pg0, pkts)
623
624 # VR should be in backup state again
625 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
626
627 def test_vrrp4_accept_mode_disabled(self):
628 """ IPv4 Master VR does not reply for VIP w/ accept mode off """
629
630 # accept mode only matters when prio < 255, so it will have to
631 # come up as a backup and take over as master after the timeout
632 vr_id = 100
633 prio = 100
634 intvl = self._default_adv
635 vip = self.pg0.remote_hosts[4].ip4
636 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
637 prio=prio, intvl=intvl,
638 flags=self._default_flags,
639 vips=[vip])
640 self._vrs.append(vr)
641 vr.add_vpp_config()
642
643 # After adding the VR, it should be in the init state
644 vr.assert_state_equals(VRRP_VR_STATE_INIT)
645
646 # start VR
647 vr.start_stop(is_start=1)
648 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
649
650 # wait for VR to take over as master
651 end_time = vr.start_time() + vr.master_down_seconds()
652 sleep_s = end_time - time.time()
653 time.sleep(sleep_s)
654 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
655
656 # send an ICMP echo to the VR virtual IP address
657 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
658 IP(dst=vip, src=self.pg0.remote_ip4) /
659 ICMP(seq=1, id=self.pg0.sw_if_index, type='echo-request'))
660 self.pg_send(self.pg0, [echo])
661
662 # wait for an echo reply. none should be received
663 time.sleep(1)
664 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
665
666 def test_vrrp4_accept_mode_enabled(self):
667 """ IPv4 Master VR replies for VIP w/ accept mode on """
668
669 # A prio 255 VR cannot be preempted so the prio has to be lower and
670 # we have to wait for it to take over
671 vr_id = 100
672 prio = 100
673 intvl = self._default_adv
674 vip = self.pg0.remote_hosts[4].ip4
675 flags = (VRRP_VR_FLAG_PREEMPT | VRRP_VR_FLAG_ACCEPT)
676 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
677 prio=prio, intvl=intvl,
678 flags=flags,
679 vips=[vip])
680 self._vrs.append(vr)
681 vr.add_vpp_config()
682
683 # After adding the VR, it should be in the init state
684 vr.assert_state_equals(VRRP_VR_STATE_INIT)
685
686 # start VR
687 vr.start_stop(is_start=1)
688 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
689
690 # wait for VR to take over as master
691 end_time = vr.start_time() + vr.master_down_seconds()
692 sleep_s = end_time - time.time()
693 time.sleep(sleep_s)
694 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
695
696 # send an ICMP echo to the VR virtual IP address
697 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
698 IP(dst=vip, src=self.pg0.remote_ip4) /
699 ICMP(seq=1, id=self.pg0.sw_if_index, type='echo-request'))
700 self.pg_send(self.pg0, [echo])
701
702 # wait for an echo reply.
703 time.sleep(1)
704 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
705 filter_out_fn=is_not_echo_reply)
706
707 self.assertEqual(rx_pkts[0][IP].src, vip)
708 self.assertEqual(rx_pkts[0][IP].dst, self.pg0.remote_ip4)
709 self.assertEqual(icmptypes[rx_pkts[0][ICMP].type], "echo-reply")
710 self.assertEqual(rx_pkts[0][ICMP].seq, 1)
711 self.assertEqual(rx_pkts[0][ICMP].id, self.pg0.sw_if_index)
712
713 def test_vrrp4_intf_tracking(self):
714 """ IPv4 Master VR adjusts priority based on tracked interface """
715
716 vr_id = 100
717 prio = 255
718 intvl = self._default_adv
719 intvl_s = intvl * 0.01
720 vip = self.pg0.local_ip4
721 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
722 prio=prio, intvl=intvl,
723 flags=self._default_flags,
724 vips=[vip])
725 self._vrs.append(vr)
726 vr.add_vpp_config()
727
728 # After adding the VR, it should be in the init state
729 vr.assert_state_equals(VRRP_VR_STATE_INIT)
730
731 # add pg1 as a tracked interface and start the VR
732 adjustment = 50
733 adjusted_prio = prio - adjustment
734 vr.add_del_tracked_interface(is_add=1,
735 sw_if_index=self.pg1.sw_if_index,
736 prio=adjustment)
737 vr.start_stop(is_start=1)
738 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
739
Rajaselvam8cc66b52021-06-30 11:20:20 +0530740 adv_configured = vr.vrrp_adv_packet(prio=prio)
741 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600742
743 # tracked intf is up -> advertised priority == configured priority
744 self.pg0.enable_capture()
745 rx = self.pg0.wait_for_packet(timeout=intvl_s,
746 filter_out_fn=is_not_adv)
747 self.assertEqual(rx, adv_configured)
748
749 # take down pg1, verify priority is now being adjusted
750 self.pg1.admin_down()
751 self.pg0.enable_capture()
752 rx = self.pg0.wait_for_packet(timeout=intvl_s,
753 filter_out_fn=is_not_adv)
754 self.assertEqual(rx, adv_adjusted)
755
756 # bring up pg1, verify priority now matches configured value
757 self.pg1.admin_up()
758 self.pg0.enable_capture()
759 rx = self.pg0.wait_for_packet(timeout=intvl_s,
760 filter_out_fn=is_not_adv)
761 self.assertEqual(rx, adv_configured)
762
763 # remove IP address from pg1, verify priority now being adjusted
764 self.pg1.unconfig_ip4()
765 self.pg0.enable_capture()
766 rx = self.pg0.wait_for_packet(timeout=intvl_s,
767 filter_out_fn=is_not_adv)
768 self.assertEqual(rx, adv_adjusted)
769
770 # add IP address to pg1, verify priority now matches configured value
771 self.pg1.config_ip4()
772 self.pg0.enable_capture()
773 rx = self.pg0.wait_for_packet(timeout=intvl_s,
774 filter_out_fn=is_not_adv)
775 self.assertEqual(rx, adv_configured)
776
777 def test_vrrp4_master_adv_unicast(self):
778 """ IPv4 Master VR advertises (unicast) """
779
780 vr_id = 100
781 prio = 255
782 intvl = self._default_adv
783 intvl_s = intvl * 0.01
784 vip = self.pg0.local_ip4
785 flags = (self._default_flags | VRRP_VR_FLAG_UNICAST)
786 unicast_peer = self.pg0.remote_hosts[4]
787 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
788 prio=prio, intvl=intvl,
789 flags=flags,
790 vips=[vip])
791 self._vrs.append(vr)
792 vr.add_vpp_config()
793 vr.set_unicast_peers([unicast_peer.ip4])
794
795 # After adding the VR, it should be in the init state
796 vr.assert_state_equals(VRRP_VR_STATE_INIT)
797
798 # Start VR, transition to master
799 vr.start_stop(is_start=1)
800 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
801
802 self.pg0.enable_capture()
803 rx = self.pg0.wait_for_packet(timeout=intvl_s,
804 filter_out_fn=is_not_adv)
805
806 self.assertTrue(rx.haslayer(Ether))
807 self.assertTrue(rx.haslayer(IP))
808 self.assertTrue(rx.haslayer(VRRPv3))
809 self.assertEqual(rx[Ether].src, self.pg0.local_mac)
810 self.assertEqual(rx[Ether].dst, unicast_peer.mac)
811 self.assertEqual(rx[IP].src, self.pg0.local_ip4)
812 self.assertEqual(rx[IP].dst, unicast_peer.ip4)
813 self.assertEqual(rx[VRRPv3].vrid, vr_id)
814 self.assertEqual(rx[VRRPv3].priority, prio)
815 self.assertEqual(rx[VRRPv3].ipcount, 1)
816 self.assertEqual(rx[VRRPv3].addrlist, [vip])
817
818
Klement Sekerab23ffd72021-05-31 16:08:53 +0200819@unittest.skipUnless(config.extended, "part of extended tests")
Rajaselvam8cc66b52021-06-30 11:20:20 +0530820class TestVRRP6(VppTestCase):
Matthew Smith39e94282020-02-11 11:25:32 -0600821 """ IPv6 VRRP Test Case """
822
823 @classmethod
824 def setUpClass(cls):
825 super(TestVRRP6, cls).setUpClass()
826
827 @classmethod
828 def tearDownClass(cls):
829 super(TestVRRP6, cls).tearDownClass()
830
831 def setUp(self):
832 super(TestVRRP6, self).setUp()
833
834 self.create_pg_interfaces(range(2))
835
836 for i in self.pg_interfaces:
837 i.admin_up()
838 i.config_ip6()
839 i.generate_remote_hosts(5)
840 i.configure_ipv6_neighbors()
841
842 self._vrs = []
843 self._default_flags = (VRRP_VR_FLAG_IPV6 | VRRP_VR_FLAG_PREEMPT)
844 self._default_adv = 100
845
846 def tearDown(self):
847 for vr in self._vrs:
848 try:
849 vr_api = vr.query_vpp_config()
850 if vr_api.runtime.state != VRRP_VR_STATE_INIT:
851 vr.start_stop(is_start=0)
852 vr.remove_vpp_config()
853 except:
854 self.logger.error("Error cleaning up")
855
856 for i in self.pg_interfaces:
857 i.admin_down()
858 i.unconfig_ip4()
859 i.unconfig_ip6()
860
861 self._vrs = []
862
863 super(TestVRRP6, self).tearDown()
864
865 def verify_vrrp6_mlr(self, pkt, vr):
866 ip6 = pkt[IPv6]
867 self.assertEqual(ip6.dst, "ff02::16")
868 self.assertEqual(ipv6nh[ip6.nh], "Hop-by-Hop Option Header")
869
870 hbh = pkt[IPv6ExtHdrHopByHop]
871 self.assertEqual(ipv6nh[hbh.nh], "ICMPv6")
872
873 self.assertTrue(pkt.haslayer(ICMPv6MLReport2))
874 mlr = pkt[ICMPv6MLReport2]
875 # should contain mc addr records for:
876 # - VRRPv3 multicast addr
877 # - solicited node mc addr record for each VR virtual IPv6 address
878 vips = vr.virtual_ips()
879 self.assertEqual(mlr.records_number, len(vips) + 1)
880 self.assertEqual(mlr.records[0].dst, vr.adv_dest_ip())
881
882 def verify_vrrp6_adv(self, rx_pkt, vr, prio=None):
883 self.assertTrue(rx_pkt.haslayer(Ether))
884 self.assertTrue(rx_pkt.haslayer(IPv6))
885 self.assertTrue(rx_pkt.haslayer(VRRPv3))
886
887 # generate a packet for this VR and compare it to the one received
Rajaselvam8cc66b52021-06-30 11:20:20 +0530888 pkt = vr.vrrp_adv_packet(prio=prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600889 self.assertTrue(rx_pkt.haslayer(Ether))
890 self.assertTrue(rx_pkt.haslayer(IPv6))
891 self.assertTrue(rx_pkt.haslayer(VRRPv3))
892
893 self.assertEqual(pkt, rx_pkt)
894
895 def verify_vrrp6_gna(self, pkt, vr):
896 self.assertTrue(pkt.haslayer(Ether))
897 self.assertTrue(pkt.haslayer(IPv6))
898 self.assertTrue(pkt.haslayer(ICMPv6ND_NA))
899 self.assertTrue(pkt.haslayer(ICMPv6NDOptDstLLAddr))
900
901 self.assertEqual(pkt[Ether].dst, "33:33:00:00:00:01")
902
903 self.assertEqual(pkt[IPv6].dst, "ff02::1")
904 # convert addrs to packed format since string versions could differ
Paul Vinciguerra582eac52020-04-03 12:18:40 -0400905 src_addr = inet_pton(socket.AF_INET6, pkt[IPv6].src)
906 vr_ll_addr = inet_pton(socket.AF_INET6, vr.interface().local_ip6_ll)
Matthew Smith39e94282020-02-11 11:25:32 -0600907 self.assertEqual(src_addr, vr_ll_addr)
908
909 self.assertTrue(pkt[ICMPv6ND_NA].tgt in vr.virtual_ips())
910 self.assertEqual(pkt[ICMPv6NDOptDstLLAddr].lladdr, vr.virtual_mac())
911
912 # VR with priority 255 owns the virtual address and should
913 # become master and start advertising immediately.
914 def test_vrrp6_master_adv(self):
915 """ IPv6 Master VR advertises """
916 self.pg_enable_capture(self.pg_interfaces)
917 self.pg_start()
918
919 prio = 255
920 intvl = self._default_adv
921 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
922 prio=prio, intvl=intvl,
923 flags=self._default_flags)
924 self._vrs.append(vr)
925
926 vr.add_vpp_config()
927 self.logger.info(self.vapi.cli("show vrrp vr"))
928 vr.start_stop(is_start=1)
929 self.logger.info(self.vapi.cli("show vrrp vr"))
930 vr.start_stop(is_start=0)
931 self.logger.info(self.vapi.cli("show vrrp vr"))
932
933 pkts = self.pg0.get_capture(4, filter_out_fn=None)
934
935 # Init -> Master: Multicast group Join, VRRP adv, gratuitous NAs sent
936 self.verify_vrrp6_mlr(pkts[0], vr)
937 self.verify_vrrp6_adv(pkts[1], vr, prio=prio)
938 self.verify_vrrp6_gna(pkts[2], vr)
939 # Master -> Init: Adv with priority 0 sent to force an election
940 self.verify_vrrp6_adv(pkts[3], vr, prio=0)
941
942 vr.remove_vpp_config()
943 self._vrs = []
944
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200945 # Same as above but with the update API, and add a change
946 # of parameters to test that too
947 def test_vrrp6_master_adv_update(self):
948 """ IPv6 Master VR adv + Update to Backup """
949 self.pg_enable_capture(self.pg_interfaces)
950 self.pg_start()
951
952 prio = 255
953 intvl = self._default_adv
954 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
955 prio=prio, intvl=intvl,
956 flags=self._default_flags)
957
958 vr.update_vpp_config()
959 vr.start_stop(is_start=1)
960 self.logger.info(self.vapi.cli("show vrrp vr"))
961 # Update VR with lower prio and larger interval
962 # we need to keep old VR for the adv checks
963 upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100,
964 prio=100, intvl=2*intvl,
965 flags=self._default_flags,
966 vips=[self.pg0.remote_ip6])
967 upd_vr._vrrp_index = vr._vrrp_index
968 upd_vr.update_vpp_config()
969 start_time = time.time()
970 self.logger.info(self.vapi.cli("show vrrp vr"))
971 upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
972 self._vrs = [upd_vr]
973
974 pkts = self.pg0.get_capture(5, filter_out_fn=None)
975
976 # Init -> Master: Multicast group Join, VRRP adv, gratuitous NAs sent
977 self.verify_vrrp6_mlr(pkts[0], vr)
978 self.verify_vrrp6_adv(pkts[1], vr, prio=prio)
979 self.verify_vrrp6_gna(pkts[2], vr)
980 # Master -> Init: Adv with priority 0 sent to force an election
981 self.verify_vrrp6_adv(pkts[3], vr, prio=0)
982 # Init -> Backup: A multicast listener report should be sent
983 # not actually verified in the test below, where I took this from
984
985 # send higher prio advertisements, should not see VPP send any
986 src_ip = self.pg0.remote_ip6_ll
987 pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)]
988 self.logger.info(self.vapi.cli("show vlib graph"))
989 end_time = start_time + 2 * upd_vr.master_down_seconds()
990 while time.time() < end_time:
991 self.send_and_assert_no_replies(
992 self.pg0, pkts, timeout=0.01*upd_vr._intvl)
993 self.logger.info(self.vapi.cli("show trace"))
994
995 vr.start_stop(is_start=0)
996 self.logger.info(self.vapi.cli("show vrrp vr"))
997
Matthew Smith39e94282020-02-11 11:25:32 -0600998 # VR with priority < 255 enters backup state and does not advertise as
999 # long as it receives higher priority advertisements
1000 def test_vrrp6_backup_noadv(self):
1001 """ IPv6 Backup VR does not advertise """
1002 self.pg_enable_capture(self.pg_interfaces)
1003 self.pg_start()
1004
1005 vr_id = 100
1006 prio = 100
1007 intvl = self._default_adv
1008 intvl_s = intvl * 0.01
1009 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1010 prio=prio, intvl=intvl,
1011 flags=self._default_flags,
1012 vips=[self.pg0.remote_ip6])
1013 vr.add_vpp_config()
1014 self._vrs.append(vr)
1015
1016 vr.start_stop(is_start=1)
1017
1018 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1019 # watch for advertisements for 2x the master down preemption timeout
1020 end_time = vr.start_time() + 2 * vr.master_down_seconds()
1021
1022 # Init -> Backup: A multicast listener report should be sent
1023 pkts = self.pg0.get_capture(1, filter_out_fn=None)
1024
1025 # send higher prio advertisements, should not see VPP send any
1026 src_ip = self.pg0.remote_ip6_ll
1027 num_advs = 5
Rajaselvam8cc66b52021-06-30 11:20:20 +05301028 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -06001029 self.logger.info(self.vapi.cli("show vlib graph"))
1030 while time.time() < end_time:
1031 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1032 self.logger.info(self.vapi.cli("show trace"))
1033 num_advs -= 1
1034
1035 vr.start_stop(is_start=0)
1036 self.logger.info(self.vapi.cli("show vrrp vr"))
1037 vr.remove_vpp_config()
1038 self._vrs = []
1039
1040 def test_vrrp6_master_nd(self):
1041 """ IPv6 Master VR replies to NDP """
1042 self.pg_start()
1043
1044 # VR virtual IP is the default, which is the pg local IP
1045 vr_id = 100
1046 prio = 255
1047 intvl = self._default_adv
1048 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
1049 prio=prio, intvl=intvl,
1050 flags=self._default_flags)
1051 vr.add_vpp_config()
1052 self._vrs.append(vr)
1053
1054 # before the VR is up, NDP should resolve to interface MAC
1055 self.pg0.resolve_ndp()
1056 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
1057
1058 # start the VR, NDP should now resolve to virtual MAC
1059 vr.start_stop(is_start=1)
1060 self.pg0.resolve_ndp()
1061 self.assertEqual(self.pg0.local_mac, vr.virtual_mac())
1062
1063 # stop the VR, ARP should resolve to interface MAC again
1064 vr.start_stop(is_start=0)
1065 self.pg0.resolve_ndp()
1066 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
1067
1068 vr.remove_vpp_config()
1069 self._vrs = []
1070
1071 def test_vrrp6_backup_nond(self):
1072 """ IPv6 Backup VR ignores NDP """
1073 # We need an address for a virtual IP that is not the IP that
1074 # ARP requests will originate from
1075
1076 vr_id = 100
1077 prio = 100
1078 intvl = self._default_adv
1079 intvl_s = intvl * 0.01
1080 vip = self.pg0.remote_hosts[1].ip6
1081 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1082 prio=prio, intvl=intvl,
1083 flags=self._default_flags,
1084 vips=[vip])
1085 vr.add_vpp_config()
1086 self._vrs.append(vr)
1087
1088 nsma = in6_getnsma(inet_pton(socket.AF_INET6, vip))
1089 dmac = in6_getnsmac(nsma)
1090 dst_ip = inet_ntop(socket.AF_INET6, nsma)
1091
1092 ndp_req = (Ether(dst=dmac, src=self.pg0.remote_mac) /
1093 IPv6(dst=dst_ip, src=self.pg0.remote_ip6) /
1094 ICMPv6ND_NS(tgt=vip) /
1095 ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
1096
1097 # Before the VR is started make sure no reply to request for VIP
1098 self.send_and_assert_no_replies(self.pg0, [ndp_req], timeout=1)
1099
1100 # VR should start in backup state and still should not reply to NDP
1101 # send a higher priority adv to make sure it does not become master
Rajaselvam8cc66b52021-06-30 11:20:20 +05301102 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip6)
Matthew Smith39e94282020-02-11 11:25:32 -06001103 pkts = [adv, ndp_req]
1104 vr.start_stop(is_start=1)
1105 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1106
1107 vr.start_stop(is_start=0)
1108
1109 def test_vrrp6_election(self):
1110 """ IPv6 Backup VR becomes master if no advertisements received """
1111
1112 vr_id = 100
1113 prio = 100
1114 intvl = self._default_adv
1115 intvl_s = intvl * 0.01
1116 vip = self.pg0.remote_ip6
1117 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1118 prio=prio, intvl=intvl,
1119 flags=self._default_flags,
1120 vips=[vip])
1121 self._vrs.append(vr)
1122 vr.add_vpp_config()
1123
1124 # After adding the VR, it should be in the init state
1125 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1126
1127 self.pg_start()
1128 vr.start_stop(is_start=1)
1129
1130 # VR should be in backup state after starting
1131 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1132 end_time = vr.start_time() + vr.master_down_seconds()
1133
1134 # no advertisements should arrive until timer expires
1135 self.pg0.enable_capture()
1136 while (time.time() + intvl_s) < end_time:
1137 time.sleep(intvl_s)
1138 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
1139
1140 # VR should be in master state after timer expires
1141 self.pg0.enable_capture()
1142 self.pg0.wait_for_packet(intvl_s, is_not_adv)
1143 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1144
1145 def test_vrrp6_backup_preempts(self):
1146 """ IPv6 Backup VR preempts lower priority master """
1147
1148 vr_id = 100
1149 prio = 100
1150 intvl = self._default_adv
1151 intvl_s = intvl * 0.01
1152 vip = self.pg0.remote_ip6
1153 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1154 prio=prio, intvl=intvl,
1155 flags=self._default_flags,
1156 vips=[vip])
1157 self._vrs.append(vr)
1158 vr.add_vpp_config()
1159
1160 # After adding the VR, it should be in the init state
1161 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1162
1163 self.pg_start()
1164 vr.start_stop(is_start=1)
1165
1166 # VR should be in backup state after starting
1167 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1168 end_time = vr.start_time() + vr.master_down_seconds()
1169
1170 # send lower prio advertisements until timer expires
1171 src_ip = self.pg0.remote_ip6
Rajaselvam8cc66b52021-06-30 11:20:20 +05301172 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -06001173 while (time.time() + intvl_s) < end_time:
1174 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1175 self.logger.info(self.vapi.cli("show trace"))
1176
1177 # when timer expires, VR should take over as master
1178 self.pg0.enable_capture()
1179 self.pg0.wait_for_packet(timeout=intvl_s, filter_out_fn=is_not_adv)
1180 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1181
1182 def test_vrrp6_master_preempted(self):
1183 """ IPv6 Master VR preempted by higher priority backup """
1184
1185 # A prio 255 VR cannot be preempted so the prio has to be lower and
1186 # we have to wait for it to take over
1187 vr_id = 100
1188 prio = 100
1189 intvl = self._default_adv
1190 vip = self.pg0.remote_ip6
1191 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1192 prio=prio, intvl=intvl,
1193 flags=self._default_flags,
1194 vips=[vip])
1195 self._vrs.append(vr)
1196 vr.add_vpp_config()
1197
1198 # After adding the VR, it should be in the init state
1199 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1200
1201 # start VR
1202 vr.start_stop(is_start=1)
1203 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1204
1205 # wait for VR to take over as master
1206 end_time = vr.start_time() + vr.master_down_seconds()
1207 sleep_s = end_time - time.time()
1208 time.sleep(sleep_s)
1209 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1210
1211 # Build advertisement packet and send it
Rajaselvam8cc66b52021-06-30 11:20:20 +05301212 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip6)]
Matthew Smith39e94282020-02-11 11:25:32 -06001213 self.pg_send(self.pg0, pkts)
1214
1215 # VR should be in backup state again
1216 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1217
1218 def test_vrrp6_accept_mode_disabled(self):
1219 """ IPv6 Master VR does not reply for VIP w/ accept mode off """
1220
1221 # accept mode only matters when prio < 255, so it will have to
1222 # come up as a backup and take over as master after the timeout
1223 vr_id = 100
1224 prio = 100
1225 intvl = self._default_adv
1226 vip = self.pg0.remote_hosts[4].ip6
1227 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1228 prio=prio, intvl=intvl,
1229 flags=self._default_flags,
1230 vips=[vip])
1231 self._vrs.append(vr)
1232 vr.add_vpp_config()
1233
1234 # After adding the VR, it should be in the init state
1235 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1236
1237 # start VR
1238 vr.start_stop(is_start=1)
1239 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1240
1241 # wait for VR to take over as master
1242 end_time = vr.start_time() + vr.master_down_seconds()
1243 sleep_s = end_time - time.time()
1244 time.sleep(sleep_s)
1245 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1246
1247 # send an ICMPv6 echo to the VR virtual IP address
1248 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
1249 IPv6(dst=vip, src=self.pg0.remote_ip6) /
1250 ICMPv6EchoRequest(seq=1, id=self.pg0.sw_if_index))
1251 self.pg_send(self.pg0, [echo])
1252
1253 # wait for an echo reply. none should be received
1254 time.sleep(1)
1255 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
1256
1257 def test_vrrp6_accept_mode_enabled(self):
1258 """ IPv6 Master VR replies for VIP w/ accept mode on """
1259
1260 # A prio 255 VR cannot be preempted so the prio has to be lower and
1261 # we have to wait for it to take over
1262 vr_id = 100
1263 prio = 100
1264 intvl = self._default_adv
1265 vip = self.pg0.remote_hosts[4].ip6
1266 flags = (self._default_flags | VRRP_VR_FLAG_ACCEPT)
1267 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1268 prio=prio, intvl=intvl,
1269 flags=flags,
1270 vips=[vip])
1271 self._vrs.append(vr)
1272 vr.add_vpp_config()
1273
1274 # After adding the VR, it should be in the init state
1275 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1276
1277 # start VR
1278 vr.start_stop(is_start=1)
1279 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1280
1281 # wait for VR to take over as master
1282 end_time = vr.start_time() + vr.master_down_seconds()
1283 sleep_s = end_time - time.time()
1284 time.sleep(sleep_s)
1285 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1286
1287 # send an ICMP echo to the VR virtual IP address
1288 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
1289 IPv6(dst=vip, src=self.pg0.remote_ip6) /
1290 ICMPv6EchoRequest(seq=1, id=self.pg0.sw_if_index))
1291 self.pg_send(self.pg0, [echo])
1292
1293 # wait for an echo reply.
1294 time.sleep(1)
1295 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
1296 filter_out_fn=is_not_echo_reply)
1297
1298 self.assertEqual(rx_pkts[0][IPv6].src, vip)
1299 self.assertEqual(rx_pkts[0][IPv6].dst, self.pg0.remote_ip6)
1300 self.assertEqual(rx_pkts[0][ICMPv6EchoReply].seq, 1)
1301 self.assertEqual(rx_pkts[0][ICMPv6EchoReply].id, self.pg0.sw_if_index)
1302
1303 def test_vrrp6_intf_tracking(self):
1304 """ IPv6 Master VR adjusts priority based on tracked interface """
1305
1306 vr_id = 100
1307 prio = 255
1308 intvl = self._default_adv
1309 intvl_s = intvl * 0.01
1310 vip = self.pg0.local_ip6
1311 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1312 prio=prio, intvl=intvl,
1313 flags=self._default_flags,
1314 vips=[vip])
1315 self._vrs.append(vr)
1316 vr.add_vpp_config()
1317
1318 # After adding the VR, it should be in the init state
1319 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1320
1321 # add pg1 as a tracked interface and start the VR
1322 adjustment = 50
1323 adjusted_prio = prio - adjustment
1324 vr.add_del_tracked_interface(is_add=1,
1325 sw_if_index=self.pg1.sw_if_index,
1326 prio=adjustment)
1327 vr.start_stop(is_start=1)
1328 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1329
Rajaselvam8cc66b52021-06-30 11:20:20 +05301330 adv_configured = vr.vrrp_adv_packet(prio=prio)
1331 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
Matthew Smith39e94282020-02-11 11:25:32 -06001332
1333 # tracked intf is up -> advertised priority == configured priority
1334 self.pg0.enable_capture()
1335 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1336 filter_out_fn=is_not_adv)
1337 self.assertEqual(rx, adv_configured)
1338
1339 # take down pg1, verify priority is now being adjusted
1340 self.pg1.admin_down()
1341 self.pg0.enable_capture()
1342 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1343 filter_out_fn=is_not_adv)
1344 self.assertEqual(rx, adv_adjusted)
1345
1346 # bring up pg1, verify priority now matches configured value
1347 self.pg1.admin_up()
1348 self.pg0.enable_capture()
1349 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1350 filter_out_fn=is_not_adv)
1351 self.assertEqual(rx, adv_configured)
1352
1353 # remove IP address from pg1, verify priority now being adjusted
1354 self.pg1.unconfig_ip6()
1355 self.pg0.enable_capture()
1356 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1357 filter_out_fn=is_not_adv)
1358 self.assertEqual(rx, adv_adjusted)
1359
1360 # add IP address to pg1, verify priority now matches configured value
1361 self.pg1.config_ip6()
1362 self.pg0.enable_capture()
1363 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1364 filter_out_fn=is_not_adv)
1365 self.assertEqual(rx, adv_configured)
1366
1367 def test_vrrp6_master_adv_unicast(self):
1368 """ IPv6 Master VR advertises (unicast) """
1369
1370 vr_id = 100
1371 prio = 255
1372 intvl = self._default_adv
1373 intvl_s = intvl * 0.01
1374 vip = self.pg0.local_ip6
1375 flags = (self._default_flags | VRRP_VR_FLAG_UNICAST)
1376 unicast_peer = self.pg0.remote_hosts[4]
1377 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1378 prio=prio, intvl=intvl,
1379 flags=flags,
1380 vips=[vip])
1381 self._vrs.append(vr)
1382 vr.add_vpp_config()
1383 vr.set_unicast_peers([unicast_peer.ip6])
1384
1385 # After adding the VR, it should be in the init state
1386 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1387
1388 # Start VR, transition to master
1389 vr.start_stop(is_start=1)
1390 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1391
1392 self.pg0.enable_capture()
1393 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1394 filter_out_fn=is_not_adv)
1395
1396 self.assertTrue(rx.haslayer(Ether))
1397 self.assertTrue(rx.haslayer(IPv6))
1398 self.assertTrue(rx.haslayer(VRRPv3))
1399 self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1400 self.assertEqual(rx[Ether].dst, unicast_peer.mac)
1401 self.assertEqual(ip6_normalize(rx[IPv6].src),
1402 ip6_normalize(self.pg0.local_ip6_ll))
1403 self.assertEqual(ip6_normalize(rx[IPv6].dst),
1404 ip6_normalize(unicast_peer.ip6))
1405 self.assertEqual(rx[VRRPv3].vrid, vr_id)
1406 self.assertEqual(rx[VRRPv3].priority, prio)
1407 self.assertEqual(rx[VRRPv3].ipcount, 1)
1408 self.assertEqual(rx[VRRPv3].addrlist, [vip])
1409
1410
1411if __name__ == '__main__':
1412 unittest.main(testRunner=VppTestRunner)