Browse Source

Add 'unknown' to the address list if the port's port security is disabled

When a port's port security is disabled then the port should be allowed to use
any mac address. And for this to work, OVN expects 'unknown' address
to be added into the Logical_Switch_Port.addresses column. This patch
adds this 'unknown' address if the port security is disabled.

Change-Id: I5f25f4d4b2acccada3ab2944e9cfd2461149ef3e
Closes-bug: #1815270
Co-Authored-By: Lucas Alvares Gomes <lucasagomes@gmail.com>
tags/6.0.0.0b1
Numan Siddique 3 months ago
parent
commit
ef5e9d31ec

+ 23
- 0
networking_ovn/common/maintenance.py View File

@@ -384,3 +384,26 @@ class DBInconsistenciesPeriodics(object):
384 384
             self._ovn_client.create_metadata_port(admin_context, n)
385 385
 
386 386
         raise periodics.NeverAgain()
387
+
388
+    # TODO(lucasagomes): Remove this in the T cycle
389
+    # A static spacing value is used here, but this method will only run
390
+    # once per lock due to the use of periodics.NeverAgain().
391
+    @periodics.periodic(spacing=600, run_immediately=True)
392
+    def check_for_port_security_unknown_address(self):
393
+
394
+        if not self.has_lock:
395
+            return
396
+
397
+        for port in self._nb_idl.lsp_list().execute(check_error=True):
398
+            addresses = port.addresses
399
+            if not port.port_security and 'unknown' not in addresses:
400
+                addresses.append('unknown')
401
+            elif port.port_security and 'unknown' in addresses:
402
+                addresses.remove('unknown')
403
+            else:
404
+                continue
405
+
406
+            self._nb_idl.lsp_set_addresses(
407
+                port.name, addresses=addresses).execute(check_error=True)
408
+
409
+        raise periodics.NeverAgain()

+ 8
- 0
networking_ovn/common/ovn_client.py View File

@@ -227,6 +227,14 @@ class OVNClient(object):
227 227
             addresses = [address]
228 228
             addresses.extend(new_macs)
229 229
 
230
+            if not port_security:
231
+                # Port security is disabled for this port.
232
+                # So this port can send traffic with any mac address.
233
+                # OVN allows any mac address from a port if "unknown"
234
+                # is added to the Logical_Switch_Port.addresses column.
235
+                # So add it.
236
+                addresses.append("unknown")
237
+
230 238
             # Only adjust the OVN type if the port is not owned by Neutron
231 239
             # DHCP agents.
232 240
             if (port['device_owner'] == const.DEVICE_OWNER_DHCP and

+ 58
- 0
networking_ovn/tests/functional/test_maintenance.py View File

@@ -735,3 +735,61 @@ class TestMaintenance(_TestMaintenanceHelper):
735 735
             # Assert create_metadata_port() wasn't called since metadata
736 736
             # is not enabled
737 737
             self.assertFalse(mock_create_port.called)
738
+
739
+    def test_check_for_port_security_unknown_address(self):
740
+        neutron_net = self._create_network('network1')
741
+        neutron_port = self._create_port('port1', neutron_net['id'])
742
+
743
+        # Let's force disabling port security for the LSP
744
+        self.nb_api.lsp_set_port_security(neutron_port['id'], []).execute(
745
+            check_error=True)
746
+
747
+        ovn_port = self.nb_api.db_find(
748
+            'Logical_Switch_Port', ('name', '=', neutron_port['id'])).execute(
749
+            check_error=True)[0]
750
+
751
+        # Assert that port security is now disabled but the 'unknown'
752
+        # is not set in the addresses column
753
+        self.assertFalse(ovn_port['port_security'])
754
+        self.assertNotIn('unknown', ovn_port['addresses'])
755
+
756
+        # Call the maintenance task to fix the problem. Note that
757
+        # NeverAgain is raised so it only runs once at start up
758
+        self.assertRaises(periodics.NeverAgain,
759
+                          self.maint.check_for_port_security_unknown_address)
760
+
761
+        ovn_port = self.nb_api.db_find(
762
+            'Logical_Switch_Port', ('name', '=', neutron_port['id'])).execute(
763
+            check_error=True)[0]
764
+
765
+        # Assert that 'unknown' was set in the addresses column for
766
+        # the port
767
+        self.assertFalse(ovn_port['port_security'])
768
+        self.assertIn('unknown', ovn_port['addresses'])
769
+
770
+        # Now the other way around, let's set port_security in the OVN
771
+        # table while the 'unknown' address is set in the addresses column
772
+        self.nb_api.lsp_set_port_security(
773
+            neutron_port['id'], ovn_port['addresses']).execute(
774
+            check_error=True)
775
+
776
+        ovn_port = self.nb_api.db_find(
777
+            'Logical_Switch_Port', ('name', '=', neutron_port['id'])).execute(
778
+            check_error=True)[0]
779
+
780
+        self.assertTrue(ovn_port['port_security'])
781
+        self.assertIn('unknown', ovn_port['addresses'])
782
+
783
+        # Call the maintenance task to fix the problem. Note that
784
+        # NeverAgain is raised so it only runs once at start up
785
+        self.assertRaises(periodics.NeverAgain,
786
+                          self.maint.check_for_port_security_unknown_address)
787
+
788
+        ovn_port = self.nb_api.db_find(
789
+            'Logical_Switch_Port', ('name', '=', neutron_port['id'])).execute(
790
+            check_error=True)[0]
791
+
792
+        # Assert that 'unknown' was removed from the addresses column
793
+        # for the port
794
+        self.assertTrue(ovn_port['port_security'])
795
+        self.assertNotIn('unknown', ovn_port['addresses'])

+ 18
- 3
networking_ovn/tests/functional/test_ovn_db_sync.py View File

@@ -20,6 +20,7 @@ from neutron.tests.unit.extensions import test_extraroute
20 20
 from neutron.tests.unit.extensions import test_securitygroup
21 21
 from neutron_lib.api.definitions import dns as dns_apidef
22 22
 from neutron_lib.api.definitions import l3
23
+from neutron_lib.api.definitions import port_security as ps
23 24
 from neutron_lib import constants
24 25
 from neutron_lib import context
25 26
 from neutron_lib.plugins import directory
@@ -78,6 +79,7 @@ class TestOvnNbSync(base.TestOVNFunctionalBase):
78 79
         self.lport_dhcp_ignored = []
79 80
         self.match_old_mac_dhcp_subnets = []
80 81
         self.expected_dns_records = []
82
+        self.expected_ports_with_unknown_addr = []
81 83
         ovn_config.cfg.CONF.set_override('ovn_metadata_enabled', True,
82 84
                                          group='ovn')
83 85
 
@@ -139,9 +141,11 @@ class TestOvnNbSync(base.TestOVNFunctionalBase):
139 141
         n1_port_dict = {}
140 142
         for p in ['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7']:
141 143
             if p in ['p1', 'p5']:
142
-                port_kwargs = {'arg_list': (dns_apidef.DNSNAME,),
143
-                               dns_apidef.DNSNAME: 'n1-' + p,
144
-                               'device_id': 'n1-' + p}
144
+                port_kwargs = {
145
+                    'arg_list': (dns_apidef.DNSNAME, ps.PORTSECURITY),
146
+                    dns_apidef.DNSNAME: 'n1-' + p,
147
+                    ps.PORTSECURITY: 'False',
148
+                    'device_id': 'n1-' + p}
145 149
             else:
146 150
                 port_kwargs = {}
147 151
 
@@ -152,6 +156,7 @@ class TestOvnNbSync(base.TestOVNFunctionalBase):
152 156
             port = self.deserialize(self.fmt, res)
153 157
             n1_port_dict[p] = port['port']['id']
154 158
             lport_name = port['port']['id']
159
+
155 160
             lswitch_name = 'neutron-' + n1['network']['id']
156 161
             if p in ['p1', 'p5']:
157 162
                 port_ips = " ".join([f['ip_address']
@@ -160,6 +165,8 @@ class TestOvnNbSync(base.TestOVNFunctionalBase):
160 165
                 self.expected_dns_records[0]['records'][hname] = port_ips
161 166
                 hname = 'n1-' + p + '.ovn.test'
162 167
                 self.expected_dns_records[0]['records'][hname] = port_ips
168
+                self.expected_ports_with_unknown_addr.append(lport_name)
169
+
163 170
             if p == 'p1':
164 171
                 fake_subnet = {'cidr': '11.11.11.11/24'}
165 172
                 dhcp_acls = acl_utils.add_acl_dhcp(port['port'], fake_subnet)
@@ -974,6 +981,14 @@ class TestOvnNbSync(base.TestOVNFunctionalBase):
974 981
                                   plugin_lport_ids_dhcpv6_enabled)
975 982
             self.assertItemsEqual(expected_dhcpv6_options_ports_ids,
976 983
                                   monitor_lport_ids_dhcpv6_enabled)
984
+
985
+            # Check if unknow address is set for the expected lports.
986
+            for row in (
987
+                self.nb_api.tables['Logical_Switch_Port'].
988
+                rows.values()):
989
+                if row.name in self.expected_ports_with_unknown_addr:
990
+                    self.assertIn('unknown', row.addresses)
991
+
977 992
         else:
978 993
             self.assertRaises(
979 994
                 AssertionError, self.assertItemsEqual, db_port_ids,

+ 20
- 0
networking_ovn/tests/unit/ml2/test_mech_driver.py View File

@@ -405,6 +405,8 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
405 405
                     self.assertEqual([],
406 406
                                      called_args_dict.get('port_security'))
407 407
 
408
+                    self.assertEqual('unknown',
409
+                                     called_args_dict.get('addresses')[1])
408 410
                     data = {'port': {'mac_address': '00:00:00:00:00:01'}}
409 411
                     req = self.new_update_request(
410 412
                         'ports',
@@ -416,6 +418,24 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
416 418
                          ).call_args_list[0][1])
417 419
                     self.assertEqual([],
418 420
                                      called_args_dict.get('port_security'))
421
+                    self.assertEqual(2, len(called_args_dict.get('addresses')))
422
+                    self.assertEqual('unknown',
423
+                                     called_args_dict.get('addresses')[1])
424
+
425
+                    # Enable port security
426
+                    data = {'port': {'port_security_enabled': 'True'}}
427
+                    req = self.new_update_request(
428
+                        'ports',
429
+                        data, port['port']['id'])
430
+                    req.get_response(self.api)
431
+                    called_args_dict = (
432
+                        (self.nb_ovn.set_lswitch_port
433
+                         ).call_args_list[1][1])
434
+                    self.assertEqual(2,
435
+                                     self.nb_ovn.set_lswitch_port.call_count)
436
+                    self.assertEqual(1, len(called_args_dict.get('addresses')))
437
+                    self.assertNotIn('unknown',
438
+                                     called_args_dict.get('addresses'))
419 439
 
420 440
     def test_create_port_security_allowed_address_pairs(self):
421 441
         kwargs = {'allowed_address_pairs':

Loading…
Cancel
Save