diff --git a/ovn_bgp_agent/drivers/openstack/utils/ovs.py b/ovn_bgp_agent/drivers/openstack/utils/ovs.py index ce1f6440..0c151f39 100644 --- a/ovn_bgp_agent/drivers/openstack/utils/ovs.py +++ b/ovn_bgp_agent/drivers/openstack/utils/ovs.py @@ -241,6 +241,12 @@ def del_flow(flow, bridge, cookie): ['--strict', 'del-flows', bridge, f]) +def add_fdb_entry(bridge: str, port: str, mac: str, vlan: int = 0): + ovn_bgp_agent.privileged.ovs_vsctl.ovs_appctl( + ['fdb/add', bridge, port, str(vlan), mac] + ) + + def get_flow_info(flow): # example: # cookie=0x3e7, duration=85.005s, table=0, n_packets=0, diff --git a/ovn_bgp_agent/drivers/openstack/utils/wire.py b/ovn_bgp_agent/drivers/openstack/utils/wire.py index b364d0e0..7bca0c8f 100644 --- a/ovn_bgp_agent/drivers/openstack/utils/wire.py +++ b/ovn_bgp_agent/drivers/openstack/utils/wire.py @@ -266,6 +266,7 @@ def _ensure_base_wiring_config_ovn(ovs_idl, ovn_idl): # External Bridge connecting to the external networks _ensure_ovn_network_link_external(ovn_idl, network, ip, mac) _ensure_ingress_flows(bridge, mac, network, provider_cidrs) + _ensure_fdb_entry(bridge, mac) return ovn_bridge_mappings, flows_info @@ -482,6 +483,18 @@ def _ensure_ingress_flows(bridge, mac, switch_name, provider_cidrs): ovs.ensure_flow(bridge, flow) +def _ensure_fdb_entry(bridge, mac): + """Ensure static FDB entries for internal ports of a fabric bridge. + + Fabric-facing bridges need to have a static MAC entry to avoid dynamic + re-learning in their FDB as this will lead to drop flows being generated + by OVS due to flow table misses. + """ + # The bridge name is also the name of its internal port. The MAC address + # is the MAC address of the bridge itself. + ovs.add_fdb_entry(bridge, bridge, mac, 0) + + def _ensure_egress_flows(bridge, patch_ports): # outcomming traffic flows # patch=`ovs-ofctl show br-provider | grep patch | grep provnet | diff --git a/ovn_bgp_agent/privileged/ovs_vsctl.py b/ovn_bgp_agent/privileged/ovs_vsctl.py index efc3b612..6bb3041d 100644 --- a/ovn_bgp_agent/privileged/ovs_vsctl.py +++ b/ovn_bgp_agent/privileged/ovs_vsctl.py @@ -42,3 +42,7 @@ def ovs_ofctl(args, timeout=None): return ovs_cmd('ovs-ofctl', args, timeout) except processutils.ProcessExecutionError: return ovs_cmd('ovs-ofctl', args + ['-O', 'OpenFlow13'], timeout) + + +def ovs_appctl(args): + return ovs_cmd('ovs-appctl', args) diff --git a/ovn_bgp_agent/tests/unit/privileged/test_ovs_vsctl.py b/ovn_bgp_agent/tests/unit/privileged/test_ovs_vsctl.py index 6eb977cf..9312bb44 100644 --- a/ovn_bgp_agent/tests/unit/privileged/test_ovs_vsctl.py +++ b/ovn_bgp_agent/tests/unit/privileged/test_ovs_vsctl.py @@ -103,3 +103,10 @@ class TestPrivilegedOvsVsctl(test_base.TestCase): mock.call('ovs-ofctl', 'add-flow', 'br-ex', 'dummy-flow', '-O', 'OpenFlow13')] self.mock_exc.assert_has_calls(calls) + + def test_ovs_appctl(self): + ovs_vsctl.ovs_appctl( + ['fdb/add', 'br-fabric', 'br-fabric', '0', 'de:ad:be:ef:ca:fe']) + self.mock_exc.assert_called_once_with( + 'ovs-appctl', 'fdb/add', 'br-fabric', 'br-fabric', + '0', 'de:ad:be:ef:ca:fe')