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