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