Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/source/contributor/internals/openvswitch_firewall.rst
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,19 @@ will be:
table=94, priority=10,reg6=0x284,dl_src=fa:16:3e:24:57:c7,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=push_vlan:0x8100,set_field:0x1->vlan_vid,output:3
table=94, priority=1 actions=NORMAL

The OVS firewall will initialize a default goto table 94 flow
on TRANSIENT_TABLE |table_60|, if ``explicitly_egress_direct``
is set to True, which is mainly for ports without security groups
and disabled port_security. For instance:

::
table=60, priority=2 actions=resubmit(,94)

Then for packets from the outside to VM without security functionalities
(--disable-port-security --no-security-group)
will go to table 94 and do the same direct actions.


OVS firewall integration points
-------------------------------

Expand Down
24 changes: 24 additions & 0 deletions neutron/agent/linux/openvswitch_firewall/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,14 @@ def _initialize_common_flows(self):
'resubmit(,%d)' % ovs_consts.BASE_EGRESS_TABLE,
)

if cfg.CONF.AGENT.explicitly_egress_direct:
self._add_flow(
table=ovs_consts.TRANSIENT_TABLE,
priority=2,
actions='resubmit(,%d)' % (
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
)

def _initialize_third_party_tables(self):
self.int_br.br.add_flow(
table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE,
Expand Down Expand Up @@ -1253,6 +1261,7 @@ def install_accepted_egress_direct_flow(self, mac, vlan_tag, dst_port,
return

# Prevent flood for accepted egress traffic
# For packets from internal ports or VM ports.
self._add_flow(
flow_group_id=dst_port,
table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE,
Expand All @@ -1261,6 +1270,15 @@ def install_accepted_egress_direct_flow(self, mac, vlan_tag, dst_port,
reg_net=vlan_tag,
actions='output:{:d}'.format(dst_port)
)
# For packets from patch ports.
self._add_flow(
flow_group_id=dst_port,
table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE,
priority=12,
dl_dst=mac,
dl_vlan=vlan_tag,
actions='strip_vlan,output:{:d}'.format(dst_port)
)

# The former flow may not match, that means the destination port is
# not in this host. So, we direct the packet to mapped bridge(s).
Expand Down Expand Up @@ -1309,6 +1327,12 @@ def delete_accepted_egress_direct_flow(self, mac, vlan_tag):
dl_src=mac,
reg_net=vlan_tag)

self._delete_flows(
table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE,
dl_dst=mac,
dl_vlan=vlan_tag
)

def _initialize_tracked_egress(self, port):
# Drop invalid packets
self._add_flow(
Expand Down
4 changes: 4 additions & 0 deletions neutron/common/ovn/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1237,3 +1237,7 @@ def validate_port_forwarding_configuration():
if any(net_type in provider_network_types
for net_type in cfg.CONF.ml2.tenant_network_types):
raise ovn_exc.InvalidPortForwardingConfiguration()


def is_nat_gateway_port_supported(idl):
return idl.is_col_present('NAT', 'gateway_port')
6 changes: 5 additions & 1 deletion neutron/conf/plugins/ml2/drivers/ovs_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,16 @@
"outgoing IP packet carrying GRE/VXLAN tunnel.")),
cfg.BoolOpt('baremetal_smartnic', default=False,
help=_("Enable the agent to process Smart NIC ports.")),
# TODO(liuyulong): consider adding a new configuration
# item to control ingress behavior.
cfg.BoolOpt('explicitly_egress_direct', default=False,
help=_("When set to True, the accepted egress unicast "
"traffic will not use action NORMAL. The accepted "
"egress packets will be taken care of in the final "
"egress tables direct output flows for unicast "
"traffic.")),
"traffic. This will aslo change the pipleline for "
"ingress traffic to ports without security, the final "
"output action will be hit in table 94. ")),
]

dhcp_opts = [
Expand Down
2 changes: 1 addition & 1 deletion neutron/db/models/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ class Tag(model_base.BASEV2):
tag = sa.Column(sa.String(255), nullable=False, primary_key=True)
standard_attr = orm.relationship(
'StandardAttribute', load_on_pending=True,
backref=orm.backref('tags', lazy='joined', viewonly=True),
backref=orm.backref('tags', lazy='subquery', viewonly=True),
sync_backref=False)
revises_on_change = ('standard_attr', )
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def setup_default_table(self, enable_openflow_dhcp=False,
self.install_goto(dest_table_id=constants.PACKET_RATE_LIMIT)
self.install_goto(dest_table_id=constants.TRANSIENT_TABLE,
table_id=constants.PACKET_RATE_LIMIT)
self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=3)
self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=1)
self.init_dhcp(enable_openflow_dhcp=enable_openflow_dhcp,
enable_dhcpv6=enable_dhcpv6)
self.install_drop(table_id=constants.ARP_SPOOF_TABLE)
Expand Down
20 changes: 18 additions & 2 deletions neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1053,8 +1053,24 @@ def update_virtual_port_host(self, port_id, chassis_id):
'Chassis', chassis_id, 'hostname').execute(check_error=True)
else:
hostname = ''
self._plugin.update_virtual_port_host(n_context.get_admin_context(),
port_id, hostname)

# Updates neutron database with hostname for virtual port
context = n_context.get_admin_context()
self._plugin.update_virtual_port_host(context, port_id, hostname)
db_port = self._plugin.get_port(context, port_id)
check_rev_cmd = self.nb_ovn.check_revision_number(
port_id, db_port, ovn_const.TYPE_PORTS)
# Updates OVN NB database with hostname for lsp virtual port
with self.nb_ovn.transaction(check_error=True) as txn:
ext_ids = ('external_ids',
{ovn_const.OVN_HOST_ID_EXT_ID_KEY: hostname})
txn.add(
self.nb_ovn.db_set(
'Logical_Switch_Port', port_id, ext_ids))
txn.add(check_rev_cmd)
if check_rev_cmd.result == ovn_const.TXN_COMMITTED:
ovn_revision_numbers_db.bump_revision(context, db_port,
ovn_const.TYPE_PORTS)

def get_workers(self):
"""Get any worker instances that should have their own process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ def get_all_logical_routers_with_rports(self):
columns['external_mac'] = nat.external_mac[0]
if nat.logical_port:
columns['logical_port'] = nat.logical_port[0]
columns['external_ids'] = nat.external_ids
columns['uuid'] = nat.uuid
if utils.is_nat_gateway_port_supported(self):
columns['gateway_port'] = nat.gateway_port
dnat_and_snats.append(columns)
elif nat.type == 'snat':
snat.append(columns)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,52 @@ def cleanup_old_hash_ring_nodes(self):
context = n_context.get_admin_context()
hash_ring_db.cleanup_old_nodes(context, days=5)

@periodics.periodic(spacing=600, run_immediately=True)
def update_nat_floating_ip_with_gateway_port_reference(self):
"""Set NAT rule gateway_port column to any floating IP without
router gateway port uuid reference - LP#2035281.
"""

if not utils.is_nat_gateway_port_supported(self._nb_idl):
raise periodics.NeverAgain()

context = n_context.get_admin_context()
fip_update = []
lrouters = self._nb_idl.get_all_logical_routers_with_rports()
for router in lrouters:
ovn_fips = router['dnat_and_snats']
for ovn_fip in ovn_fips:
# Skip FIPs that are already configured with gateway_port
if ovn_fip['gateway_port']:
continue
fip_id = ovn_fip['external_ids'].get(
ovn_const.OVN_FIP_EXT_ID_KEY)
if fip_id:
fip_update.append({'uuid': ovn_fip['uuid'],
'router_id': router['name']})

# Simple caching mechanism to avoid unnecessary DB calls
gw_port_id_cache = {}
lrp_cache = {}
cmds = []
for fip in fip_update:
lrouter = utils.ovn_name(fip['router_id'])
if lrouter not in gw_port_id_cache.keys():
router_db = self._ovn_client._l3_plugin.get_router(
context, fip['router_id'], fields=['gw_port_id'])
gw_port_id_cache[lrouter] = router_db.get('gw_port_id')
lrp_cache[lrouter] = self._nb_idl.get_lrouter_port(
gw_port_id_cache[lrouter])
columns = {'gateway_port': lrp_cache[lrouter].uuid}
cmds.append(self._nb_idl.set_nat_rule_in_lrouter(
lrouter, fip['uuid'], **columns))

if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()


class HashRingHealthCheckPeriodics(object):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,14 @@ def _create_or_update_floatingip(self, floatingip, txn=None):
'logical_port': floatingip['port_id'],
'external_ids': ext_ids}

# If OVN supports gateway_port column for NAT rules set gateway port
# uuid to any floating IP without gw port reference - LP#2035281.
if utils.is_nat_gateway_port_supported(self._nb_idl):
router_db = self._l3_plugin.get_router(admin_context, router_id)
gw_port_id = router_db.get('gw_port_id')
lrp = self._nb_idl.get_lrouter_port(gw_port_id)
columns['gateway_port'] = lrp.uuid

if ovn_conf.is_ovn_distributed_floating_ip():
if self._nb_idl.lsp_get_up(floatingip['port_id']).execute():
columns['external_mac'] = port_db['mac_address']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,17 @@ def match_fn(self, event, row, old):

virtual_parents = (row.options or {}).get(
ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY)

if getattr(old, 'chassis', None) is not None and virtual_parents:
# The port moved from chassis due to VIP failover or migration,
# which means we need to update the host_id information
return True

if getattr(old, 'options', None) is None:
# The "old.options" dictionary is not being modified,
# thus the virtual parents didn't change.
return False

old_virtual_parents = getattr(old, 'options', {}).get(
ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY)
if virtual_parents != old_virtual_parents:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,65 @@ def test_check_for_ha_chassis_group(self):
lsp = self.nb_api.lookup('Logical_Switch_Port', p1['id'])
self.assertEqual(hcg_uuid, lsp.ha_chassis_group[0].uuid)

def test_floating_ip_with_gateway_port(self):
ext_net = self._create_network('ext_networktest', external=True)
ext_subnet = self._create_subnet(
'ext_subnettest',
ext_net['id'],
**{'cidr': '100.0.0.0/24',
'gateway_ip': '100.0.0.254',
'allocation_pools': [
{'start': '100.0.0.2', 'end': '100.0.0.253'}],
'enable_dhcp': False})
net1 = self._create_network('network1test', external=False)
subnet1 = self._create_subnet('subnet1test', net1['id'])
external_gateway_info = {
'enable_snat': True,
'network_id': ext_net['id'],
'external_fixed_ips': [
{'ip_address': '100.0.0.2', 'subnet_id': ext_subnet['id']}]}
router = self._create_router(
'routertest', external_gateway_info=external_gateway_info)
self._add_router_interface(router['id'], subnet1['id'])

p1 = self._create_port('testp1', net1['id'])
logical_ip = p1['fixed_ips'][0]['ip_address']
fip_info = {'floatingip': {
'tenant_id': self._tenant_id,
'description': 'test_fip',
'floating_network_id': ext_net['id'],
'port_id': p1['id'],
'fixed_ip_address': logical_ip}}

# Create floating IP without gateway_port
with mock.patch.object(utils,
'is_nat_gateway_port_supported', return_value=False):
fip = self.l3_plugin.create_floatingip(self.context, fip_info)

self.assertEqual(router['id'], fip['router_id'])
self.assertEqual('testp1', fip['port_details']['name'])
self.assertIsNotNone(self.nb_api.get_lswitch_port(fip['port_id']))

rules = self.nb_api.get_all_logical_routers_with_rports()[0]
fip_rule = rules['dnat_and_snats'][0]
if utils.is_nat_gateway_port_supported(self.nb_api):
self.assertEqual([], fip_rule['gateway_port'])
else:
self.assertNotIn('gateway_port', fip_rule)

# Call the maintenance task and check that the value has been
# updated in the NAT rule
self.assertRaises(periodics.NeverAgain,
self.maint.update_nat_floating_ip_with_gateway_port_reference)

rules = self.nb_api.get_all_logical_routers_with_rports()[0]
fip_rule = rules['dnat_and_snats'][0]

if utils.is_nat_gateway_port_supported(self.nb_api):
self.assertNotEqual([], fip_rule['gateway_port'])
else:
self.assertNotIn('gateway_port', fip_rule)


class TestLogMaintenance(_TestMaintenanceHelper,
test_log_driver.LogApiTestCaseBase):
Expand Down
Loading