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