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