blob: 6a62a88c2a10a75cd3f9344b8f76cbe8a5f3fe3a [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
Klement Sekerab23ffd72021-05-31 16:08:53 +020026from config import config
27from framework import VppTestCase, VppTestRunner
Matthew Smith39e94282020-02-11 11:25:32 -060028from util import ip6_normalize
29
30VRRP_VR_FLAG_PREEMPT = 1
31VRRP_VR_FLAG_ACCEPT = 2
32VRRP_VR_FLAG_UNICAST = 4
33VRRP_VR_FLAG_IPV6 = 8
34
35VRRP_VR_STATE_INIT = 0
36VRRP_VR_STATE_BACKUP = 1
37VRRP_VR_STATE_MASTER = 2
38VRRP_VR_STATE_INTF_DOWN = 3
39
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +020040VRRP_INDEX_INVALID = 0xffffffff
41
Matthew Smith39e94282020-02-11 11:25:32 -060042
43def 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
51def 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
59def 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
70class 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 = []
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200100 self._vrrp_index = VRRP_INDEX_INVALID
Matthew Smith39e94282020-02-11 11:25:32 -0600101
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
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200112 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
Matthew Smith39e94282020-02-11 11:25:32 -0600126 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
Matthew Smith39e94282020-02-11 11:25:32 -0600180 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
Paul Vinciguerraa5dd6d72021-04-15 11:37:44 -0400215 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
Rajaselvam8cc66b52021-06-30 11:20:20 +0530236class TestVRRP4(VppTestCase):
Matthew Smith39e94282020-02-11 11:25:32 -0600237 """ IPv4 VRRP Test Case """
238
239 @classmethod
240 def setUpClass(cls):
241 super(TestVRRP4, cls).setUpClass()
242
243 @classmethod
244 def tearDownClass(cls):
245 super(TestVRRP4, cls).tearDownClass()
246
247 def setUp(self):
248 super(TestVRRP4, self).setUp()
249
250 self.create_pg_interfaces(range(2))
251
252 for i in self.pg_interfaces:
253 i.admin_up()
254 i.config_ip4()
255 i.generate_remote_hosts(5)
256 i.configure_ipv4_neighbors()
257
258 self._vrs = []
259 self._default_flags = VRRP_VR_FLAG_PREEMPT
260 self._default_adv = 100
261
262 def tearDown(self):
263 for vr in self._vrs:
264 try:
265 vr_api = vr.query_vpp_config()
266 if vr_api.runtime.state != VRRP_VR_STATE_INIT:
267 vr.start_stop(is_start=0)
268 vr.remove_vpp_config()
269 except:
270 self.logger.error("Error cleaning up")
271
272 for i in self.pg_interfaces:
273 i.admin_down()
274 i.unconfig_ip4()
275 i.unconfig_ip6()
276
277 self._vrs = []
278
279 super(TestVRRP4, self).tearDown()
280
281 def verify_vrrp4_igmp(self, pkt):
282 ip = pkt[IP]
283 self.assertEqual(ip.dst, "224.0.0.22")
284 self.assertEqual(ip.proto, 2)
285
286 igmp = pkt[IGMPv3]
287 self.assertEqual(IGMPv3.igmpv3types[igmp.type],
288 "Version 3 Membership Report")
289
290 igmpmr = pkt[IGMPv3mr]
291 self.assertEqual(igmpmr.numgrp, 1)
292 self.assertEqual(igmpmr.records[0].maddr, "224.0.0.18")
293
294 def verify_vrrp4_garp(self, pkt, vip, vmac):
295 arp = pkt[ARP]
296
297 # ARP "who-has" op == 1
298 self.assertEqual(arp.op, 1)
299 self.assertEqual(arp.pdst, arp.psrc)
300 self.assertEqual(arp.pdst, vip)
301 self.assertEqual(arp.hwsrc, vmac)
302
303 def verify_vrrp4_adv(self, rx_pkt, vr, prio=None):
304 vips = vr.virtual_ips()
305 eth = rx_pkt[Ether]
306 ip = rx_pkt[IP]
307 vrrp = rx_pkt[VRRPv3]
308
Rajaselvam8cc66b52021-06-30 11:20:20 +0530309 pkt = vr.vrrp_adv_packet(prio=prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600310
311 # Source MAC is virtual MAC, destination is multicast MAC
312 self.assertEqual(eth.src, vr.virtual_mac())
313 self.assertEqual(eth.dst, vr.adv_dest_mac())
314
315 self.assertEqual(ip.dst, "224.0.0.18")
316 self.assertEqual(ip.ttl, 255)
317 self.assertEqual(ip.proto, IPPROTO_VRRP)
318
319 self.assertEqual(vrrp.version, 3)
320 self.assertEqual(vrrp.type, 1)
321 self.assertEqual(vrrp.vrid, vr.vr_id())
322 if prio is None:
323 prio = vr.priority()
324 self.assertEqual(vrrp.priority, prio)
325 self.assertEqual(vrrp.ipcount, len(vips))
326 self.assertEqual(vrrp.adv, vr.adv_interval())
327 self.assertListEqual(vrrp.addrlist, vips)
328
329 # VR with priority 255 owns the virtual address and should
330 # become master and start advertising immediately.
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000331 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600332 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
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200361 # Same as above but with the update API, and add a change
362 # of parameters to test that too
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000363 @unittest.skipUnless(config.extended, "part of extended tests")
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200364 def test_vrrp4_master_adv_update(self):
365 """ IPv4 Master VR adv + Update to Backup """
366 self.pg_enable_capture(self.pg_interfaces)
367 self.pg_start()
368
369 prio = 255
370 intvl = self._default_adv
371 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
372 prio=prio, intvl=intvl,
373 flags=self._default_flags)
374
375 vr.update_vpp_config()
376 vr.start_stop(is_start=1)
377 self.logger.info(self.vapi.cli("show vrrp vr"))
378 # Update VR with lower prio and larger interval
379 # we need to keep old VR for the adv checks
380 upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100,
381 prio=100, intvl=2*intvl,
382 flags=self._default_flags,
383 vips=[self.pg0.remote_ip4])
384 upd_vr._vrrp_index = vr._vrrp_index
385 upd_vr.update_vpp_config()
386 start_time = time.time()
387 self.logger.info(self.vapi.cli("show vrrp vr"))
388 upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
389 self._vrs = [upd_vr]
390
391 pkts = self.pg0.get_capture(5)
392 # Init -> Master: IGMP Join, VRRP adv, gratuitous ARP are sent
393 self.verify_vrrp4_igmp(pkts[0])
394 self.verify_vrrp4_adv(pkts[1], vr, prio=prio)
395 self.verify_vrrp4_garp(pkts[2], vr.virtual_ips()[0], vr.virtual_mac())
396 # Master -> Init: Adv with priority 0 sent to force an election
397 self.verify_vrrp4_adv(pkts[3], vr, prio=0)
398 # Init -> Backup: An IGMP join should be sent
399 self.verify_vrrp4_igmp(pkts[4])
400
401 # send higher prio advertisements, should not receive any
402 end_time = start_time + 2 * upd_vr.master_down_seconds()
403 src_ip = self.pg0.remote_ip4
404 pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)]
405 while time.time() < end_time:
406 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl*0.01)
407 self.logger.info(self.vapi.cli("show trace"))
408
409 upd_vr.start_stop(is_start=0)
410 self.logger.info(self.vapi.cli("show vrrp vr"))
411
Matthew Smith39e94282020-02-11 11:25:32 -0600412 # VR with priority < 255 enters backup state and does not advertise as
413 # long as it receives higher priority advertisements
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000414 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600415 def test_vrrp4_backup_noadv(self):
416 """ IPv4 Backup VR does not advertise """
417 self.pg_enable_capture(self.pg_interfaces)
418 self.pg_start()
419
420 vr_id = 100
421 prio = 100
422 intvl = self._default_adv
423 intvl_s = intvl * 0.01
424 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
425 prio=prio, intvl=intvl,
426 flags=self._default_flags,
427 vips=[self.pg0.remote_ip4])
428 self._vrs.append(vr)
429 vr.add_vpp_config()
430
431 vr.start_stop(is_start=1)
432
433 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
434 # watch for advertisements for 2x the master down preemption timeout
435 end_time = vr.start_time() + 2 * vr.master_down_seconds()
436
437 # Init -> Backup: An IGMP join should be sent
438 pkts = self.pg0.get_capture(1)
439 self.verify_vrrp4_igmp(pkts[0])
440
441 # send higher prio advertisements, should not receive any
442 src_ip = self.pg0.remote_ip4
Rajaselvam8cc66b52021-06-30 11:20:20 +0530443 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -0600444 while time.time() < end_time:
445 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
446 self.logger.info(self.vapi.cli("show trace"))
447
448 vr.start_stop(is_start=0)
449 self.logger.info(self.vapi.cli("show vrrp vr"))
450 vr.remove_vpp_config()
451 self._vrs = []
452
453 def test_vrrp4_master_arp(self):
454 """ IPv4 Master VR replies to ARP """
455 self.pg_start()
456
457 # VR virtual IP is the default, which is the pg local IP
458 vr_id = 100
459 prio = 255
460 intvl = self._default_adv
461 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
462 prio=prio, intvl=intvl,
463 flags=self._default_flags)
464 self._vrs.append(vr)
465
466 vr.add_vpp_config()
467
468 # before the VR is up, ARP should resolve to interface MAC
469 self.pg0.resolve_arp()
470 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
471
472 # start the VR, ARP should now resolve to virtual MAC
473 vr.start_stop(is_start=1)
474 self.pg0.resolve_arp()
475 self.assertEqual(self.pg0.local_mac, vr.virtual_mac())
476
477 # stop the VR, ARP should resolve to interface MAC again
478 vr.start_stop(is_start=0)
479 self.pg0.resolve_arp()
480 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
481
482 vr.remove_vpp_config()
483 self._vrs = []
484
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000485 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600486 def test_vrrp4_backup_noarp(self):
487 """ IPv4 Backup VR ignores ARP """
488 # We need an address for a virtual IP that is not the IP that
489 # ARP requests will originate from
490
491 vr_id = 100
492 prio = 100
493 intvl = self._default_adv
494 vip = self.pg0.remote_hosts[1].ip4
495 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
496 prio=prio, intvl=intvl,
497 flags=self._default_flags,
498 vips=[vip])
499 self._vrs.append(vr)
500 vr.add_vpp_config()
501
502 arp_req = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
503 ARP(op=ARP.who_has, pdst=vip,
504 psrc=self.pg0.remote_ip4, hwsrc=self.pg0.remote_mac))
505
506 # Before the VR is started make sure no reply to request for VIP
507 self.pg_start()
508 self.pg_enable_capture(self.pg_interfaces)
509 self.send_and_assert_no_replies(self.pg0, [arp_req], timeout=1)
510
511 # VR should start in backup state and still should not reply to ARP
512 # send a higher priority adv to make sure it does not become master
Rajaselvam8cc66b52021-06-30 11:20:20 +0530513 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip4)
Matthew Smith39e94282020-02-11 11:25:32 -0600514 vr.start_stop(is_start=1)
515 self.send_and_assert_no_replies(self.pg0, [adv, arp_req], timeout=1)
516
517 vr.start_stop(is_start=0)
518 vr.remove_vpp_config()
519 self._vrs = []
520
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000521 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600522 def test_vrrp4_election(self):
523 """ IPv4 Backup VR becomes master if no advertisements received """
524
525 vr_id = 100
526 prio = 100
527 intvl = self._default_adv
528 intvl_s = intvl * 0.01
529 vip = self.pg0.remote_ip4
530 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
531 prio=prio, intvl=intvl,
532 flags=self._default_flags,
533 vips=[vip])
534 self._vrs.append(vr)
535 vr.add_vpp_config()
536
537 # After adding the VR, it should be in the init state
538 vr.assert_state_equals(VRRP_VR_STATE_INIT)
539
540 self.pg_start()
541 vr.start_stop(is_start=1)
542
543 # VR should be in backup state after starting
544 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
545 end_time = vr.start_time() + vr.master_down_seconds()
546
547 # should not receive adverts until timer expires & state transition
548 self.pg_enable_capture(self.pg_interfaces)
549 while (time.time() + intvl_s) < end_time:
550 time.sleep(intvl_s)
551 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
552
553 # VR should be in master state, should send an adv
554 self.pg0.enable_capture()
555 self.pg0.wait_for_packet(intvl_s, is_not_adv)
556 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
557
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000558 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600559 def test_vrrp4_backup_preempts(self):
560 """ IPv4 Backup VR preempts lower priority master """
561
562 vr_id = 100
563 prio = 100
564 intvl = self._default_adv
565 intvl_s = intvl * 0.01
566 vip = self.pg0.remote_ip4
567 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
568 prio=prio, intvl=intvl,
569 flags=self._default_flags,
570 vips=[vip])
571 self._vrs.append(vr)
572 vr.add_vpp_config()
573
574 # After adding the VR, it should be in the init state
575 vr.assert_state_equals(VRRP_VR_STATE_INIT)
576
577 self.pg_start()
578 vr.start_stop(is_start=1)
579
580 # VR should be in backup state after starting
581 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
582 end_time = vr.start_time() + vr.master_down_seconds()
583
584 # send lower prio advertisements until timer expires
585 src_ip = self.pg0.remote_ip4
Rajaselvam8cc66b52021-06-30 11:20:20 +0530586 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -0600587 while time.time() + intvl_s < end_time:
588 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
589 self.logger.info(self.vapi.cli("show trace"))
590
591 # when timer expires, VR should take over as master
592 self.pg0.enable_capture()
593 self.pg0.wait_for_packet(timeout=intvl_s, filter_out_fn=is_not_adv)
594 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
595
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000596 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600597 def test_vrrp4_master_preempted(self):
598 """ IPv4 Master VR preempted by higher priority backup """
599
600 # A prio 255 VR cannot be preempted so the prio has to be lower and
601 # we have to wait for it to take over
602 vr_id = 100
603 prio = 100
604 intvl = self._default_adv
605 vip = self.pg0.remote_ip4
606 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
607 prio=prio, intvl=intvl,
608 flags=self._default_flags,
609 vips=[vip])
610 self._vrs.append(vr)
611 vr.add_vpp_config()
612
613 # After adding the VR, it should be in the init state
614 vr.assert_state_equals(VRRP_VR_STATE_INIT)
615
616 # start VR
617 vr.start_stop(is_start=1)
618 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
619
620 # wait for VR to take over as master
621 end_time = vr.start_time() + vr.master_down_seconds()
622 sleep_s = end_time - time.time()
623 time.sleep(sleep_s)
624 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
625
626 # Build advertisement packet and send it
Rajaselvam8cc66b52021-06-30 11:20:20 +0530627 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip4)]
Matthew Smith39e94282020-02-11 11:25:32 -0600628 self.pg_send(self.pg0, pkts)
629
630 # VR should be in backup state again
631 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
632
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000633 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600634 def test_vrrp4_accept_mode_disabled(self):
635 """ IPv4 Master VR does not reply for VIP w/ accept mode off """
636
637 # accept mode only matters when prio < 255, so it will have to
638 # come up as a backup and take over as master after the timeout
639 vr_id = 100
640 prio = 100
641 intvl = self._default_adv
642 vip = self.pg0.remote_hosts[4].ip4
643 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
644 prio=prio, intvl=intvl,
645 flags=self._default_flags,
646 vips=[vip])
647 self._vrs.append(vr)
648 vr.add_vpp_config()
649
650 # After adding the VR, it should be in the init state
651 vr.assert_state_equals(VRRP_VR_STATE_INIT)
652
653 # start VR
654 vr.start_stop(is_start=1)
655 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
656
657 # wait for VR to take over as master
658 end_time = vr.start_time() + vr.master_down_seconds()
659 sleep_s = end_time - time.time()
660 time.sleep(sleep_s)
661 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
662
663 # send an ICMP echo to the VR virtual IP address
664 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
665 IP(dst=vip, src=self.pg0.remote_ip4) /
666 ICMP(seq=1, id=self.pg0.sw_if_index, type='echo-request'))
667 self.pg_send(self.pg0, [echo])
668
669 # wait for an echo reply. none should be received
670 time.sleep(1)
671 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
672
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000673 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600674 def test_vrrp4_accept_mode_enabled(self):
675 """ IPv4 Master VR replies for VIP w/ accept mode on """
676
677 # A prio 255 VR cannot be preempted so the prio has to be lower and
678 # we have to wait for it to take over
679 vr_id = 100
680 prio = 100
681 intvl = self._default_adv
682 vip = self.pg0.remote_hosts[4].ip4
683 flags = (VRRP_VR_FLAG_PREEMPT | VRRP_VR_FLAG_ACCEPT)
684 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
685 prio=prio, intvl=intvl,
686 flags=flags,
687 vips=[vip])
688 self._vrs.append(vr)
689 vr.add_vpp_config()
690
691 # After adding the VR, it should be in the init state
692 vr.assert_state_equals(VRRP_VR_STATE_INIT)
693
694 # start VR
695 vr.start_stop(is_start=1)
696 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
697
698 # wait for VR to take over as master
699 end_time = vr.start_time() + vr.master_down_seconds()
700 sleep_s = end_time - time.time()
701 time.sleep(sleep_s)
702 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
703
704 # send an ICMP echo to the VR virtual IP address
705 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
706 IP(dst=vip, src=self.pg0.remote_ip4) /
707 ICMP(seq=1, id=self.pg0.sw_if_index, type='echo-request'))
708 self.pg_send(self.pg0, [echo])
709
710 # wait for an echo reply.
711 time.sleep(1)
712 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
713 filter_out_fn=is_not_echo_reply)
714
715 self.assertEqual(rx_pkts[0][IP].src, vip)
716 self.assertEqual(rx_pkts[0][IP].dst, self.pg0.remote_ip4)
717 self.assertEqual(icmptypes[rx_pkts[0][ICMP].type], "echo-reply")
718 self.assertEqual(rx_pkts[0][ICMP].seq, 1)
719 self.assertEqual(rx_pkts[0][ICMP].id, self.pg0.sw_if_index)
720
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000721 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600722 def test_vrrp4_intf_tracking(self):
723 """ IPv4 Master VR adjusts priority based on tracked interface """
724
725 vr_id = 100
726 prio = 255
727 intvl = self._default_adv
728 intvl_s = intvl * 0.01
729 vip = self.pg0.local_ip4
730 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
731 prio=prio, intvl=intvl,
732 flags=self._default_flags,
733 vips=[vip])
734 self._vrs.append(vr)
735 vr.add_vpp_config()
736
737 # After adding the VR, it should be in the init state
738 vr.assert_state_equals(VRRP_VR_STATE_INIT)
739
740 # add pg1 as a tracked interface and start the VR
741 adjustment = 50
742 adjusted_prio = prio - adjustment
743 vr.add_del_tracked_interface(is_add=1,
744 sw_if_index=self.pg1.sw_if_index,
745 prio=adjustment)
746 vr.start_stop(is_start=1)
747 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
748
Rajaselvam8cc66b52021-06-30 11:20:20 +0530749 adv_configured = vr.vrrp_adv_packet(prio=prio)
750 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600751
752 # tracked intf is up -> advertised priority == configured priority
753 self.pg0.enable_capture()
754 rx = self.pg0.wait_for_packet(timeout=intvl_s,
755 filter_out_fn=is_not_adv)
756 self.assertEqual(rx, adv_configured)
757
758 # take down pg1, verify priority is now being adjusted
759 self.pg1.admin_down()
760 self.pg0.enable_capture()
761 rx = self.pg0.wait_for_packet(timeout=intvl_s,
762 filter_out_fn=is_not_adv)
763 self.assertEqual(rx, adv_adjusted)
764
765 # bring up pg1, verify priority now matches configured value
766 self.pg1.admin_up()
767 self.pg0.enable_capture()
768 rx = self.pg0.wait_for_packet(timeout=intvl_s,
769 filter_out_fn=is_not_adv)
770 self.assertEqual(rx, adv_configured)
771
772 # remove IP address from pg1, verify priority now being adjusted
773 self.pg1.unconfig_ip4()
774 self.pg0.enable_capture()
775 rx = self.pg0.wait_for_packet(timeout=intvl_s,
776 filter_out_fn=is_not_adv)
777 self.assertEqual(rx, adv_adjusted)
778
779 # add IP address to pg1, verify priority now matches configured value
780 self.pg1.config_ip4()
781 self.pg0.enable_capture()
782 rx = self.pg0.wait_for_packet(timeout=intvl_s,
783 filter_out_fn=is_not_adv)
784 self.assertEqual(rx, adv_configured)
785
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000786 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600787 def test_vrrp4_master_adv_unicast(self):
788 """ IPv4 Master VR advertises (unicast) """
789
790 vr_id = 100
791 prio = 255
792 intvl = self._default_adv
793 intvl_s = intvl * 0.01
794 vip = self.pg0.local_ip4
795 flags = (self._default_flags | VRRP_VR_FLAG_UNICAST)
796 unicast_peer = self.pg0.remote_hosts[4]
797 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
798 prio=prio, intvl=intvl,
799 flags=flags,
800 vips=[vip])
801 self._vrs.append(vr)
802 vr.add_vpp_config()
803 vr.set_unicast_peers([unicast_peer.ip4])
804
805 # After adding the VR, it should be in the init state
806 vr.assert_state_equals(VRRP_VR_STATE_INIT)
807
808 # Start VR, transition to master
809 vr.start_stop(is_start=1)
810 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
811
812 self.pg0.enable_capture()
813 rx = self.pg0.wait_for_packet(timeout=intvl_s,
814 filter_out_fn=is_not_adv)
815
816 self.assertTrue(rx.haslayer(Ether))
817 self.assertTrue(rx.haslayer(IP))
818 self.assertTrue(rx.haslayer(VRRPv3))
819 self.assertEqual(rx[Ether].src, self.pg0.local_mac)
820 self.assertEqual(rx[Ether].dst, unicast_peer.mac)
821 self.assertEqual(rx[IP].src, self.pg0.local_ip4)
822 self.assertEqual(rx[IP].dst, unicast_peer.ip4)
823 self.assertEqual(rx[VRRPv3].vrid, vr_id)
824 self.assertEqual(rx[VRRPv3].priority, prio)
825 self.assertEqual(rx[VRRPv3].ipcount, 1)
826 self.assertEqual(rx[VRRPv3].addrlist, [vip])
827
828
Rajaselvam8cc66b52021-06-30 11:20:20 +0530829class TestVRRP6(VppTestCase):
Matthew Smith39e94282020-02-11 11:25:32 -0600830 """ IPv6 VRRP Test Case """
831
832 @classmethod
833 def setUpClass(cls):
834 super(TestVRRP6, cls).setUpClass()
835
836 @classmethod
837 def tearDownClass(cls):
838 super(TestVRRP6, cls).tearDownClass()
839
840 def setUp(self):
841 super(TestVRRP6, self).setUp()
842
843 self.create_pg_interfaces(range(2))
844
845 for i in self.pg_interfaces:
846 i.admin_up()
847 i.config_ip6()
848 i.generate_remote_hosts(5)
849 i.configure_ipv6_neighbors()
850
851 self._vrs = []
852 self._default_flags = (VRRP_VR_FLAG_IPV6 | VRRP_VR_FLAG_PREEMPT)
853 self._default_adv = 100
854
855 def tearDown(self):
856 for vr in self._vrs:
857 try:
858 vr_api = vr.query_vpp_config()
859 if vr_api.runtime.state != VRRP_VR_STATE_INIT:
860 vr.start_stop(is_start=0)
861 vr.remove_vpp_config()
862 except:
863 self.logger.error("Error cleaning up")
864
865 for i in self.pg_interfaces:
866 i.admin_down()
867 i.unconfig_ip4()
868 i.unconfig_ip6()
869
870 self._vrs = []
871
872 super(TestVRRP6, self).tearDown()
873
874 def verify_vrrp6_mlr(self, pkt, vr):
875 ip6 = pkt[IPv6]
876 self.assertEqual(ip6.dst, "ff02::16")
877 self.assertEqual(ipv6nh[ip6.nh], "Hop-by-Hop Option Header")
878
879 hbh = pkt[IPv6ExtHdrHopByHop]
880 self.assertEqual(ipv6nh[hbh.nh], "ICMPv6")
881
882 self.assertTrue(pkt.haslayer(ICMPv6MLReport2))
883 mlr = pkt[ICMPv6MLReport2]
884 # should contain mc addr records for:
885 # - VRRPv3 multicast addr
886 # - solicited node mc addr record for each VR virtual IPv6 address
887 vips = vr.virtual_ips()
888 self.assertEqual(mlr.records_number, len(vips) + 1)
889 self.assertEqual(mlr.records[0].dst, vr.adv_dest_ip())
890
891 def verify_vrrp6_adv(self, rx_pkt, vr, prio=None):
892 self.assertTrue(rx_pkt.haslayer(Ether))
893 self.assertTrue(rx_pkt.haslayer(IPv6))
894 self.assertTrue(rx_pkt.haslayer(VRRPv3))
895
896 # generate a packet for this VR and compare it to the one received
Rajaselvam8cc66b52021-06-30 11:20:20 +0530897 pkt = vr.vrrp_adv_packet(prio=prio)
Matthew Smith39e94282020-02-11 11:25:32 -0600898 self.assertTrue(rx_pkt.haslayer(Ether))
899 self.assertTrue(rx_pkt.haslayer(IPv6))
900 self.assertTrue(rx_pkt.haslayer(VRRPv3))
901
902 self.assertEqual(pkt, rx_pkt)
903
904 def verify_vrrp6_gna(self, pkt, vr):
905 self.assertTrue(pkt.haslayer(Ether))
906 self.assertTrue(pkt.haslayer(IPv6))
907 self.assertTrue(pkt.haslayer(ICMPv6ND_NA))
908 self.assertTrue(pkt.haslayer(ICMPv6NDOptDstLLAddr))
909
910 self.assertEqual(pkt[Ether].dst, "33:33:00:00:00:01")
911
912 self.assertEqual(pkt[IPv6].dst, "ff02::1")
913 # convert addrs to packed format since string versions could differ
Paul Vinciguerra582eac52020-04-03 12:18:40 -0400914 src_addr = inet_pton(socket.AF_INET6, pkt[IPv6].src)
915 vr_ll_addr = inet_pton(socket.AF_INET6, vr.interface().local_ip6_ll)
Matthew Smith39e94282020-02-11 11:25:32 -0600916 self.assertEqual(src_addr, vr_ll_addr)
917
918 self.assertTrue(pkt[ICMPv6ND_NA].tgt in vr.virtual_ips())
919 self.assertEqual(pkt[ICMPv6NDOptDstLLAddr].lladdr, vr.virtual_mac())
920
921 # VR with priority 255 owns the virtual address and should
922 # become master and start advertising immediately.
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000923 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -0600924 def test_vrrp6_master_adv(self):
925 """ IPv6 Master VR advertises """
926 self.pg_enable_capture(self.pg_interfaces)
927 self.pg_start()
928
929 prio = 255
930 intvl = self._default_adv
931 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
932 prio=prio, intvl=intvl,
933 flags=self._default_flags)
934 self._vrs.append(vr)
935
936 vr.add_vpp_config()
937 self.logger.info(self.vapi.cli("show vrrp vr"))
938 vr.start_stop(is_start=1)
939 self.logger.info(self.vapi.cli("show vrrp vr"))
940 vr.start_stop(is_start=0)
941 self.logger.info(self.vapi.cli("show vrrp vr"))
942
943 pkts = self.pg0.get_capture(4, filter_out_fn=None)
944
945 # Init -> Master: Multicast group Join, VRRP adv, gratuitous NAs sent
946 self.verify_vrrp6_mlr(pkts[0], vr)
947 self.verify_vrrp6_adv(pkts[1], vr, prio=prio)
948 self.verify_vrrp6_gna(pkts[2], vr)
949 # Master -> Init: Adv with priority 0 sent to force an election
950 self.verify_vrrp6_adv(pkts[3], vr, prio=0)
951
952 vr.remove_vpp_config()
953 self._vrs = []
954
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200955 # Same as above but with the update API, and add a change
956 # of parameters to test that too
Alexander Chernavin65e770d2022-04-11 13:02:11 +0000957 @unittest.skipUnless(config.extended, "part of extended tests")
Emanuele Di Pascale7539e4b2022-03-29 12:29:23 +0200958 def test_vrrp6_master_adv_update(self):
959 """ IPv6 Master VR adv + Update to Backup """
960 self.pg_enable_capture(self.pg_interfaces)
961 self.pg_start()
962
963 prio = 255
964 intvl = self._default_adv
965 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
966 prio=prio, intvl=intvl,
967 flags=self._default_flags)
968
969 vr.update_vpp_config()
970 vr.start_stop(is_start=1)
971 self.logger.info(self.vapi.cli("show vrrp vr"))
972 # Update VR with lower prio and larger interval
973 # we need to keep old VR for the adv checks
974 upd_vr = VppVRRPVirtualRouter(self, self.pg0, 100,
975 prio=100, intvl=2*intvl,
976 flags=self._default_flags,
977 vips=[self.pg0.remote_ip6])
978 upd_vr._vrrp_index = vr._vrrp_index
979 upd_vr.update_vpp_config()
980 start_time = time.time()
981 self.logger.info(self.vapi.cli("show vrrp vr"))
982 upd_vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
983 self._vrs = [upd_vr]
984
985 pkts = self.pg0.get_capture(5, filter_out_fn=None)
986
987 # Init -> Master: Multicast group Join, VRRP adv, gratuitous NAs sent
988 self.verify_vrrp6_mlr(pkts[0], vr)
989 self.verify_vrrp6_adv(pkts[1], vr, prio=prio)
990 self.verify_vrrp6_gna(pkts[2], vr)
991 # Master -> Init: Adv with priority 0 sent to force an election
992 self.verify_vrrp6_adv(pkts[3], vr, prio=0)
993 # Init -> Backup: A multicast listener report should be sent
994 # not actually verified in the test below, where I took this from
995
996 # send higher prio advertisements, should not see VPP send any
997 src_ip = self.pg0.remote_ip6_ll
998 pkts = [upd_vr.vrrp_adv_packet(prio=110, src_ip=src_ip)]
999 self.logger.info(self.vapi.cli("show vlib graph"))
1000 end_time = start_time + 2 * upd_vr.master_down_seconds()
1001 while time.time() < end_time:
1002 self.send_and_assert_no_replies(
1003 self.pg0, pkts, timeout=0.01*upd_vr._intvl)
1004 self.logger.info(self.vapi.cli("show trace"))
1005
1006 vr.start_stop(is_start=0)
1007 self.logger.info(self.vapi.cli("show vrrp vr"))
1008
Matthew Smith39e94282020-02-11 11:25:32 -06001009 # VR with priority < 255 enters backup state and does not advertise as
1010 # long as it receives higher priority advertisements
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001011 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001012 def test_vrrp6_backup_noadv(self):
1013 """ IPv6 Backup VR does not advertise """
1014 self.pg_enable_capture(self.pg_interfaces)
1015 self.pg_start()
1016
1017 vr_id = 100
1018 prio = 100
1019 intvl = self._default_adv
1020 intvl_s = intvl * 0.01
1021 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1022 prio=prio, intvl=intvl,
1023 flags=self._default_flags,
1024 vips=[self.pg0.remote_ip6])
1025 vr.add_vpp_config()
1026 self._vrs.append(vr)
1027
1028 vr.start_stop(is_start=1)
1029
1030 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1031 # watch for advertisements for 2x the master down preemption timeout
1032 end_time = vr.start_time() + 2 * vr.master_down_seconds()
1033
1034 # Init -> Backup: A multicast listener report should be sent
1035 pkts = self.pg0.get_capture(1, filter_out_fn=None)
1036
1037 # send higher prio advertisements, should not see VPP send any
1038 src_ip = self.pg0.remote_ip6_ll
1039 num_advs = 5
Rajaselvam8cc66b52021-06-30 11:20:20 +05301040 pkts = [vr.vrrp_adv_packet(prio=prio+10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -06001041 self.logger.info(self.vapi.cli("show vlib graph"))
1042 while time.time() < end_time:
1043 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1044 self.logger.info(self.vapi.cli("show trace"))
1045 num_advs -= 1
1046
1047 vr.start_stop(is_start=0)
1048 self.logger.info(self.vapi.cli("show vrrp vr"))
1049 vr.remove_vpp_config()
1050 self._vrs = []
1051
1052 def test_vrrp6_master_nd(self):
1053 """ IPv6 Master VR replies to NDP """
1054 self.pg_start()
1055
1056 # VR virtual IP is the default, which is the pg local IP
1057 vr_id = 100
1058 prio = 255
1059 intvl = self._default_adv
1060 vr = VppVRRPVirtualRouter(self, self.pg0, 100,
1061 prio=prio, intvl=intvl,
1062 flags=self._default_flags)
1063 vr.add_vpp_config()
1064 self._vrs.append(vr)
1065
1066 # before the VR is up, NDP should resolve to interface MAC
1067 self.pg0.resolve_ndp()
1068 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
1069
1070 # start the VR, NDP should now resolve to virtual MAC
1071 vr.start_stop(is_start=1)
1072 self.pg0.resolve_ndp()
1073 self.assertEqual(self.pg0.local_mac, vr.virtual_mac())
1074
1075 # stop the VR, ARP should resolve to interface MAC again
1076 vr.start_stop(is_start=0)
1077 self.pg0.resolve_ndp()
1078 self.assertNotEqual(self.pg0.local_mac, vr.virtual_mac())
1079
1080 vr.remove_vpp_config()
1081 self._vrs = []
1082
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001083 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001084 def test_vrrp6_backup_nond(self):
1085 """ IPv6 Backup VR ignores NDP """
1086 # We need an address for a virtual IP that is not the IP that
1087 # ARP requests will originate from
1088
1089 vr_id = 100
1090 prio = 100
1091 intvl = self._default_adv
1092 intvl_s = intvl * 0.01
1093 vip = self.pg0.remote_hosts[1].ip6
1094 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1095 prio=prio, intvl=intvl,
1096 flags=self._default_flags,
1097 vips=[vip])
1098 vr.add_vpp_config()
1099 self._vrs.append(vr)
1100
1101 nsma = in6_getnsma(inet_pton(socket.AF_INET6, vip))
1102 dmac = in6_getnsmac(nsma)
1103 dst_ip = inet_ntop(socket.AF_INET6, nsma)
1104
1105 ndp_req = (Ether(dst=dmac, src=self.pg0.remote_mac) /
1106 IPv6(dst=dst_ip, src=self.pg0.remote_ip6) /
1107 ICMPv6ND_NS(tgt=vip) /
1108 ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
1109
1110 # Before the VR is started make sure no reply to request for VIP
1111 self.send_and_assert_no_replies(self.pg0, [ndp_req], timeout=1)
1112
1113 # VR should start in backup state and still should not reply to NDP
1114 # send a higher priority adv to make sure it does not become master
Rajaselvam8cc66b52021-06-30 11:20:20 +05301115 adv = vr.vrrp_adv_packet(prio=prio+10, src_ip=self.pg0.remote_ip6)
Matthew Smith39e94282020-02-11 11:25:32 -06001116 pkts = [adv, ndp_req]
1117 vr.start_stop(is_start=1)
1118 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1119
1120 vr.start_stop(is_start=0)
1121
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001122 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001123 def test_vrrp6_election(self):
1124 """ IPv6 Backup VR becomes master if no advertisements received """
1125
1126 vr_id = 100
1127 prio = 100
1128 intvl = self._default_adv
1129 intvl_s = intvl * 0.01
1130 vip = self.pg0.remote_ip6
1131 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1132 prio=prio, intvl=intvl,
1133 flags=self._default_flags,
1134 vips=[vip])
1135 self._vrs.append(vr)
1136 vr.add_vpp_config()
1137
1138 # After adding the VR, it should be in the init state
1139 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1140
1141 self.pg_start()
1142 vr.start_stop(is_start=1)
1143
1144 # VR should be in backup state after starting
1145 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1146 end_time = vr.start_time() + vr.master_down_seconds()
1147
1148 # no advertisements should arrive until timer expires
1149 self.pg0.enable_capture()
1150 while (time.time() + intvl_s) < end_time:
1151 time.sleep(intvl_s)
1152 self.pg0.assert_nothing_captured(filter_out_fn=is_not_adv)
1153
1154 # VR should be in master state after timer expires
1155 self.pg0.enable_capture()
1156 self.pg0.wait_for_packet(intvl_s, is_not_adv)
1157 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1158
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001159 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001160 def test_vrrp6_backup_preempts(self):
1161 """ IPv6 Backup VR preempts lower priority master """
1162
1163 vr_id = 100
1164 prio = 100
1165 intvl = self._default_adv
1166 intvl_s = intvl * 0.01
1167 vip = self.pg0.remote_ip6
1168 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1169 prio=prio, intvl=intvl,
1170 flags=self._default_flags,
1171 vips=[vip])
1172 self._vrs.append(vr)
1173 vr.add_vpp_config()
1174
1175 # After adding the VR, it should be in the init state
1176 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1177
1178 self.pg_start()
1179 vr.start_stop(is_start=1)
1180
1181 # VR should be in backup state after starting
1182 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1183 end_time = vr.start_time() + vr.master_down_seconds()
1184
1185 # send lower prio advertisements until timer expires
1186 src_ip = self.pg0.remote_ip6
Rajaselvam8cc66b52021-06-30 11:20:20 +05301187 pkts = [vr.vrrp_adv_packet(prio=prio-10, src_ip=src_ip)]
Matthew Smith39e94282020-02-11 11:25:32 -06001188 while (time.time() + intvl_s) < end_time:
1189 self.send_and_assert_no_replies(self.pg0, pkts, timeout=intvl_s)
1190 self.logger.info(self.vapi.cli("show trace"))
1191
1192 # when timer expires, VR should take over as master
1193 self.pg0.enable_capture()
1194 self.pg0.wait_for_packet(timeout=intvl_s, filter_out_fn=is_not_adv)
1195 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1196
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001197 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001198 def test_vrrp6_master_preempted(self):
1199 """ IPv6 Master VR preempted by higher priority backup """
1200
1201 # A prio 255 VR cannot be preempted so the prio has to be lower and
1202 # we have to wait for it to take over
1203 vr_id = 100
1204 prio = 100
1205 intvl = self._default_adv
1206 vip = self.pg0.remote_ip6
1207 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1208 prio=prio, intvl=intvl,
1209 flags=self._default_flags,
1210 vips=[vip])
1211 self._vrs.append(vr)
1212 vr.add_vpp_config()
1213
1214 # After adding the VR, it should be in the init state
1215 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1216
1217 # start VR
1218 vr.start_stop(is_start=1)
1219 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1220
1221 # wait for VR to take over as master
1222 end_time = vr.start_time() + vr.master_down_seconds()
1223 sleep_s = end_time - time.time()
1224 time.sleep(sleep_s)
1225 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1226
1227 # Build advertisement packet and send it
Rajaselvam8cc66b52021-06-30 11:20:20 +05301228 pkts = [vr.vrrp_adv_packet(prio=255, src_ip=self.pg0.remote_ip6)]
Matthew Smith39e94282020-02-11 11:25:32 -06001229 self.pg_send(self.pg0, pkts)
1230
1231 # VR should be in backup state again
1232 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1233
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001234 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001235 def test_vrrp6_accept_mode_disabled(self):
1236 """ IPv6 Master VR does not reply for VIP w/ accept mode off """
1237
1238 # accept mode only matters when prio < 255, so it will have to
1239 # come up as a backup and take over as master after the timeout
1240 vr_id = 100
1241 prio = 100
1242 intvl = self._default_adv
1243 vip = self.pg0.remote_hosts[4].ip6
1244 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1245 prio=prio, intvl=intvl,
1246 flags=self._default_flags,
1247 vips=[vip])
1248 self._vrs.append(vr)
1249 vr.add_vpp_config()
1250
1251 # After adding the VR, it should be in the init state
1252 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1253
1254 # start VR
1255 vr.start_stop(is_start=1)
1256 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1257
1258 # wait for VR to take over as master
1259 end_time = vr.start_time() + vr.master_down_seconds()
1260 sleep_s = end_time - time.time()
1261 time.sleep(sleep_s)
1262 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1263
1264 # send an ICMPv6 echo to the VR virtual IP address
1265 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
1266 IPv6(dst=vip, src=self.pg0.remote_ip6) /
1267 ICMPv6EchoRequest(seq=1, id=self.pg0.sw_if_index))
1268 self.pg_send(self.pg0, [echo])
1269
1270 # wait for an echo reply. none should be received
1271 time.sleep(1)
1272 self.pg0.assert_nothing_captured(filter_out_fn=is_not_echo_reply)
1273
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001274 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001275 def test_vrrp6_accept_mode_enabled(self):
1276 """ IPv6 Master VR replies for VIP w/ accept mode on """
1277
1278 # A prio 255 VR cannot be preempted so the prio has to be lower and
1279 # we have to wait for it to take over
1280 vr_id = 100
1281 prio = 100
1282 intvl = self._default_adv
1283 vip = self.pg0.remote_hosts[4].ip6
1284 flags = (self._default_flags | VRRP_VR_FLAG_ACCEPT)
1285 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1286 prio=prio, intvl=intvl,
1287 flags=flags,
1288 vips=[vip])
1289 self._vrs.append(vr)
1290 vr.add_vpp_config()
1291
1292 # After adding the VR, it should be in the init state
1293 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1294
1295 # start VR
1296 vr.start_stop(is_start=1)
1297 vr.assert_state_equals(VRRP_VR_STATE_BACKUP)
1298
1299 # wait for VR to take over as master
1300 end_time = vr.start_time() + vr.master_down_seconds()
1301 sleep_s = end_time - time.time()
1302 time.sleep(sleep_s)
1303 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1304
1305 # send an ICMP echo to the VR virtual IP address
1306 echo = (Ether(dst=vr.virtual_mac(), src=self.pg0.remote_mac) /
1307 IPv6(dst=vip, src=self.pg0.remote_ip6) /
1308 ICMPv6EchoRequest(seq=1, id=self.pg0.sw_if_index))
1309 self.pg_send(self.pg0, [echo])
1310
1311 # wait for an echo reply.
1312 time.sleep(1)
1313 rx_pkts = self.pg0.get_capture(expected_count=1, timeout=1,
1314 filter_out_fn=is_not_echo_reply)
1315
1316 self.assertEqual(rx_pkts[0][IPv6].src, vip)
1317 self.assertEqual(rx_pkts[0][IPv6].dst, self.pg0.remote_ip6)
1318 self.assertEqual(rx_pkts[0][ICMPv6EchoReply].seq, 1)
1319 self.assertEqual(rx_pkts[0][ICMPv6EchoReply].id, self.pg0.sw_if_index)
1320
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001321 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001322 def test_vrrp6_intf_tracking(self):
1323 """ IPv6 Master VR adjusts priority based on tracked interface """
1324
1325 vr_id = 100
1326 prio = 255
1327 intvl = self._default_adv
1328 intvl_s = intvl * 0.01
1329 vip = self.pg0.local_ip6
1330 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1331 prio=prio, intvl=intvl,
1332 flags=self._default_flags,
1333 vips=[vip])
1334 self._vrs.append(vr)
1335 vr.add_vpp_config()
1336
1337 # After adding the VR, it should be in the init state
1338 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1339
1340 # add pg1 as a tracked interface and start the VR
1341 adjustment = 50
1342 adjusted_prio = prio - adjustment
1343 vr.add_del_tracked_interface(is_add=1,
1344 sw_if_index=self.pg1.sw_if_index,
1345 prio=adjustment)
1346 vr.start_stop(is_start=1)
1347 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1348
Rajaselvam8cc66b52021-06-30 11:20:20 +05301349 adv_configured = vr.vrrp_adv_packet(prio=prio)
1350 adv_adjusted = vr.vrrp_adv_packet(prio=adjusted_prio)
Matthew Smith39e94282020-02-11 11:25:32 -06001351
1352 # tracked intf is up -> advertised priority == configured priority
1353 self.pg0.enable_capture()
1354 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1355 filter_out_fn=is_not_adv)
1356 self.assertEqual(rx, adv_configured)
1357
1358 # take down pg1, verify priority is now being adjusted
1359 self.pg1.admin_down()
1360 self.pg0.enable_capture()
1361 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1362 filter_out_fn=is_not_adv)
1363 self.assertEqual(rx, adv_adjusted)
1364
1365 # bring up pg1, verify priority now matches configured value
1366 self.pg1.admin_up()
1367 self.pg0.enable_capture()
1368 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1369 filter_out_fn=is_not_adv)
1370 self.assertEqual(rx, adv_configured)
1371
1372 # remove IP address from pg1, verify priority now being adjusted
1373 self.pg1.unconfig_ip6()
1374 self.pg0.enable_capture()
1375 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1376 filter_out_fn=is_not_adv)
1377 self.assertEqual(rx, adv_adjusted)
1378
1379 # add IP address to pg1, verify priority now matches configured value
1380 self.pg1.config_ip6()
1381 self.pg0.enable_capture()
1382 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1383 filter_out_fn=is_not_adv)
1384 self.assertEqual(rx, adv_configured)
1385
Alexander Chernavin65e770d2022-04-11 13:02:11 +00001386 @unittest.skipUnless(config.extended, "part of extended tests")
Matthew Smith39e94282020-02-11 11:25:32 -06001387 def test_vrrp6_master_adv_unicast(self):
1388 """ IPv6 Master VR advertises (unicast) """
1389
1390 vr_id = 100
1391 prio = 255
1392 intvl = self._default_adv
1393 intvl_s = intvl * 0.01
1394 vip = self.pg0.local_ip6
1395 flags = (self._default_flags | VRRP_VR_FLAG_UNICAST)
1396 unicast_peer = self.pg0.remote_hosts[4]
1397 vr = VppVRRPVirtualRouter(self, self.pg0, vr_id,
1398 prio=prio, intvl=intvl,
1399 flags=flags,
1400 vips=[vip])
1401 self._vrs.append(vr)
1402 vr.add_vpp_config()
1403 vr.set_unicast_peers([unicast_peer.ip6])
1404
1405 # After adding the VR, it should be in the init state
1406 vr.assert_state_equals(VRRP_VR_STATE_INIT)
1407
1408 # Start VR, transition to master
1409 vr.start_stop(is_start=1)
1410 vr.assert_state_equals(VRRP_VR_STATE_MASTER)
1411
1412 self.pg0.enable_capture()
1413 rx = self.pg0.wait_for_packet(timeout=intvl_s,
1414 filter_out_fn=is_not_adv)
1415
1416 self.assertTrue(rx.haslayer(Ether))
1417 self.assertTrue(rx.haslayer(IPv6))
1418 self.assertTrue(rx.haslayer(VRRPv3))
1419 self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1420 self.assertEqual(rx[Ether].dst, unicast_peer.mac)
1421 self.assertEqual(ip6_normalize(rx[IPv6].src),
1422 ip6_normalize(self.pg0.local_ip6_ll))
1423 self.assertEqual(ip6_normalize(rx[IPv6].dst),
1424 ip6_normalize(unicast_peer.ip6))
1425 self.assertEqual(rx[VRRPv3].vrid, vr_id)
1426 self.assertEqual(rx[VRRPv3].priority, prio)
1427 self.assertEqual(rx[VRRPv3].ipcount, 1)
1428 self.assertEqual(rx[VRRPv3].addrlist, [vip])
1429
1430
1431if __name__ == '__main__':
1432 unittest.main(testRunner=VppTestRunner)