4 # Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
6 # SPDX-License-Identifier: Apache-2.0
12 from socket import inet_pton, inet_ntop
14 from vpp_object import VppObject
15 from vpp_papi import VppEnum
17 from scapy.packet import raw
18 from scapy.layers.l2 import Ether, ARP
19 from scapy.layers.inet import IP, ICMP, icmptypes
20 from scapy.layers.inet6 import IPv6, ipv6nh, IPv6ExtHdrHopByHop, \
21 ICMPv6MLReport2, ICMPv6ND_NA, ICMPv6ND_NS, ICMPv6NDOptDstLLAddr, \
22 ICMPv6NDOptSrcLLAddr, ICMPv6EchoRequest, ICMPv6EchoReply
23 from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mr, IGMPv3gr
24 from scapy.layers.vrrp import IPPROTO_VRRP, VRRPv3
25 from scapy.utils6 import in6_getnsma, in6_getnsmac
26 from config import config
27 from framework import VppTestCase, VppTestRunner
28 from util import ip6_normalize
30 VRRP_VR_FLAG_PREEMPT = 1
31 VRRP_VR_FLAG_ACCEPT = 2
32 VRRP_VR_FLAG_UNICAST = 4
35 VRRP_VR_STATE_INIT = 0
36 VRRP_VR_STATE_BACKUP = 1
37 VRRP_VR_STATE_MASTER = 2
38 VRRP_VR_STATE_INTF_DOWN = 3
40 VRRP_INDEX_INVALID = 0xffffffff
44 """ Want to filter out advertisements, igmp, etc"""
52 """ Filter out everything but advertisements. E.g. multicast RD/ND """
53 if p.haslayer(VRRPv3):
59 def 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":
64 elif p.haslayer(IPv6) and p.haslayer(ICMPv6EchoReply):
70 class VppVRRPVirtualRouter(VppObject):
78 flags=VRRP_VR_FLAG_PREEMPT,
82 self._sw_if_index = self._intf.sw_if_index
87 if (flags & VRRP_VR_FLAG_IPV6):
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)
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 = []
100 self._vrrp_index = VRRP_INDEX_INVALID
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,
107 interval=self._intvl,
109 n_addrs=len(self._vips),
112 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,
117 interval=self._intvl,
119 n_addrs=len(self._vips),
121 self._vrrp_index = r.vrrp_index
123 def delete_vpp_config(self):
124 self._test.vapi.vrrp_vr_del(vrrp_index=self._vrrp_index)
126 def query_vpp_config(self):
127 vrs = self._test.vapi.vrrp_vr_dump(sw_if_index=self._intf.sw_if_index)
129 if vr.config.vr_id != self._vr_id:
132 is_ipv6 = (1 if (vr.config.flags & VRRP_VR_FLAG_IPV6) else 0)
133 if is_ipv6 != self._is_ipv6:
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,
145 interval=self._intvl,
147 n_addrs=len(self._vips),
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,
154 is_ipv6=self._is_ipv6)
155 self._start_time = (time.time() if is_start else None)
157 def add_del_tracked_interface(self, is_add, sw_if_index, prio):
159 'sw_if_index': self._intf.sw_if_index,
160 'is_ipv6': self._is_ipv6,
161 'vr_id': self._vr_id,
164 'ifs': [{'sw_if_index': sw_if_index, 'priority': prio}]
166 self._test.vapi.vrrp_vr_track_if_add_del(**args)
167 self._tracked_ifs.append(args['ifs'][0])
169 def set_unicast_peers(self, addrs):
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),
177 self._test.vapi.vrrp_vr_set_peers(**args)
178 self._unicast_peers = addrs
180 def start_time(self):
181 return self._start_time
183 def virtual_mac(self):
184 return self._virtual_mac
186 def virtual_ips(self):
189 def adv_dest_mac(self):
190 return self._adv_dest_mac
192 def adv_dest_ip(self):
193 return self._adv_dest_ip
201 def adv_interval(self):
207 def assert_state_equals(self, state):
208 vr_details = self.query_vpp_config()
209 self._test.assertEqual(vr_details.runtime.state, state)
211 def master_down_seconds(self):
212 vr_details = self.query_vpp_config()
213 return (vr_details.runtime.master_down_int * 0.01)
215 def vrrp_adv_packet(self, prio=None, src_ip=None):
216 dst_ip = self._adv_dest_ip
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)
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
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
231 # Fill in default values & checksums
232 pkt = Ether(raw(eth / ip / vrrp))
236 @unittest.skipUnless(config.extended, "part of extended tests")
237 class TestVRRP4(VppTestCase):
238 """ IPv4 VRRP Test Case """
242 super(TestVRRP4, cls).setUpClass()
245 def tearDownClass(cls):
246 super(TestVRRP4, cls).tearDownClass()
249 super(TestVRRP4, self).setUp()
251 self.create_pg_interfaces(range(2))
253 for i in self.pg_interfaces:
256 i.generate_remote_hosts(5)
257 i.configure_ipv4_neighbors()
260 self._default_flags = VRRP_VR_FLAG_PREEMPT
261 self._default_adv = 100
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()
271 self.logger.error("Error cleaning up")
273 for i in self.pg_interfaces:
280 super(TestVRRP4, self).tearDown()
282 def verify_vrrp4_igmp(self, pkt):
284 self.assertEqual(ip.dst, "224.0.0.22")
285 self.assertEqual(ip.proto, 2)
288 self.assertEqual(IGMPv3.igmpv3types[igmp.type],
289 "Version 3 Membership Report")
291 igmpmr = pkt[IGMPv3mr]
292 self.assertEqual(igmpmr.numgrp, 1)
293 self.assertEqual(igmpmr.records[0].maddr, "224.0.0.18")
295 def verify_vrrp4_garp(self, pkt, vip, vmac):
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)
304 def verify_vrrp4_adv(self, rx_pkt, vr, prio=None):
305 vips = vr.virtual_ips()
308 vrrp = rx_pkt[VRRPv3]
310 pkt = vr.vrrp_adv_packet(prio=prio)
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())
316 self.assertEqual(ip.dst, "224.0.0.18")
317 self.assertEqual(ip.ttl, 255)
318 self.assertEqual(ip.proto, IPPROTO_VRRP)
320 self.assertEqual(vrrp.version, 3)
321 self.assertEqual(vrrp.type, 1)
322 self.assertEqual(vrrp.vrid, vr.vr_id())
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)
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)
338 intvl = self._default_adv
339 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
340 prio=prio, intvl=intvl,
341 flags=self._default_flags)
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"))
349 pkts = self.pg0.get_capture(4)
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)
358 vr.remove_vpp_config()
361 # 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)
369 intvl = self._default_adv
370 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
371 prio=prio, intvl=intvl,
372 flags=self._default_flags)
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)
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])
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"))
408 upd_vr.start_stop(is_start=0)
409 self.logger.info(self.vapi.cli("show vrrp vr"))
411 # 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)
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])
429 vr.start_stop(is_start=1)
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()
435 # Init -> Backup: An IGMP join should be sent
436 pkts = self.pg0.get_capture(1)
437 self.verify_vrrp4_igmp(pkts[0])
439 # send higher prio advertisements, should not receive any
440 src_ip = self.pg0.remote_ip4
441 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
442 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"))
446 vr.start_stop(is_start=0)
447 self.logger.info(self.vapi.cli("show vrrp vr"))
448 vr.remove_vpp_config()
451 def test_vrrp4_master_arp(self):
452 """ IPv4 Master VR replies to ARP """
455 # VR virtual IP is the default, which is the pg local IP
458 intvl = self._default_adv
459 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
460 prio=prio, intvl=intvl,
461 flags=self._default_flags)
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())
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())
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())
480 vr.remove_vpp_config()
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
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,
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))
503 # Before the VR is started make sure no reply to request for VIP
505 self.pg_enable_capture(self.pg_interfaces)
506 self.send_and_assert_no_replies(self.pg0, [arp_req], timeout=1)
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
510 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip4)
511 vr.start_stop(is_start=1)
512 self.send_and_assert_no_replies(self.pg0, [adv, arp_req], timeout=1)
514 vr.start_stop(is_start=0)
515 vr.remove_vpp_config()
518 def test_vrrp4_election(self):
519 """ IPv4 Backup VR becomes master if no advertisements received """
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,
533 # After adding the VR, it should be in the init state
534 vr.assert_state_equals(VRRP_VR_STATE_INIT)
537 vr.start_stop(is_start=1)
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()
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:
547 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
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)
554 def test_vrrp4_backup_preempts(self):
555 """ IPv4 Backup VR preempts lower priority master """
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,
569 # After adding the VR, it should be in the init state
570 vr.assert_state_equals(VRRP_VR_STATE_INIT)
573 vr.start_stop(is_start=1)
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()
579 # send lower prio advertisements until timer expires
580 src_ip = self.pg0.remote_ip4
581 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
582 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"))
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)
591 def test_vrrp4_master_preempted(self):
592 """ IPv4 Master VR preempted by higher priority backup """
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
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,
607 # After adding the VR, it should be in the init state
608 vr.assert_state_equals(VRRP_VR_STATE_INIT)
611 vr.start_stop(is_start=1)
612 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
618 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
620 # Build advertisement packet and send it
621 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip4)]
622 self.pg_send(self.pg0, pkts)
624 # VR should be in backup state again
625 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
627 def test_vrrp4_accept_mode_disabled(self):
628 """ IPv4 Master VR does not reply for VIP w/ accept mode off """
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
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,
643 # After adding the VR, it should be in the init state
644 vr.assert_state_equals(VRRP_VR_STATE_INIT)
647 vr.start_stop(is_start=1)
648 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
654 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
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])
662 # wait for an echo reply. none should be received
664 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
666 def test_vrrp4_accept_mode_enabled(self):
667 """ IPv4 Master VR replies for VIP w/ accept mode on """
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
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,
683 # After adding the VR, it should be in the init state
684 vr.assert_state_equals(VRRP_VR_STATE_INIT)
687 vr.start_stop(is_start=1)
688 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
694 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
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])
702 # wait for an echo reply.
704 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
705 filter_out_fn=is_not_echo_reply)
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)
713 def test_vrrp4_intf_tracking(self):
714 """ IPv4 Master VR adjusts priority based on tracked interface """
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,
728 # After adding the VR, it should be in the init state
729 vr.assert_state_equals(VRRP_VR_STATE_INIT)
731 # add pg1 as a tracked interface and start the VR
733 adjusted_prio = prio - adjustment
734 vr.add_del_tracked_interface(is_add=1,
735 sw_if_index=self.pg1.sw_if_index,
737 vr.start_stop(is_start=1)
738 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
740 adv_configured = vr.vrrp_adv_packet(prio=prio)
741 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
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)
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)
756 # bring up pg1, verify priority now matches configured value
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)
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)
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)
777 def test_vrrp4_master_adv_unicast(self):
778 """ IPv4 Master VR advertises (unicast) """
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,
793 vr.set_unicast_peers([unicast_peer.ip4])
795 # After adding the VR, it should be in the init state
796 vr.assert_state_equals(VRRP_VR_STATE_INIT)
798 # Start VR, transition to master
799 vr.start_stop(is_start=1)
800 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
802 self.pg0.enable_capture()
803 rx = self.pg0.wait_for_packet(timeout=intvl_s,
804 filter_out_fn=is_not_adv)
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])
819 @unittest.skipUnless(config.extended, "part of extended tests")
820 class TestVRRP6(VppTestCase):
821 """ IPv6 VRRP Test Case """
825 super(TestVRRP6, cls).setUpClass()
828 def tearDownClass(cls):
829 super(TestVRRP6, cls).tearDownClass()
832 super(TestVRRP6, self).setUp()
834 self.create_pg_interfaces(range(2))
836 for i in self.pg_interfaces:
839 i.generate_remote_hosts(5)
840 i.configure_ipv6_neighbors()
843 self._default_flags = (VRRP_VR_FLAG_IPV6 | VRRP_VR_FLAG_PREEMPT)
844 self._default_adv = 100
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()
854 self.logger.error("Error cleaning up")
856 for i in self.pg_interfaces:
863 super(TestVRRP6, self).tearDown()
865 def verify_vrrp6_mlr(self, pkt, vr):
867 self.assertEqual(ip6.dst, "ff02::16")
868 self.assertEqual(ipv6nh[ip6.nh], "Hop-by-Hop Option Header")
870 hbh = pkt[IPv6ExtHdrHopByHop]
871 self.assertEqual(ipv6nh[hbh.nh], "ICMPv6")
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())
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))
887 # generate a packet for this VR and compare it to the one received
888 pkt = vr.vrrp_adv_packet(prio=prio)
889 self.assertTrue(rx_pkt.haslayer(Ether))
890 self.assertTrue(rx_pkt.haslayer(IPv6))
891 self.assertTrue(rx_pkt.haslayer(VRRPv3))
893 self.assertEqual(pkt, rx_pkt)
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))
901 self.assertEqual(pkt[Ether].dst, "33:33:00:00:00:01")
903 self.assertEqual(pkt[IPv6].dst, "ff02::1")
904 # convert addrs to packed format since string versions could differ
905 src_addr = inet_pton(socket.AF_INET6, pkt[IPv6].src)
906 vr_ll_addr = inet_pton(socket.AF_INET6, vr.interface().local_ip6_ll)
907 self.assertEqual(src_addr, vr_ll_addr)
909 self.assertTrue(pkt[ICMPv6ND_NA].tgt in vr.virtual_ips())
910 self.assertEqual(pkt[ICMPv6NDOptDstLLAddr].lladdr, vr.virtual_mac())
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)
920 intvl = self._default_adv
921 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
922 prio=prio, intvl=intvl,
923 flags=self._default_flags)
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"))
933 pkts = self.pg0.get_capture(4, filter_out_fn=None)
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)
942 vr.remove_vpp_config()
945 # 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)
953 intvl = self._default_adv
954 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
955 prio=prio, intvl=intvl,
956 flags=self._default_flags)
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)
974 pkts = self.pg0.get_capture(5, filter_out_fn=None)
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
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"))
995 vr.start_stop(is_start=0)
996 self.logger.info(self.vapi.cli("show vrrp vr"))
998 # 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)
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])
1014 self._vrs.append(vr)
1016 vr.start_stop(is_start=1)
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()
1022 # Init -> Backup: A multicast listener report should be sent
1023 pkts = self.pg0.get_capture(1, filter_out_fn=None)
1025 # send higher prio advertisements, should not see VPP send any
1026 src_ip = self.pg0.remote_ip6_ll
1028 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
1029 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"))
1035 vr.start_stop(is_start=0)
1036 self.logger.info(self.vapi.cli("show vrrp vr"))
1037 vr.remove_vpp_config()
1040 def test_vrrp6_master_nd(self):
1041 """ IPv6 Master VR replies to NDP """
1044 # VR virtual IP is the default, which is the pg local IP
1047 intvl = self._default_adv
1048 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
1049 prio=prio, intvl=intvl,
1050 flags=self._default_flags)
1052 self._vrs.append(vr)
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())
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())
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())
1068 vr.remove_vpp_config()
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
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,
1086 self._vrs.append(vr)
1088 nsma = in6_getnsma(inet_pton(socket.AF_INET6, vip))
1089 dmac = in6_getnsmac(nsma)
1090 dst_ip = inet_ntop(socket.AF_INET6, nsma)
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))
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)
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
1102 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip6)
1103 pkts = [adv, ndp_req]
1104 vr.start_stop(is_start=1)
1105 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1107 vr.start_stop(is_start=0)
1109 def test_vrrp6_election(self):
1110 """ IPv6 Backup VR becomes master if no advertisements received """
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,
1121 self._vrs.append(vr)
1124 # After adding the VR, it should be in the init state
1125 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1128 vr.start_stop(is_start=1)
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()
1134 # no advertisements should arrive until timer expires
1135 self.pg0.enable_capture()
1136 while (time.time() + intvl_s) < end_time:
1138 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
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)
1145 def test_vrrp6_backup_preempts(self):
1146 """ IPv6 Backup VR preempts lower priority master """
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,
1157 self._vrs.append(vr)
1160 # After adding the VR, it should be in the init state
1161 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1164 vr.start_stop(is_start=1)
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()
1170 # send lower prio advertisements until timer expires
1171 src_ip = self.pg0.remote_ip6
1172 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
1173 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"))
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)
1182 def test_vrrp6_master_preempted(self):
1183 """ IPv6 Master VR preempted by higher priority backup """
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
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,
1195 self._vrs.append(vr)
1198 # After adding the VR, it should be in the init state
1199 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1202 vr.start_stop(is_start=1)
1203 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
1209 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1211 # Build advertisement packet and send it
1212 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip6)]
1213 self.pg_send(self.pg0, pkts)
1215 # VR should be in backup state again
1216 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1218 def test_vrrp6_accept_mode_disabled(self):
1219 """ IPv6 Master VR does not reply for VIP w/ accept mode off """
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
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,
1231 self._vrs.append(vr)
1234 # After adding the VR, it should be in the init state
1235 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1238 vr.start_stop(is_start=1)
1239 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
1245 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
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])
1253 # wait for an echo reply. none should be received
1255 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
1257 def test_vrrp6_accept_mode_enabled(self):
1258 """ IPv6 Master VR replies for VIP w/ accept mode on """
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
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,
1271 self._vrs.append(vr)
1274 # After adding the VR, it should be in the init state
1275 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1278 vr.start_stop(is_start=1)
1279 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
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()
1285 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
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])
1293 # wait for an echo reply.
1295 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
1296 filter_out_fn=is_not_echo_reply)
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)
1303 def test_vrrp6_intf_tracking(self):
1304 """ IPv6 Master VR adjusts priority based on tracked interface """
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,
1315 self._vrs.append(vr)
1318 # After adding the VR, it should be in the init state
1319 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1321 # add pg1 as a tracked interface and start the VR
1323 adjusted_prio = prio - adjustment
1324 vr.add_del_tracked_interface(is_add=1,
1325 sw_if_index=self.pg1.sw_if_index,
1327 vr.start_stop(is_start=1)
1328 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1330 adv_configured = vr.vrrp_adv_packet(prio=prio)
1331 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
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)
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)
1346 # bring up pg1, verify priority now matches configured value
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)
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)
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)
1367 def test_vrrp6_master_adv_unicast(self):
1368 """ IPv6 Master VR advertises (unicast) """
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,
1381 self._vrs.append(vr)
1383 vr.set_unicast_peers([unicast_peer.ip6])
1385 # After adding the VR, it should be in the init state
1386 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1388 # Start VR, transition to master
1389 vr.start_stop(is_start=1)
1390 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1392 self.pg0.enable_capture()
1393 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1394 filter_out_fn=is_not_adv)
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])
1411 if __name__ == '__main__':
1412 unittest.main(testRunner=VppTestRunner)