diff --git a/doc/source/admin/config-ovsfwdriver.rst b/doc/source/admin/config-ovsfwdriver.rst index e915700b462..061c77cea6c 100644 --- a/doc/source/admin/config-ovsfwdriver.rst +++ b/doc/source/admin/config-ovsfwdriver.rst @@ -88,6 +88,18 @@ not true and there may be slight differences between those drivers. | (please check [3]_ for details) | | rule. | +----------------------------------------+-----------------------+-----------------------+ + +Permitted ethertypes +~~~~~~~~~~~~~~~~~~~~ + +The OVS Firewall blocks traffic that does not have either the IPv4 or IPv6 +ethertypes at present. This is a behavior change compared to the +"iptables_hybrid" firewall, which only operates on IP packets and thus does +not address other ethertypes. With the configuration option +``permitted_ethertypes`` it is possible to define a set of allowed ethertypes. +Any traffic with these allowed ethertypes with destination to a local port or +generated from a local port and MAC address, will be allowed. + References ~~~~~~~~~~ diff --git a/neutron/agent/linux/openvswitch_firewall/firewall.py b/neutron/agent/linux/openvswitch_firewall/firewall.py index b57658b2321..992d1754fbf 100644 --- a/neutron/agent/linux/openvswitch_firewall/firewall.py +++ b/neutron/agent/linux/openvswitch_firewall/firewall.py @@ -1371,6 +1371,25 @@ def _initialize_ingress(self, port): actions='output:{:d}'.format(port.ofport) ) + # Allow custom ethertypes + for permitted_ethertype in self.permitted_ethertypes: + if permitted_ethertype[:2] == '0x': + try: + hex_ethertype = hex(int(permitted_ethertype, base=16)) + self._add_flow( + table=ovs_consts.BASE_INGRESS_TABLE, + priority=100, + dl_type=hex_ethertype, + reg_port=port.ofport, + actions='output:{:d}'.format(port.ofport) + ) + continue + except ValueError: + pass + LOG.warning('Custom ethertype %(permitted_ethertype)s is not ' + 'a hexadecimal number.', + {'permitted_ethertype': permitted_ethertype}) + self._initialize_ingress_ipv6_icmp(port) # DHCP offers diff --git a/neutron/common/ovn/constants.py b/neutron/common/ovn/constants.py index 8ac8bfc91af..93e9035c7d7 100644 --- a/neutron/common/ovn/constants.py +++ b/neutron/common/ovn/constants.py @@ -372,8 +372,6 @@ LSP_OPTIONS_MCAST_FLOOD = 'mcast_flood' LRP_OPTIONS_RESIDE_REDIR_CH = 'reside-on-redirect-chassis' -LRP_OPTIONS_REDIRECT_TYPE = 'redirect-type' -BRIDGE_REDIRECT_TYPE = "bridged" # Port Binding types PB_TYPE_VIRTUAL = 'virtual' diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index 0b56b6cce88..492939bdca8 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -407,7 +407,7 @@ def create_network_db(self, context, network): args = {'tenant_id': project_id, 'id': n.get('id') or uuidutils.generate_uuid(), 'name': n['name'], - 'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU), + 'mtu': n.get('mtu', 0), 'admin_state_up': n['admin_state_up'], 'status': n.get('status', constants.NET_STATUS_ACTIVE), 'description': n.get('description')} diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py index a25e75bcb4b..a6c1f1c628e 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py @@ -831,7 +831,10 @@ def check_vlan_distributed_ports(self): # Get router ports belonging to VLAN networks vlan_nets = self._ovn_client._plugin.get_networks( context, {pnet.NETWORK_TYPE: [n_const.TYPE_VLAN]}) - vlan_net_ids = [vn['id'] for vn in vlan_nets] + # FIXME(ltomasbo): Once Bugzilla 2162756 is fixed the + # is_provider_network check should be removed + vlan_net_ids = [vn['id'] for vn in vlan_nets + if not utils.is_provider_network(vn)] router_ports = self._ovn_client._plugin.get_ports( context, {'network_id': vlan_net_ids, 'device_owner': n_const.ROUTER_PORT_OWNERS}) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index d319e7d5cc7..984e88d6bbd 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -1554,29 +1554,21 @@ def _gen_router_port_options(self, port, network=None): if network is None: network = self._plugin.get_network(admin_context, port['network_id']) - # For VLAN type networks we need to set the # "reside-on-redirect-chassis" option so the routing for this # logical router port is centralized in the chassis hosting the # distributed gateway port. # https://github.com/openvswitch/ovs/commit/85706c34d53d4810f54bec1de662392a3c06a996 + # FIXME(ltomasbo): Once Bugzilla 2162756 is fixed the + # is_provider_network check should be removed if network.get(pnet.NETWORK_TYPE) == const.TYPE_VLAN: options[ovn_const.LRP_OPTIONS_RESIDE_REDIR_CH] = ( - 'false' if ovn_conf.is_ovn_distributed_floating_ip() + 'false' if (ovn_conf.is_ovn_distributed_floating_ip() and + not utils.is_provider_network(network)) else 'true') is_gw_port = const.DEVICE_OWNER_ROUTER_GW == port.get( 'device_owner') - - # NOTE(ltomasbo): For VLAN type networks connected through the gateway - # port there is a need to set the redirect-type option to bridge to - # ensure traffic is not centralized through the controller. - # For geneve based tenant networks it won't have any effect as it only - # applies to network with a localnet associated to it - if is_gw_port and ovn_conf.is_ovn_distributed_floating_ip(): - options[ovn_const.LRP_OPTIONS_REDIRECT_TYPE] = ( - ovn_const.BRIDGE_REDIRECT_TYPE) - if is_gw_port and ovn_conf.is_ovn_emit_need_to_frag_enabled(): try: router_ports = self._get_router_ports(admin_context, @@ -2377,17 +2369,26 @@ def _find_metadata_port_ip(self, context, subnet): return fixed_ip['ip_address'] def create_metadata_port(self, context, network): - if ovn_conf.is_ovn_metadata_enabled(): - metadata_port = self._find_metadata_port(context, network['id']) - if not metadata_port: - # Create a neutron port for DHCP/metadata services - port = {'port': - {'network_id': network['id'], + if not ovn_conf.is_ovn_metadata_enabled(): + return + + if self._find_metadata_port(context, network['id']): + return + + # Create a neutron port for DHCP/metadata services + filters = {'network_id': [network['id']]} + subnets = self._plugin.get_subnets(context, filters=filters) + fixed_ips = [{'subnet_id': s['id']} + for s in subnets if s['enable_dhcp']] + port = {'port': {'network_id': network['id'], 'tenant_id': network['project_id'], 'device_owner': const.DEVICE_OWNER_DISTRIBUTED, - 'device_id': 'ovnmeta-%s' % network['id']}} - # TODO(boden): rehome create_port into neutron-lib - p_utils.create_port(self._plugin, context, port) + 'device_id': 'ovnmeta-%s' % network['id'], + 'fixed_ips': fixed_ips, + } + } + # TODO(boden): rehome create_port into neutron-lib + p_utils.create_port(self._plugin, context, port) def update_metadata_port(self, context, network_id, subnet=None): """Update metadata port. diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py new file mode 100644 index 00000000000..6a9b5a0938a --- /dev/null +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py @@ -0,0 +1,68 @@ +# Copyright 2023 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_lib import constants + +from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as ovn_config +from neutron.tests.functional import base + + +class TestOVNClient(base.TestOVNFunctionalBase): + + def test_create_metadata_port(self): + def check_metadata_port(enable_dhcp): + ports = self.plugin.get_ports( + self.context, filters={'network_id': [network['id']]}) + self.assertEqual(1, len(ports)) + if enable_dhcp: + self.assertEqual(1, len(ports[0]['fixed_ips'])) + else: + self.assertEqual(0, len(ports[0]['fixed_ips'])) + return ports + + ovn_config.cfg.CONF.set_override('ovn_metadata_enabled', True, + group='ovn') + ovn_client = self.mech_driver._ovn_client + for enable_dhcp in (True, False): + network_args = {'tenant_id': 'project_1', + 'name': 'test_net_1', + 'admin_state_up': True, + 'shared': False, + 'status': constants.NET_STATUS_ACTIVE} + network = self.plugin.create_network(self.context, + {'network': network_args}) + subnet_args = {'tenant_id': 'project_1', + 'name': 'test_snet_1', + 'network_id': network['id'], + 'ip_version': constants.IP_VERSION_4, + 'cidr': '10.210.10.0/28', + 'enable_dhcp': enable_dhcp, + 'gateway_ip': constants.ATTR_NOT_SPECIFIED, + 'allocation_pools': constants.ATTR_NOT_SPECIFIED, + 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, + 'host_routes': constants.ATTR_NOT_SPECIFIED} + self.plugin.create_subnet(self.context, {'subnet': subnet_args}) + + # The metadata port has been created during the network creation. + ports = check_metadata_port(enable_dhcp) + + # Force the deletion and creation the metadata port. + self.plugin.delete_port(self.context, ports[0]['id']) + ovn_client.create_metadata_port(self.context, network) + check_metadata_port(enable_dhcp) + + # Call again the "create_metadata_port" method as is idempotent + # because it checks first if the metadata port exists. + ovn_client.create_metadata_port(self.context, network) + check_metadata_port(enable_dhcp) diff --git a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py index 0976b2f646d..4824ca3c9f8 100644 --- a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py +++ b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py @@ -30,6 +30,7 @@ from neutron.agent.linux.openvswitch_firewall import exceptions from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw from neutron.conf.agent import securitygroups_rpc +from neutron.conf.plugins.ml2.drivers import ovs_conf from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ @@ -514,6 +515,7 @@ def __init__(self, name, port, mac): class TestOVSFirewallDriver(base.BaseTestCase): def setUp(self): super(TestOVSFirewallDriver, self).setUp() + ovs_conf.register_ovs_agent_opts(cfg=cfg.CONF) mock_bridge = mock.patch.object( ovs_lib, 'OVSBridge', autospec=True).start() securitygroups_rpc.register_securitygroups_opts() @@ -840,6 +842,26 @@ def test_initialize_port_flows_vlan_dvr_conntrack_direct_vlan(self): return_value={"vlan1": "br-vlan1"}): self.firewall.initialize_port_flows(port) + def test_initialize_port_flows_permitted_ethertypes(self): + self.firewall.permitted_ethertypes = ['0x1234', '0x5678'] + port_dict = {'device': 'port-id', + 'security_groups': [1]} + of_port = create_ofport(port_dict, + network_type=constants.TYPE_VLAN, + physical_network='vlan1') + self.firewall.sg_port_map.ports[of_port.id] = of_port + port = self.firewall.get_or_create_ofport(port_dict) + with mock.patch.object(self.firewall, '_add_flow') as mock_add_flow: + self.firewall.initialize_port_flows(port) + + calls = [mock.call(table=ovs_consts.BASE_INGRESS_TABLE, + priority=100, dl_type='0x1234', + reg_port=1, actions='output:1'), + mock.call(table=ovs_consts.BASE_INGRESS_TABLE, + priority=100, dl_type='0x5678', + reg_port=1, actions='output:1')] + mock_add_flow.assert_has_calls(calls, any_order=True) + def test_delete_all_port_flows(self): port_dict = { 'device': 'port-id', diff --git a/releasenotes/notes/bug-2003455-dff0d0f00b5a18e2.yaml b/releasenotes/notes/bug-2003455-dff0d0f00b5a18e2.yaml deleted file mode 100644 index c17fe4338da..00000000000 --- a/releasenotes/notes/bug-2003455-dff0d0f00b5a18e2.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -fixes: - - | - [`bug 2003455 `_] - Previous commit (https://review.opendev.org/c/openstack/neutron/+/871252) - added a workaround to avoid vlan provider networks traffic to be tunneled - to the compute nodes but it was still centralized. Now the traffic is - distributed thanks to using the "redirect-type" flag on the ovn gateway - port.