fix(monitor): place guests behind the firewall on their OVS bridge

On an OVS bridge Proxmox links the firewall bridge fwbrNiM through its
own internal port fwlnNoM and creates no fwprNpM peer, so the guest was
shown on fwbrNiM. Check both links. Verified on an OVS 3.5 host with a
firewalled container.
This commit is contained in:
MacRimi
2026-11-05 18:40:33 +01:00
parent 163317f727
commit 3090f1d772
2 changed files with 17 additions and 11 deletions
+8 -4
View File
@@ -6266,11 +6266,15 @@ def _resolve_bridge_owner(iface_name):
if not master: if not master:
return None return None
if master.startswith('fwbr'): if master.startswith('fwbr'):
fwpr = re.sub(r'^fwbr', 'fwpr', master) # A Linux bridge reaches the firewall bridge through fwprNpM; an OVS
fwpr = re.sub(r'i(\d+)$', r'p\1', fwpr) # bridge through its own internal port fwlnNoM.
suffix = master[len('fwbr'):]
links = (re.sub(r'i(\d+)$', r'p\1', 'fwpr' + suffix),
re.sub(r'i(\d+)$', r'o\1', 'fwln' + suffix))
try: try:
if os.path.exists(f'/sys/class/net/{fwpr}'): for link in links:
real = ovs_owners.get(fwpr) or _iface_master(fwpr) if os.path.exists(f'/sys/class/net/{link}'):
real = ovs_owners.get(link) or _iface_master(link)
if real: if real:
return real return real
except Exception: except Exception:
+7 -5
View File
@@ -30,7 +30,8 @@ def table(headings, *rows):
def fixture(): def fixture():
# Mixed Linux/OVS host, named bond without a kernel netdev, tagged # Mixed Linux/OVS host, named bond without a kernel netdev, tagged
# internal port, direct guest and guest behind a Proxmox firewall. # internal port, direct guest and guest behind a Proxmox firewall,
# which on OVS links its fwbr bridge through the internal port fwlnNoM.
return ( return (
table('_uuid,name,ports', ['b1', 'switch0', refs('p0', 'p1', 'p2', 'p3', 'p4', 'p5')]), table('_uuid,name,ports', ['b1', 'switch0', refs('p0', 'p1', 'p2', 'p3', 'p4', 'p5')]),
table('_uuid,name,interfaces,bond_mode,tag,trunks', table('_uuid,name,interfaces,bond_mode,tag,trunks',
@@ -39,10 +40,10 @@ def fixture():
['p2', 'uplink', refs('i2', 'i3'), 'balance-tcp', ['set', []], ['set', []]], ['p2', 'uplink', refs('i2', 'i3'), 'balance-tcp', ['set', []], ['set', []]],
['p3', 'vlan100', uuid('i4'), '', 100, ['set', []]], ['p3', 'vlan100', uuid('i4'), '', 100, ['set', []]],
['p4', 'tap103i0', uuid('i5'), '', 100, ['set', []]], ['p4', 'tap103i0', uuid('i5'), '', 100, ['set', []]],
['p5', 'fwpr115p0', uuid('i6'), '', 10, ['set', []]]), ['p5', 'fwln115o0', uuid('i6'), '', 10, ['set', []]]),
table('_uuid,name,type', ['i0', 'switch0', 'internal'], ['i1', 'eno1', ''], table('_uuid,name,type', ['i0', 'switch0', 'internal'], ['i1', 'eno1', ''],
['i2', 'eno2', ''], ['i3', 'eno3', ''], ['i4', 'vlan100', 'internal'], ['i2', 'eno2', ''], ['i3', 'eno3', ''], ['i4', 'vlan100', 'internal'],
['i5', 'tap103i0', ''], ['i6', 'fwpr115p0', 'internal'])) ['i5', 'tap103i0', ''], ['i6', 'fwln115o0', 'internal']))
class OvsTopology(unittest.TestCase): class OvsTopology(unittest.TestCase):
@@ -114,7 +115,7 @@ class OvsTopology(unittest.TestCase):
'_ensure_net_rate_sampler': lambda: None, '_compute_per_nic_rates_live': lambda _: {}, '_ensure_net_rate_sampler': lambda: None, '_compute_per_nic_rates_live': lambda _: {},
'_is_bond_master': lambda _: False, '_is_physical_dev': lambda name: name.startswith('eno'), '_is_bond_master': lambda _: False, '_is_physical_dev': lambda name: name.startswith('eno'),
'_ethtool_max_speed': lambda _: 1000, '_ethtool_max_speed': lambda _: 1000,
'_iface_master': lambda name: {'tap115i0': 'fwbr115i0', 'fwpr115p0': 'ovs-system'}.get(name), '_iface_master': lambda name: {'tap115i0': 'fwbr115i0', 'fwln115o0': 'ovs-system'}.get(name),
'extract_vmid_from_interface': lambda name: (103 if '103' in name else 115, 'vm'), 'extract_vmid_from_interface': lambda name: (103 if '103' in name else 115, 'vm'),
'get_bridge_info': lambda _: {'members': [], 'physical_interface': None, 'get_bridge_info': lambda _: {'members': [], 'physical_interface': None,
'physical_duplex': 'unknown', 'bond_slaves': []}} 'physical_duplex': 'unknown', 'bond_slaves': []}}
@@ -123,7 +124,8 @@ class OvsTopology(unittest.TestCase):
def test_api_preserves_linux_bridge_and_assigns_ovs_guests_and_bond(self): def test_api_preserves_linux_bridge_and_assigns_ovs_guests_and_bond(self):
ns = self.api_namespace() ns = self.api_namespace()
with patch('os.path.exists', return_value=True): # OVS creates no fwpr peer for the firewall bridge.
with patch('os.path.exists', side_effect=lambda path: 'fwpr' not in str(path)):
result = ns['get_network_info']() result = ns['get_network_info']()
self.assertNotIn('error', result) self.assertNotIn('error', result)
self.assertEqual({i['name'] for i in result['bridge_interfaces']}, {'switch0', 'vmbr1'}) self.assertEqual({i['name'] for i in result['bridge_interfaces']}, {'switch0', 'vmbr1'})