From b20669fa6ec88445ccf4b3fa8cb38aa05b5b3efb Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 10 Jan 2020 13:57:08 +0000 Subject: [PATCH] IPv6 accepts first address only for routers Neutron considers the network address a valid IPv6 address if the owner is a router or is not defined yet [1][2]. If not, the IPv6 address is discharded. [1] https://tools.ietf.org/html/rfc4291#section-2.6.1 [2] https://review.opendev.org/#/c/647484/ Change-Id: I1ddee94bd34182960de8e5261fbc99edc10b6654 Closes-Bug: #1859163 --- neutron/ipam/utils.py | 3 ++- neutron/tests/unit/ipam/test_utils.py | 37 ++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/neutron/ipam/utils.py b/neutron/ipam/utils.py index 3c20c2f2102..1a4b303f959 100644 --- a/neutron/ipam/utils.py +++ b/neutron/ipam/utils.py @@ -27,7 +27,8 @@ def check_subnet_ip(cidr, ip_address, port_owner=''): # NOTE(njohnston): In some cases the code cannot know the owner of the # port. In these cases port_owner should an empty string, and we pass # it through here. - return (port_owner in (constants.ROUTER_PORT_OWNERS + ('', )) and + return ((port_owner in (constants.ROUTER_PORT_OWNERS + ('', )) or + ip != net.network) and ip in net) else: return ip != net.network and ip != net.broadcast and ip in net diff --git a/neutron/tests/unit/ipam/test_utils.py b/neutron/tests/unit/ipam/test_utils.py index 8e1fe57c956..b3b77e6ebea 100644 --- a/neutron/tests/unit/ipam/test_utils.py +++ b/neutron/tests/unit/ipam/test_utils.py @@ -14,6 +14,7 @@ # under the License. import netaddr +from neutron_lib import constants from neutron.ipam import utils from neutron.tests import base @@ -31,13 +32,37 @@ class TestIpamUtils(base.BaseTestCase): self.assertTrue(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.1')) self.assertTrue(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.254')) - def test_check_subnet_ip_v6_network(self): - self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::0')) + def test_check_subnet_ip_v6_owner_router_or_not_defined(self): + for port_owner in (constants.ROUTER_PORT_OWNERS + ('', )): + # IP address == network + self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::0', + port_owner=port_owner)) + # IP address == broadcast + self.assertTrue(utils.check_subnet_ip( + 'F111::0/64', 'F111::FFFF:FFFF:FFFF:FFFF', + port_owner=port_owner)) + # IP address in network + self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::50', + port_owner=port_owner)) + # IP address not in network + self.assertFalse(utils.check_subnet_ip('F111::0/64', 'F112::50', + port_owner=port_owner)) - def test_check_subnet_ip_v6_valid(self): - self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::1')) - self.assertTrue(utils.check_subnet_ip('F111::0/64', - 'F111::FFFF:FFFF:FFFF:FFFF')) + def test_check_subnet_ip_v6_owner_not_router(self): + port_owner = 'nova:compute' + # IP address == network + self.assertFalse(utils.check_subnet_ip('F111::0/64', 'F111::0', + port_owner=port_owner)) + # IP address == broadcast + self.assertTrue(utils.check_subnet_ip( + 'F111::0/64', 'F111::FFFF:FFFF:FFFF:FFFF', + port_owner=port_owner)) + # IP address in network + self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::50', + port_owner=port_owner)) + # IP address not in network + self.assertFalse(utils.check_subnet_ip('F111::0/64', 'F112::50', + port_owner=port_owner)) def test_generate_pools_v4_nogateway(self): cidr = '192.168.0.0/24'