From b0aeb2a9c196ea7048545e38e6f3c5759ff4a5ac Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 27 Apr 2022 21:42:22 +0200 Subject: arp: T4397: change CLI syntax to support interface and VRF bound ARP entries * set protocols static arp interface eth0 address 192.0.2.1 mac 01:23:45:67:89:01 --- interface-definitions/protocols-static-arp.xml.in | 44 +++++++---- smoketest/configs/basic-vyos | 32 +++++++- smoketest/scripts/cli/test_protocols_static_arp.py | 16 ++-- src/conf_mode/arp.py | 28 ++++--- src/migration-scripts/system/23-to-24 | 85 ++++++++++++++++++++++ 5 files changed, 170 insertions(+), 35 deletions(-) create mode 100755 src/migration-scripts/system/23-to-24 diff --git a/interface-definitions/protocols-static-arp.xml.in b/interface-definitions/protocols-static-arp.xml.in index e5e8a9ad9..8b1b3b5e1 100644 --- a/interface-definitions/protocols-static-arp.xml.in +++ b/interface-definitions/protocols-static-arp.xml.in @@ -4,32 +4,46 @@ - + Static ARP translation - - ipv4 - IPv4 destination address - - - - - + - Translation MAC address + Interface configuration + + + - macaddr - Hardware (MAC) address + txt + Interface name - + - + + + + IP address for static ARP entry + + ipv4 + IPv4 destination address + + + + + + + #include + #include + + + + - + diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos index 3d62d269c..e6f89954f 100644 --- a/smoketest/configs/basic-vyos +++ b/smoketest/configs/basic-vyos @@ -6,16 +6,26 @@ interfaces { speed auto } ethernet eth1 { - address 100.64.0.0/31 duplex auto smp-affinity auto speed auto } ethernet eth2 { - address 100.100.0.1/24 duplex auto smp-affinity auto speed auto + vif 100 { + address 100.100.0.1/24 + } + vif-s 200 { + address 100.64.200.254/24 + vif-c 201 { + address 100.64.201.254/24 + } + vif-c 202 { + address 100.64.202.254/24 + } + } } loopback lo { } @@ -40,6 +50,24 @@ protocols { arp 100.100.0.4 { hwaddr 00:50:00:00:02:04 } + arp 100.64.200.1 { + hwaddr 00:50:00:00:00:01 + } + arp 100.64.200.2 { + hwaddr 00:50:00:00:00:02 + } + arp 100.64.201.10 { + hwaddr 00:50:00:00:00:10 + } + arp 100.64.201.20 { + hwaddr 00:50:00:00:00:20 + } + arp 100.64.202.30 { + hwaddr 00:50:00:00:00:30 + } + arp 100.64.202.40 { + hwaddr 00:50:00:00:00:40 + } route 0.0.0.0/0 { next-hop 100.64.0.1 { } diff --git a/smoketest/scripts/cli/test_protocols_static_arp.py b/smoketest/scripts/cli/test_protocols_static_arp.py index 6663ade96..b61d8f854 100755 --- a/smoketest/scripts/cli/test_protocols_static_arp.py +++ b/smoketest/scripts/cli/test_protocols_static_arp.py @@ -52,16 +52,16 @@ class TestARP(VyOSUnitTestSHIM.TestCase): def test_static_arp(self): test_data = { - '192.0.2.10' : { 'lladdr' : '00:01:02:03:04:0a' }, - '192.0.2.11' : { 'lladdr' : '00:01:02:03:04:0b' }, - '192.0.2.12' : { 'lladdr' : '00:01:02:03:04:0c' }, - '192.0.2.13' : { 'lladdr' : '00:01:02:03:04:0d' }, - '192.0.2.14' : { 'lladdr' : '00:01:02:03:04:0e' }, - '192.0.2.15' : { 'lladdr' : '00:01:02:03:04:0f' }, + '192.0.2.10' : { 'mac' : '00:01:02:03:04:0a' }, + '192.0.2.11' : { 'mac' : '00:01:02:03:04:0b' }, + '192.0.2.12' : { 'mac' : '00:01:02:03:04:0c' }, + '192.0.2.13' : { 'mac' : '00:01:02:03:04:0d' }, + '192.0.2.14' : { 'mac' : '00:01:02:03:04:0e' }, + '192.0.2.15' : { 'mac' : '00:01:02:03:04:0f' }, } for host, host_config in test_data.items(): - self.cli_set(base_path + [host, 'hwaddr', host_config['lladdr']]) + self.cli_set(base_path + ['interface', interface, 'address', host, 'mac', host_config['mac']]) self.cli_commit() @@ -76,7 +76,7 @@ class TestARP(VyOSUnitTestSHIM.TestCase): continue if entry['dst'] == host: - self.assertEqual(entry['lladdr'], host_config['lladdr']) + self.assertEqual(entry['lladdr'], host_config['mac']) self.assertEqual(entry['dev'], interface) found = True diff --git a/src/conf_mode/arp.py b/src/conf_mode/arp.py index 51a08bee5..1cd8f5451 100755 --- a/src/conf_mode/arp.py +++ b/src/conf_mode/arp.py @@ -30,9 +30,12 @@ def get_config(config=None): conf = Config() base = ['protocols', 'static', 'arp'] - arp = conf.get_config_dict(base) - tmp = node_changed(conf, base) - if tmp: arp.update({'removed' : node_changed(conf, base)}) + arp = conf.get_config_dict(base, get_first_key=True) + + if 'interface' in arp: + for interface in arp['interface']: + tmp = node_changed(conf, base + ['interface', interface, 'address'], recursive=True) + if tmp: arp['interface'][interface].update({'address_old' : tmp}) return arp @@ -46,14 +49,19 @@ def apply(arp): if not arp: return None - if 'removed' in arp: - for host in arp['removed']: - call(f'arp --delete {host}') + if 'interface' in arp: + for interface, interface_config in arp['interface'].items(): + # Delete old static ARP assignments first + if 'address_old' in interface_config: + for address in interface_config['address_old']: + call(f'ip neigh del {address} dev {interface}') - if 'arp' in arp: - for host, host_config in arp['arp'].items(): - mac = host_config['hwaddr'] - call(f'arp --set {host} {mac}') + # Add new static ARP entries to interface + if 'address' not in interface_config: + continue + for address, address_config in interface_config['address'].items(): + mac = address_config['mac'] + call(f'ip neigh add {address} lladdr {mac} dev {interface}') if __name__ == '__main__': try: diff --git a/src/migration-scripts/system/23-to-24 b/src/migration-scripts/system/23-to-24 new file mode 100755 index 000000000..5ea71d51a --- /dev/null +++ b/src/migration-scripts/system/23-to-24 @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +from ipaddress import ip_interface +from ipaddress import ip_address +from sys import exit, argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'static', 'arp'] +tmp_base = ['protocols', 'static', 'arp-tmp'] +config = ConfigTree(config_file) + +def fixup_cli(config, path, interface): + if config.exists(path + ['address']): + for address in config.return_values(path + ['address']): + tmp = ip_interface(address) + if ip_address(host) in tmp.network.hosts(): + mac = config.return_value(tmp_base + [host, 'hwaddr']) + iface_path = ['protocols', 'static', 'arp', 'interface'] + config.set(iface_path + [interface, 'address', host, 'mac'], value=mac) + config.set_tag(iface_path) + config.set_tag(iface_path + [interface, 'address']) + continue + +if not config.exists(base): + # Nothing to do + exit(0) + +# We need a temporary copy of the config tree as the original one needs to be +# deleted first due to a change iun thge tagNode structure. +config.copy(base, tmp_base) +config.delete(base) + +for host in config.list_nodes(tmp_base): + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + if_base = ['interfaces', type, interface] + fixup_cli(config, if_base, interface) + + if config.exists(if_base + ['vif']): + for vif in config.list_nodes(if_base + ['vif']): + vif_base = ['interfaces', type, interface, 'vif', vif] + fixup_cli(config, vif_base, f'{interface}.{vif}') + + if config.exists(if_base + ['vif-s']): + for vif_s in config.list_nodes(if_base + ['vif-s']): + vif_s_base = ['interfaces', type, interface, 'vif-s', vif_s] + fixup_cli(config, vif_s_base, f'{interface}.{vif_s}') + + if config.exists(if_base + ['vif-s', vif_s, 'vif-c']): + for vif_c in config.list_nodes(if_base + ['vif-s', vif_s, 'vif-c']): + vif_c_base = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c', vif_c] + fixup_cli(config, vif_c_base, f'{interface}.{vif_s}.{vif_c}') + +config.delete(tmp_base) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) -- cgit v1.2.3