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