Skip to content

Commit d8edcca

Browse files
committed
T7731: Static ARP entries are missing after an interface status change
1 parent 21d509d commit d8edcca

File tree

14 files changed

+149
-4
lines changed

14 files changed

+149
-4
lines changed

data/config-mode-dependencies/vyos-1x.json

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,40 @@
88
"group_resync": ["system_conntrack", "nat", "policy_route", "load-balancing_wan"]
99
},
1010
"interfaces_bonding": {
11-
"ethernet": ["interfaces_ethernet"]
11+
"ethernet": ["interfaces_ethernet"],
12+
"static_arp": ["protocols_static_arp"]
1213
},
1314
"interfaces_bridge": {
1415
"vxlan": ["interfaces_vxlan"],
15-
"wlan": ["interfaces_wireless"]
16+
"wlan": ["interfaces_wireless"],
17+
"static_arp": ["protocols_static_arp"]
18+
},
19+
"interfaces_ethernet": {
20+
"static_arp": ["protocols_static_arp"]
21+
},
22+
"interfaces_geneve": {
23+
"static_arp": ["protocols_static_arp"]
24+
},
25+
"interfaces_l2tpv3": {
26+
"static_arp": ["protocols_static_arp"]
27+
},
28+
"interfaces_macsec": {
29+
"static_arp": ["protocols_static_arp"]
30+
},
31+
"interfaces_pseudo_ethernet": {
32+
"static_arp": ["protocols_static_arp"]
33+
},
34+
"interfaces_virtual_ethernet": {
35+
"static_arp": ["protocols_static_arp"]
36+
},
37+
"interfaces_vxlan": {
38+
"static_arp": ["protocols_static_arp"]
39+
},
40+
"interfaces_wireless": {
41+
"static_arp": ["protocols_static_arp"]
42+
},
43+
"interfaces_wwan": {
44+
"static_arp": ["protocols_static_arp"]
1645
},
1746
"interfaces_wireguard": {
1847
"vxlan": ["interfaces_vxlan"]

python/vyos/configdict.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,16 @@ def get_interface_dict(config, base, ifname='', recursive_defaults=True, with_pk
613613

614614
# Check vif, vif-s/vif-c VLAN interfaces for removal
615615
dict = get_removed_vlans(config, base + [ifname], dict)
616+
617+
# Checks for the presence of static ARP entries on a given interface or VLAN
618+
static_arp = config.get_config_dict(
619+
['protocols', 'static', 'arp', 'interface'],
620+
key_mangling=('-', '_'),
621+
get_first_key=True,
622+
)
623+
if any(key == ifname or key.startswith(f'{ifname}.') for key in static_arp.keys()):
624+
dict.update({'static_arp': {}})
625+
616626
return ifname, dict
617627

618628
def get_vlan_ids(interface):

smoketest/scripts/cli/test_vpp.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,30 @@ def test_20_kernel_options_hugepages(self):
15951595
self.assertIn(f' hugepagesz={hp_size_1g} hugepages={hp_count_1g}', tmp)
15961596
self.assertIn(f' hugepagesz={hp_size_2m} hugepages={hp_count_2m}', tmp)
15971597

1598+
def test_21_static_arp(self):
1599+
host = '192.0.2.10'
1600+
mac = '00:01:02:03:04:0a'
1601+
path_static_arp = ['protocols', 'static', 'arp']
1602+
1603+
self.cli_set(['interfaces', 'ethernet', interface, 'address', '192.0.2.1/24'])
1604+
self.cli_set(
1605+
path_static_arp + ['interface', interface, 'address', host, 'mac', mac]
1606+
)
1607+
self.cli_commit()
1608+
1609+
# Change VPP configuration
1610+
self.cli_set(base_path + ['settings', 'unix', 'poll-sleep-usec', '50'])
1611+
1612+
# Ensure arp entry is not disappeared
1613+
_, neighbors = rc_cmd('sudo ip neighbor')
1614+
self.assertIn(f'{host} dev {interface} lladdr {mac}', neighbors)
1615+
1616+
# Check VPP IP neighbors
1617+
_, vpp_neighbors = rc_cmd('sudo vppctl show ip neighbors')
1618+
self.assertRegex(vpp_neighbors, rf'{host}\s+S\s+{mac}\s+{interface}')
1619+
1620+
self.cli_delete(path_static_arp)
1621+
15981622

15991623
if __name__ == '__main__':
16001624
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())

src/conf_mode/interfaces_bonding.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ def get_config(config=None):
175175
tmp = has_vrf_configured(conf, interface)
176176
if tmp: bond['member']['interface'][interface].update({'has_vrf' : ''})
177177

178+
# Protocols static arp dependency
179+
if 'static_arp' in bond:
180+
set_dependents('static_arp', conf)
181+
178182
return bond
179183

180184

@@ -279,7 +283,7 @@ def apply(bond):
279283
else:
280284
b.update(bond)
281285

282-
if dict_search('member.interface_remove', bond):
286+
if dict_search('member.interface_remove', bond) or 'static_arp' in bond:
283287
try:
284288
call_dependents()
285289
except ConfigError:

src/conf_mode/interfaces_bridge.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ def get_config(config=None):
124124
if len(bridge['member']) == 0:
125125
del bridge['member']
126126

127+
# Protocols static arp dependency
128+
if 'static_arp' in bridge:
129+
set_dependents('static_arp', conf)
130+
127131
return bridge
128132

129133
def verify(bridge):
@@ -220,6 +224,9 @@ def apply(bridge):
220224
except ConfigError:
221225
raise ConfigError(f'Error updating member interface {interface} configuration after changing bridge!')
222226

227+
if 'static_arp' in bridge:
228+
call_dependents()
229+
223230
return None
224231

225232
if __name__ == '__main__':

src/conf_mode/interfaces_ethernet.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
from vyos.base import Warning
2222
from vyos.config import Config
23+
from vyos.configdep import set_dependents
24+
from vyos.configdep import call_dependents
2325
from vyos.configdict import get_interface_dict
2426
from vyos.configdict import is_node_changed
2527
from vyos.configdict import get_flowtable_interfaces
@@ -171,6 +173,10 @@ def get_config(config=None):
171173

172174
ethernet['flowtable_interfaces'] = get_flowtable_interfaces(conf)
173175

176+
# Protocols static arp dependency
177+
if 'static_arp' in ethernet:
178+
set_dependents('static_arp', conf)
179+
174180
return ethernet
175181

176182
def verify_speed_duplex(ethernet: dict, ethtool: Ethtool):
@@ -368,6 +374,7 @@ def apply(ethernet):
368374
e.remove()
369375
else:
370376
e.update(ethernet)
377+
call_dependents()
371378
return None
372379

373380
if __name__ == '__main__':

src/conf_mode/interfaces_geneve.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from sys import exit
1818

1919
from vyos.config import Config
20+
from vyos.configdep import set_dependents
21+
from vyos.configdep import call_dependents
2022
from vyos.configdict import get_interface_dict
2123
from vyos.configdict import is_node_changed
2224
from vyos.configverify import verify_address
@@ -51,6 +53,10 @@ def get_config(config=None):
5153
if is_node_changed(conf, base + [ifname, cli_option]):
5254
geneve.update({'rebuild_required': {}})
5355

56+
# Protocols static arp dependency
57+
if 'static_arp' in geneve:
58+
set_dependents('static_arp', conf)
59+
5460
return geneve
5561

5662
def verify(geneve):
@@ -90,6 +96,8 @@ def apply(geneve):
9096
g = GeneveIf(**geneve)
9197
g.update(geneve)
9298

99+
call_dependents()
100+
93101
return None
94102

95103

src/conf_mode/interfaces_l2tpv3.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from sys import exit
1818

1919
from vyos.config import Config
20+
from vyos.configdep import set_dependents
21+
from vyos.configdep import call_dependents
2022
from vyos.configdict import get_interface_dict
2123
from vyos.configdict import leaf_node_changed
2224
from vyos.configverify import verify_address
@@ -56,6 +58,10 @@ def get_config(config=None):
5658
tmp = leaf_node_changed(conf, base + [ifname, 'session-id'])
5759
l2tpv3.update({'session_id': tmp[0]})
5860

61+
# Protocols static arp dependency
62+
if 'static_arp' in l2tpv3:
63+
set_dependents('static_arp', conf)
64+
5965
return l2tpv3
6066

6167
def verify(l2tpv3):
@@ -100,6 +106,8 @@ def apply(l2tpv3):
100106
l = L2TPv3If(**l2tpv3)
101107
l.update(l2tpv3)
102108

109+
call_dependents()
110+
103111
return None
104112

105113
if __name__ == '__main__':

src/conf_mode/interfaces_macsec.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from sys import exit
2020

2121
from vyos.config import Config
22+
from vyos.configdep import set_dependents
23+
from vyos.configdep import call_dependents
2224
from vyos.configdict import get_interface_dict
2325
from vyos.configdict import is_node_changed
2426
from vyos.configdict import is_source_interface
@@ -78,6 +80,10 @@ def get_config(config=None):
7880
tmp = is_source_interface(conf, macsec['source_interface'], ['macsec', 'pseudo-ethernet'])
7981
if tmp and tmp != ifname: macsec.update({'is_source_interface' : tmp})
8082

83+
# Protocols static arp dependency
84+
if 'static_arp' in macsec:
85+
set_dependents('static_arp', conf)
86+
8187
return macsec
8288

8389

@@ -193,6 +199,8 @@ def apply(macsec):
193199
if not is_systemd_service_running(systemd_service) or 'shutdown_required' in macsec:
194200
call(f'systemctl reload-or-restart {systemd_service}')
195201

202+
call_dependents()
203+
196204
return None
197205

198206

src/conf_mode/interfaces_pseudo-ethernet.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
from sys import exit
1818

1919
from vyos.config import Config
20+
from vyos.configdep import set_dependents
21+
from vyos.configdep import call_dependents
2022
from vyos.configdict import get_interface_dict
21-
from vyos.configdict import is_node_changed
2223
from vyos.configdict import is_source_interface
2324
from vyos.configdict import is_node_changed
2425
from vyos.configverify import verify_vrf
@@ -61,6 +62,10 @@ def get_config(config=None):
6162
tmp = is_source_interface(conf, peth['source_interface'], ['macsec'])
6263
if tmp and tmp != ifname: peth.update({'is_source_interface' : tmp})
6364

65+
# Protocols static arp dependency
66+
if 'static_arp' in peth:
67+
set_dependents('static_arp', conf)
68+
6469
return peth
6570

6671
def verify(peth):
@@ -95,6 +100,8 @@ def apply(peth):
95100
p = MACVLANIf(**peth)
96101
p.update(peth)
97102

103+
call_dependents()
104+
98105
return None
99106

100107
if __name__ == '__main__':

0 commit comments

Comments
 (0)