Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.

Commit 739f22c

Browse files
committed
T7577: VPP VRRP plugin implementation
CLI commands: set vpp vrrp group FIRST address <192.0.2.1> # multi set vpp vrrp group FIRST interface <eth1> set vpp vrrp group FIRST vrid <10> set vpp vrrp group FIRST priority <100> set vpp vrrp group FIRST peer-address <192.0.2.12> # multi set vpp vrrp group FIRST advertise-interval <100> set vpp vrrp group FIRST no-preempt set vpp vrrp group FIRST accept-mode set vpp vrrp group FIRST disable set vpp vrrp group FIRST description <test>
1 parent d37cca0 commit 739f22c

File tree

10 files changed

+712
-1
lines changed

10 files changed

+712
-1
lines changed

data/config-mode-dependencies/vyos-vpp.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"vpp_acl": ["vpp_acl"],
1414
"vpp_nat": ["vpp_nat"],
1515
"vpp_nat_cgnat": ["vpp_nat_cgnat"],
16-
"vpp_kernel_interface": ["vpp_kernel-interfaces"]
16+
"vpp_kernel_interface": ["vpp_kernel-interfaces"],
17+
"vpp_vrrp": ["vpp_vrrp"]
1718
},
1819
"vpp_interfaces_bonding": {
1920
"vpp_interfaces_xconnect": ["vpp_interfaces_xconnect"],

data/templates/vpp/startup.conf.j2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ plugins {
116116
# plugin wireguard_plugin.so { enable }
117117
# ACL
118118
plugin acl_plugin.so { enable }
119+
# VRRP
120+
plugin vrrp_plugin.so { enable }
119121
}
120122

121123
linux-cp {

interface-definitions/vpp.xml.in

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,115 @@
15171517
</node>
15181518
</children>
15191519
</node>
1520+
<node name="vrrp" owner="${vyos_conf_scripts_dir}/vpp_vrrp.py">
1521+
<properties>
1522+
<help>VPP Virtual Router Redundancy Protocol (VRRP) settings</help>
1523+
<priority>333</priority>
1524+
</properties>
1525+
<children>
1526+
<tagNode name="group">
1527+
<properties>
1528+
<help>VPP VRRP group</help>
1529+
</properties>
1530+
<children>
1531+
<leafNode name="interface">
1532+
<properties>
1533+
<help>Interface</help>
1534+
<completionHelp>
1535+
<script>${vyos_completion_dir}/list_interfaces</script>
1536+
</completionHelp>
1537+
</properties>
1538+
</leafNode>
1539+
<leafNode name="advertise-interval">
1540+
<properties>
1541+
<help>Advertise interval</help>
1542+
<valueHelp>
1543+
<format>u32:1-255</format>
1544+
<description>Advertise interval in seconds</description>
1545+
</valueHelp>
1546+
<constraint>
1547+
<validator name="numeric" argument="--range 1-255"/>
1548+
</constraint>
1549+
</properties>
1550+
<defaultValue>1</defaultValue>
1551+
</leafNode>
1552+
#include <include/generic-description.xml.i>
1553+
#include <include/generic-disable-node.xml.i>
1554+
<leafNode name="peer-address">
1555+
<properties>
1556+
<help>Unicast VRRP peer address</help>
1557+
<valueHelp>
1558+
<format>ipv4</format>
1559+
<description>IPv4 unicast peer address</description>
1560+
</valueHelp>
1561+
<valueHelp>
1562+
<format>ipv6</format>
1563+
<description>IPv6 unicast peer address</description>
1564+
</valueHelp>
1565+
<constraint>
1566+
<validator name="ip-address"/>
1567+
</constraint>
1568+
<multi/>
1569+
</properties>
1570+
</leafNode>
1571+
<leafNode name="no-preempt">
1572+
<properties>
1573+
<valueless/>
1574+
<help>Disable master preemption</help>
1575+
</properties>
1576+
</leafNode>
1577+
<leafNode name="accept-mode">
1578+
<properties>
1579+
<valueless/>
1580+
<help>Allow backup VRRP router to accept and process packets</help>
1581+
</properties>
1582+
</leafNode>
1583+
<leafNode name="priority">
1584+
<properties>
1585+
<help>Router priority</help>
1586+
<valueHelp>
1587+
<format>u32:1-255</format>
1588+
<description>Router priority</description>
1589+
</valueHelp>
1590+
<constraint>
1591+
<validator name="numeric" argument="--range 1-255"/>
1592+
</constraint>
1593+
</properties>
1594+
<defaultValue>100</defaultValue>
1595+
</leafNode>
1596+
<leafNode name="address">
1597+
<properties>
1598+
<help>Virtual IP address</help>
1599+
<valueHelp>
1600+
<format>ipv4</format>
1601+
<description>IPv4 address</description>
1602+
</valueHelp>
1603+
<valueHelp>
1604+
<format>ipv6</format>
1605+
<description>IPv6 address</description>
1606+
</valueHelp>
1607+
<constraint>
1608+
<validator name="ip-address"/>
1609+
</constraint>
1610+
<multi/>
1611+
</properties>
1612+
</leafNode>
1613+
<leafNode name="vrid">
1614+
<properties>
1615+
<help>Virtual router identifier</help>
1616+
<valueHelp>
1617+
<format>u32:1-255</format>
1618+
<description>Virtual router identifier</description>
1619+
</valueHelp>
1620+
<constraint>
1621+
<validator name="numeric" argument="--range 1-255"/>
1622+
</constraint>
1623+
</properties>
1624+
</leafNode>
1625+
</children>
1626+
</tagNode>
1627+
</children>
1628+
</node>
15201629
<tagNode name="kernel-interfaces" owner="${vyos_conf_scripts_dir}/vpp_kernel-interfaces.py">
15211630
<properties>
15221631
<help>VPP kernel interface settings</help>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0"?>
2+
<interfaceDefinition>
3+
<node name="show">
4+
<children>
5+
<node name="vpp">
6+
<children>
7+
<tagNode name="vrrp">
8+
<properties>
9+
<help>Show specified VPP VRRP group</help>
10+
<completionHelp>
11+
<path>vpp vrrp group</path>
12+
</completionHelp>
13+
</properties>
14+
<standalone>
15+
<help>Show VPP VRRP information</help>
16+
<command>sudo ${vyos_op_scripts_dir}/vpp_vrrp.py show_vrrp</command>
17+
</standalone>
18+
<command>sudo ${vyos_op_scripts_dir}/vpp_vrrp.py show_vrrp --group="$5"</command>
19+
</tagNode>
20+
</children>
21+
</node>
22+
</children>
23+
</node>
24+
</interfaceDefinition>

python/vyos/vpp/vrrp/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .vrrp import Vrrp
2+
3+
__all__ = ['Vrrp']

python/vyos/vpp/vrrp/vrrp.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#
2+
# Copyright (C) 2025 VyOS Inc.
3+
#
4+
# This program is free software; you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation; either version 2 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License along
15+
# with this program; if not, write to the Free Software Foundation, Inc.,
16+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
18+
from vyos.vpp import VPPControl
19+
20+
21+
class Vrrp:
22+
def __init__(self):
23+
self.vpp = VPPControl()
24+
25+
def add_vrrp_vr(self, interface, vrid, priority, interval, flags, addrs):
26+
"""Add new VRRP VR"""
27+
self.vpp.api.vrrp_vr_add_del(
28+
vr_id=vrid,
29+
is_add=True,
30+
sw_if_index=self.vpp.get_sw_if_index(interface),
31+
priority=priority,
32+
interval=interval * 100,
33+
flags=flags,
34+
n_addrs=len(addrs),
35+
addrs=addrs,
36+
)
37+
38+
def delete_vrrp_vr(self, interface, vrid, priority, interval, flags, addrs):
39+
"""Delete existing VRRP VR"""
40+
self.vpp.api.vrrp_vr_add_del(
41+
vr_id=vrid,
42+
is_add=False,
43+
sw_if_index=self.vpp.get_sw_if_index(interface),
44+
priority=priority,
45+
interval=interval * 100,
46+
flags=flags,
47+
n_addrs=len(addrs),
48+
addrs=addrs,
49+
)
50+
51+
def start_stop_proto_vrrp_vr(self, vrid, interface, is_ipv6, is_start):
52+
"""Start or shutdown the VRRP protocol for a VR"""
53+
self.vpp.api.vrrp_vr_start_stop(
54+
vr_id=vrid,
55+
sw_if_index=self.vpp.get_sw_if_index(interface),
56+
is_ipv6=is_ipv6,
57+
is_start=is_start,
58+
)
59+
60+
def set_vrrp_peers(self, interface, vrid, is_ipv6, addrs):
61+
"""Set unicast peers for a VR"""
62+
self.vpp.api.vrrp_vr_set_peers(
63+
sw_if_index=self.vpp.get_sw_if_index(interface),
64+
vr_id=vrid,
65+
is_ipv6=is_ipv6,
66+
n_addrs=len(addrs),
67+
addrs=addrs,
68+
)

smoketest/scripts/cli/test_vpp.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,93 @@ def test_17_vpp_nat(self):
13931393
_, out = rc_cmd('sudo vppctl show nat44 summary')
13941394
self.assertIn(f'max translations per thread: {sess_limit} fib 0', out)
13951395

1396+
def test_18_vpp_vrrp(self):
1397+
base_vrrp = base_path + ['vrrp', 'group']
1398+
addresses_first = ['192.0.10.1']
1399+
vrid_first = '10'
1400+
advertise_interval = '5'
1401+
priority = '150'
1402+
peer_addresses = ['192.0.2.11', '192.0.2.12']
1403+
addresses_second = ['2001:db8:1111::1', '2001:db8:1112::1']
1404+
vrid_second = '20'
1405+
addresses_third = ['192.0.20.1']
1406+
vrid_third = '30'
1407+
1408+
# Set VRRP group FIRST (ipv4)
1409+
self.cli_set(base_vrrp + ['FIRST', 'interface', interface])
1410+
self.cli_set(base_vrrp + ['FIRST', 'vrid', vrid_first])
1411+
self.cli_set(base_vrrp + ['FIRST', 'advertise-interval', advertise_interval])
1412+
self.cli_set(base_vrrp + ['FIRST', 'priority', priority])
1413+
for address in addresses_first:
1414+
self.cli_set(base_vrrp + ['FIRST', 'address', address])
1415+
for address in peer_addresses:
1416+
self.cli_set(base_vrrp + ['FIRST', 'peer-address', address])
1417+
1418+
# Interface should have IP address
1419+
# expect raise ConfigError
1420+
with self.assertRaises(ConfigSessionError):
1421+
self.cli_commit()
1422+
1423+
self.cli_set(['interfaces', 'ethernet', interface, 'address', '192.0.2.11/24'])
1424+
1425+
self.cli_commit()
1426+
1427+
# Check information for group FIRST
1428+
_, out = rc_cmd('sudo vppctl show vrrp vr')
1429+
self.assertIn(f'[0] sw_if_index 1 VR ID {vrid_first} IPv4', out)
1430+
self.assertIn('flags: preempt yes accept no unicast yes', out)
1431+
self.assertIn(f'priority: configured {priority}', out)
1432+
self.assertIn(f'timers: adv interval {advertise_interval}00', out)
1433+
self.assertIn(f'addresses {" ".join(addresses_first)}', out)
1434+
self.assertIn(f'peer addresses {" ".join(peer_addresses)}', out)
1435+
1436+
# Set VRRP group SECOND (ipv6)
1437+
self.cli_set(base_vrrp + ['SECOND', 'interface', interface])
1438+
self.cli_set(base_vrrp + ['SECOND', 'vrid', vrid_second])
1439+
for address in addresses_second:
1440+
self.cli_set(base_vrrp + ['SECOND', 'address', address])
1441+
1442+
self.cli_commit()
1443+
1444+
# Check information for group SECOND
1445+
_, out = rc_cmd('sudo vppctl show vrrp vr')
1446+
self.assertIn(f'[1] sw_if_index 1 VR ID {vrid_second} IPv6', out)
1447+
self.assertIn('flags: preempt yes accept no unicast no', out)
1448+
self.assertIn('priority: configured 100', out) # default priority
1449+
self.assertIn('timers: adv interval 100', out) # default advertise-interval
1450+
self.assertIn(f'addresses {" ".join(addresses_second)}', out)
1451+
1452+
# VRID can only be used once on interface with the same address family
1453+
self.cli_set(base_vrrp + ['THIRD', 'interface', interface])
1454+
self.cli_set(base_vrrp + ['THIRD', 'vrid', vrid_first])
1455+
self.cli_set(base_vrrp + ['THIRD', 'no-preempt'])
1456+
for address in addresses_third:
1457+
self.cli_set(base_vrrp + ['THIRD', 'address', address])
1458+
1459+
# expect raise ConfigError
1460+
with self.assertRaises(ConfigSessionError):
1461+
self.cli_commit()
1462+
1463+
self.cli_set(base_vrrp + ['THIRD', 'vrid', vrid_third])
1464+
1465+
# Virtual address should not be used in another group
1466+
self.cli_set(base_vrrp + ['THIRD', 'address', addresses_first[0]])
1467+
# expect raise ConfigError
1468+
with self.assertRaises(ConfigSessionError):
1469+
self.cli_commit()
1470+
1471+
self.cli_delete(base_vrrp + ['THIRD', 'address', addresses_first[0]])
1472+
1473+
self.cli_commit()
1474+
1475+
# Check information for group THIRD
1476+
_, out = rc_cmd('sudo vppctl show vrrp vr')
1477+
self.assertIn(f'[2] sw_if_index 1 VR ID {vrid_third} IPv4', out)
1478+
self.assertIn('flags: preempt no accept no unicast no', out)
1479+
self.assertIn('priority: configured 100', out) # default priority
1480+
self.assertIn('timers: adv interval 100', out) # default advertise-interval
1481+
self.assertIn(f'addresses {" ".join(addresses_third)}', out)
1482+
13961483

13971484
if __name__ == '__main__':
13981485
unittest.main(verbosity=2)

src/conf_mode/vpp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ def get_config(config=None):
297297
# Return to config dictionary
298298
config['persist_config'] = eth_ifaces_persist
299299

300+
# VRRP dependency
301+
if conf.exists(['vpp', 'vrrp', 'group']):
302+
set_dependents('vpp_vrrp', conf)
303+
300304
return config
301305

302306

0 commit comments

Comments
 (0)