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