diff --git a/neutron/agent/l2/extensions/dhcp/extension.py b/neutron/agent/l2/extensions/dhcp/extension.py index 14ba3343b38..ac14388ec10 100644 --- a/neutron/agent/l2/extensions/dhcp/extension.py +++ b/neutron/agent/l2/extensions/dhcp/extension.py @@ -26,6 +26,10 @@ from neutron.agent.l2.extensions.dhcp import ipv6 from neutron.api.rpc.callbacks import resources LOG = logging.getLogger(__name__) +LINK_LOCAL_GATEWAY = { + constants.IP_VERSION_4: constants.METADATA_V4_IP, + constants.IP_VERSION_6: constants.METADATA_V6_IP +} class DHCPExtensionPortInfoAPI: @@ -64,7 +68,9 @@ class DHCPExtensionPortInfoAPI: 'cidr': subnet.cidr, 'host_routes': subnet.host_routes, 'dns_nameservers': subnet.dns_nameservers, - 'gateway_ip': subnet.gateway_ip} + 'gateway_ip': subnet.gateway_ip or LINK_LOCAL_GATEWAY[ + subnet.ip_version + ]} fixed_ips.append(info) net = self.cache_api.get_resource_by_id( resources.NETWORK, port_obj.network_id) diff --git a/neutron/agent/l2/extensions/dhcp/ipv4.py b/neutron/agent/l2/extensions/dhcp/ipv4.py index aeb7a20a81f..3e6719421ba 100644 --- a/neutron/agent/l2/extensions/dhcp/ipv4.py +++ b/neutron/agent/l2/extensions/dhcp/ipv4.py @@ -49,13 +49,13 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase): def get_bin_routes(self, gateway=None, routes=None): bin_routes = b'' - # Default routes - default_route = self.get_bin_route(constants.IPv4_ANY, gateway) - bin_routes += default_route + if gateway and gateway != constants.METADATA_V4_IP: + # Default routes + default_route = self.get_bin_route(constants.IPv4_ANY, gateway) + bin_routes += default_route - # For some VMs they may need the metadata IP's route, we move - # the destination to gateway IP. - if gateway: + # For some VMs they may need the metadata IP's route, we move + # the destination to gateway IP. meta_route = self.get_bin_route( constants.METADATA_V4_CIDR, gateway) bin_routes += meta_route @@ -71,7 +71,7 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase): net = netaddr.IPNetwork(fixed_ips[0]['cidr']) dns_nameservers = fixed_ips[0]['dns_nameservers'] host_routes = fixed_ips[0]['host_routes'] - gateway_ip = fixed_ips[0]['gateway_ip'] + gateway_ip = str(fixed_ips[0]['gateway_ip']) bin_server = addrconv.ipv4.text_to_bin(gateway_ip) option_list = [] @@ -122,9 +122,10 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase): value=struct.pack( '!%ds' % len(cfg.CONF.dns_domain), str.encode(cfg.CONF.dns_domain)))) - option_list.append( - dhcp.option(tag=dhcp.DHCP_GATEWAY_ADDR_OPT, - value=bin_server)) + if gateway_ip != constants.METADATA_V4_IP: + option_list.append( + dhcp.option(tag=dhcp.DHCP_GATEWAY_ADDR_OPT, + value=bin_server)) # Static routes option_list.append( dhcp.option(tag=dhcp.DHCP_CLASSLESS_ROUTE_OPT, diff --git a/neutron/agent/l2/extensions/dhcp/ipv6.py b/neutron/agent/l2/extensions/dhcp/ipv6.py index cd756df1322..118218f6bb8 100644 --- a/neutron/agent/l2/extensions/dhcp/ipv6.py +++ b/neutron/agent/l2/extensions/dhcp/ipv6.py @@ -147,7 +147,7 @@ class DHCPIPv6Responder(dhcp_base.DHCPResponderBase): def get_dhcp_options(self, mac, ip_info, req_options, req_type): ip_addr = ip_info['ip_address'] - gateway_ip = ip_info['gateway_ip'] + gateway_ip = str(ip_info['gateway_ip']) dns_nameservers = ip_info['dns_nameservers'] option_list = [] @@ -210,9 +210,9 @@ class DHCPIPv6Responder(dhcp_base.DHCPResponderBase): dhcp6.option( code=DHCPV6_OPTION_DNS_RECURSIVE_NS, data=domain_serach, length=len(domain_serach))) - else: + elif gateway_ip != constants.METADATA_V6_IP: # use gateway as the default DNS server address - domain_serach = addrconv.ipv6.text_to_bin(str(gateway_ip)) + domain_serach = addrconv.ipv6.text_to_bin(gateway_ip) option_list.append( dhcp6.option( code=DHCPV6_OPTION_DNS_RECURSIVE_NS, diff --git a/neutron/tests/unit/agent/l2/extensions/dhcp/test_base.py b/neutron/tests/unit/agent/l2/extensions/dhcp/test_base.py index ac50c6d3d81..7eedf456693 100644 --- a/neutron/tests/unit/agent/l2/extensions/dhcp/test_base.py +++ b/neutron/tests/unit/agent/l2/extensions/dhcp/test_base.py @@ -61,41 +61,81 @@ class FakeMsg: self.data = packet.data +IPV4_INFO = { + 'version': 4, + 'host_routes': [ + subnet_obj.Route( + destination=netaddr.IPNetwork('1.1.1.0/24'), + nexthop='192.168.1.100', + subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f' + ), + subnet_obj.Route( + destination=netaddr.IPNetwork('2.2.2.2/32'), + nexthop='192.168.1.101', + subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f' + ) + ], + 'subnet_id': 'daed3c3d-d95a-48a8-a8b1-17d408cd760f', + 'dns_nameservers': [ + subnet_obj.DNSNameServer( + address='8.8.8.8', + order=0, + subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f' + ), + subnet_obj.DNSNameServer( + address='8.8.4.4', + order=1, + subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f' + ) + ], + 'cidr': net_utils.AuthenticIPNetwork('192.168.111.0/24'), + 'ip_address': '192.168.111.45', + 'gateway_ip': netaddr.IPAddress('192.168.111.1') +} + + +IPV6_INFO = { + 'version': 6, + 'host_routes': [], + 'subnet_id': 'bd013460-b05f-4927-a4c6-5127584b2487', + 'dns_nameservers': [], + 'cidr': net_utils.AuthenticIPNetwork('fda7:a5cc:3460:1::/64'), + 'ip_address': 'fda7:a5cc:3460:1::bf', + 'gateway_ip': netaddr.IPAddress('fda7:a5cc:3460:1::1') +} + + PORT_INFO = { 'device_owner': 'compute:nova', 'admin_state_up': True, 'network_id': 'd666ccb3-69e9-46cb-b157-bb3741d87d5a', 'fixed_ips': [ - {'version': 4, - 'host_routes': [ - subnet_obj.Route( - destination=netaddr.IPNetwork('1.1.1.0/24'), - nexthop='192.168.1.100', - subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f'), - subnet_obj.Route( - destination=netaddr.IPNetwork('2.2.2.2/32'), - nexthop='192.168.1.101', - subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f')], - 'subnet_id': 'daed3c3d-d95a-48a8-a8b1-17d408cd760f', - 'dns_nameservers': [ - subnet_obj.DNSNameServer( - address='8.8.8.8', - order=0, - subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f'), - subnet_obj.DNSNameServer( - address='8.8.4.4', - order=1, - subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f')], - 'cidr': net_utils.AuthenticIPNetwork('192.168.111.0/24'), - 'ip_address': '192.168.111.45', - 'gateway_ip': netaddr.IPAddress('192.168.111.1')}, - {'version': 6, - 'host_routes': [], - 'subnet_id': 'bd013460-b05f-4927-a4c6-5127584b2487', - 'dns_nameservers': [], - 'cidr': net_utils.AuthenticIPNetwork('fda7:a5cc:3460:1::/64'), - 'ip_address': 'fda7:a5cc:3460:1::bf', - 'gateway_ip': netaddr.IPAddress('fda7:a5cc:3460:1::1')} + IPV4_INFO, + IPV6_INFO + ], + 'mac_address': '00:01:02:03:04:05', + 'port_id': '9a0e1889-f05f-43c7-a319-e1a723ed1587', + 'mtu': 1450 +} + + +IPV4_INFO_NO_GATEWAY = { + **IPV4_INFO, + 'gateway_ip': netaddr.IPAddress(constants.METADATA_V4_IP) +} +IPV6_INFO_NO_GATEWAY = { + **IPV6_INFO, + 'gateway_ip': netaddr.IPAddress(constants.METADATA_V6_IP) +} + + +NO_GATEWAY_PORT_INFO = { + 'device_owner': 'compute:nova', + 'admin_state_up': True, + 'network_id': 'd666ccb3-69e9-46cb-b157-bb3741d87d5a', + 'fixed_ips': [ + IPV4_INFO_NO_GATEWAY, + IPV6_INFO_NO_GATEWAY ], 'mac_address': '00:01:02:03:04:05', 'port_id': '9a0e1889-f05f-43c7-a319-e1a723ed1587', @@ -128,6 +168,7 @@ class DHCPResponderBaseTestCase(base.BaseTestCase): self.base_responer.handle_dhcp = mock.Mock() self.port_info = PORT_INFO + self.no_gateway_port_info = NO_GATEWAY_PORT_INFO def _create_test_dhcp_request_packet(self): option_list = [] diff --git a/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv4.py b/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv4.py index fa4b8bcfe37..13453dc9694 100644 --- a/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv4.py +++ b/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv4.py @@ -67,53 +67,70 @@ class DHCPIPv4ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase): dhcp_pkt = ret_pkt.get_protocols(dhcp.dhcp) self.assertIsNotNone(dhcp_pkt) - def test_get_dhcp_options(self): - expect_bin_routes = (b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01' - b'\x18\x01\x01\x01\xc0\xa8\x01d ' - b'\x02\x02\x02\x02\xc0\xa8\x01e') + def _test_get_dhcp_options(self, port_info, has_gateway_ip=False): + expect_bin_routes = ( + b'\x18\x01\x01\x01\xc0\xa8\x01d ' + b'\x02\x02\x02\x02\xc0\xa8\x01e' + ) + offer_option_list = [ + dhcp.option(length=0, tag=53, value=b'\x02'), + dhcp.option(length=0, tag=54, value=b'\xa9\xfe\xa9\xfe'), + dhcp.option(length=0, tag=51, value=b'\x00\x01Q\x80'), + dhcp.option(length=0, tag=1, value=b'\xff\xff\xff\x00'), + dhcp.option(length=0, tag=28, value=b'\xc0\xa8o\xff'), + dhcp.option(length=0, tag=6, + value=b'\x08\x08\x08\x08\x08\x08\x04\x04'), + dhcp.option(length=0, tag=15, value=b'openstacklocal'), + dhcp.option(length=0, tag=121, value=expect_bin_routes), + dhcp.option(length=0, tag=26, value=b'\x05\xaa') + ] + ack_option_list = list(offer_option_list) + ack_option_list[0] = dhcp.option(length=0, tag=53, value=b'\x05') + if has_gateway_ip: + expect_bin_routes = ( + b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01' + b'\x18\x01\x01\x01\xc0\xa8\x01d ' + b'\x02\x02\x02\x02\xc0\xa8\x01e' + ) + offer_option_list[1] = dhcp.option( + length=0, tag=54, value=b'\xc0\xa8o\x01' + ) + ack_option_list[1] = dhcp.option( + length=0, tag=54, value=b'\xc0\xa8o\x01' + ) + offer_option_list[7] = dhcp.option( + length=0, tag=121, value=expect_bin_routes + ) + ack_option_list[7] = dhcp.option( + length=0, tag=121, value=expect_bin_routes + ) + offer_option_list.append( + dhcp.option(length=0, tag=3, value=b'\xc0\xa8o\x01') + ) + ack_option_list.append( + dhcp.option(length=0, tag=3, value=b'\xc0\xa8o\x01') + ) + expect_offer_options = dhcp.options( magic_cookie='99.130.83.99', - option_list=[ - dhcp.option(length=0, tag=53, value=b'\x02'), - dhcp.option(length=0, tag=54, value=b'\xc0\xa8o\x01'), - dhcp.option(length=0, tag=51, value=b'\x00\x01Q\x80'), - dhcp.option(length=0, tag=1, value=b'\xff\xff\xff\x00'), - dhcp.option(length=0, tag=28, value=b'\xc0\xa8o\xff'), - dhcp.option(length=0, tag=6, - value=b'\x08\x08\x08\x08\x08\x08\x04\x04'), - dhcp.option(length=0, tag=15, value=b'openstacklocal'), - dhcp.option(length=0, tag=3, value=b'\xc0\xa8o\x01'), - dhcp.option( - length=0, tag=121, - value=expect_bin_routes), - dhcp.option(length=0, tag=26, value=b'\x05\xaa')], + option_list=offer_option_list, options_len=0) - offer_options = self.dhcp4_responer.get_dhcp_options(self.port_info) + offer_options = self.dhcp4_responer.get_dhcp_options(port_info) self._compare_option_values(expect_offer_options.option_list, offer_options.option_list) expect_ack_options = dhcp.options( magic_cookie='99.130.83.99', - option_list=[ - dhcp.option(length=0, tag=53, value=b'\x05'), - dhcp.option(length=0, tag=54, value=b'\xc0\xa8o\x01'), - dhcp.option(length=0, tag=51, value=b'\x00\x01Q\x80'), - dhcp.option(length=0, tag=1, value=b'\xff\xff\xff\x00'), - dhcp.option(length=0, tag=28, value=b'\xc0\xa8o\xff'), - dhcp.option(length=0, tag=6, - value=b'\x08\x08\x08\x08\x08\x08\x04\x04'), - dhcp.option(length=0, tag=15, value=b'openstacklocal'), - dhcp.option(length=0, tag=3, value=b'\xc0\xa8o\x01'), - dhcp.option( - length=0, tag=121, - value=expect_bin_routes), - dhcp.option(length=0, tag=26, value=b'\x05\xaa')], + option_list=ack_option_list, options_len=0) ack_options = self.dhcp4_responer.get_dhcp_options( - self.port_info, is_ack=True) + port_info, is_ack=True) self._compare_option_values(expect_ack_options.option_list, ack_options.option_list) + def test_get_dhcp_options(self): + self._test_get_dhcp_options(self.port_info, has_gateway_ip=True) + def test_get_bin_routes(self): expect_bin_routes = (b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01' b'\x18\x01\x01\x01\xc0\xa8\x01d ' @@ -122,3 +139,15 @@ class DHCPIPv4ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase): self.port_info['fixed_ips'][0]['gateway_ip'], self.port_info['fixed_ips'][0]['host_routes']) self.assertEqual(expect_bin_routes, bin_routes) + + def test_get_dhcp_options_no_gateway(self): + self._test_get_dhcp_options( + self.no_gateway_port_info, has_gateway_ip=False + ) + + def test_get_bin_routes_no_gateway(self): + expect_bin_routes = (b'\x18\x01\x01\x01\xc0\xa8\x01d ' + b'\x02\x02\x02\x02\xc0\xa8\x01e') + bin_routes = self.dhcp4_responer.get_bin_routes( + routes=self.port_info['fixed_ips'][0]['host_routes']) + self.assertEqual(expect_bin_routes, bin_routes) diff --git a/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv6.py b/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv6.py index 5c42a5f7bc1..eb4ed6fb565 100644 --- a/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv6.py +++ b/neutron/tests/unit/agent/l2/extensions/dhcp/test_ipv6.py @@ -114,14 +114,27 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase): self.assertEqual(expect_status_code, status_code) def test_get_dhcp_options(self): - self._test_get_dhcp_options() + self._test_get_dhcp_options(self.port_info, has_gateway_ip=True) def test_get_dhcp_options_zero_time(self): - self._test_get_dhcp_options(zero_time=True) + self._test_get_dhcp_options( + self.port_info, has_gateway_ip=True, zero_time=True + ) - def _test_get_dhcp_options(self, zero_time=False): - ip_info = self.dhcp6_responer.get_port_ip(self.port_info, ip_version=6) - mac = self.port_info['mac_address'] + def test_get_dhcp_options_no_gateway(self): + self._test_get_dhcp_options( + self.no_gateway_port_info, has_gateway_ip=False + ) + + def test_get_dhcp_options_zero_time_no_gateway(self): + self._test_get_dhcp_options( + self.no_gateway_port_info, has_gateway_ip=False, zero_time=True + ) + + def _test_get_dhcp_options(self, port_info,has_gateway_ip=False, + zero_time=False): + ip_info = self.dhcp6_responer.get_port_ip(port_info, ip_version=6) + mac = port_info['mac_address'] option_list = [ dhcp6.option( @@ -135,11 +148,6 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase): dhcp6.option(code=13, data=b'\x00\x00success', length=9), - dhcp6.option( - code=23, - data=(b'\xfd\xa7\xa5\xcc4`\x00\x01\x00' - b'\x00\x00\x00\x00\x00\x00\x01'), - length=16), dhcp6.option( code=24, data=b'\x0eopenstacklocal\x00', @@ -148,6 +156,26 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase): code=39, data=b'\x03(host-fda7-a5cc-3460-1--bf.openstacklocal', length=42)] + + if has_gateway_ip: + option_list.append( + dhcp6.option( + code=23, + data=(b'\xfd\xa7\xa5\xcc4`\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x01'), + length=16 + ) + ) + else: + option_list.append( + dhcp6.option( + code=23, + data=(b'\xfe\x80\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\xa9\xfe\xa9\xfe'), + length=16 + ) + ) + if zero_time: option_list.append( dhcp6.option(