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