Merge "Fix DHCP extension if subnet has no gateway_ip"

This commit is contained in:
Zuul
2025-05-08 17:11:14 +00:00
committed by Gerrit Code Review
6 changed files with 193 additions and 88 deletions

View File

@@ -26,6 +26,10 @@ from neutron.agent.l2.extensions.dhcp import ipv6
from neutron.api.rpc.callbacks import resources from neutron.api.rpc.callbacks import resources
LOG = logging.getLogger(__name__) 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: class DHCPExtensionPortInfoAPI:
@@ -64,7 +68,9 @@ class DHCPExtensionPortInfoAPI:
'cidr': subnet.cidr, 'cidr': subnet.cidr,
'host_routes': subnet.host_routes, 'host_routes': subnet.host_routes,
'dns_nameservers': subnet.dns_nameservers, '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) fixed_ips.append(info)
net = self.cache_api.get_resource_by_id( net = self.cache_api.get_resource_by_id(
resources.NETWORK, port_obj.network_id) resources.NETWORK, port_obj.network_id)

View File

@@ -49,13 +49,13 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase):
def get_bin_routes(self, gateway=None, routes=None): def get_bin_routes(self, gateway=None, routes=None):
bin_routes = b'' bin_routes = b''
# Default routes if gateway and gateway != constants.METADATA_V4_IP:
default_route = self.get_bin_route(constants.IPv4_ANY, gateway) # Default routes
bin_routes += default_route 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 # For some VMs they may need the metadata IP's route, we move
# the destination to gateway IP. # the destination to gateway IP.
if gateway:
meta_route = self.get_bin_route( meta_route = self.get_bin_route(
constants.METADATA_V4_CIDR, gateway) constants.METADATA_V4_CIDR, gateway)
bin_routes += meta_route bin_routes += meta_route
@@ -71,7 +71,7 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase):
net = netaddr.IPNetwork(fixed_ips[0]['cidr']) net = netaddr.IPNetwork(fixed_ips[0]['cidr'])
dns_nameservers = fixed_ips[0]['dns_nameservers'] dns_nameservers = fixed_ips[0]['dns_nameservers']
host_routes = fixed_ips[0]['host_routes'] 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) bin_server = addrconv.ipv4.text_to_bin(gateway_ip)
option_list = [] option_list = []
@@ -122,9 +122,10 @@ class DHCPIPv4Responder(dhcp_base.DHCPResponderBase):
value=struct.pack( value=struct.pack(
'!%ds' % len(cfg.CONF.dns_domain), '!%ds' % len(cfg.CONF.dns_domain),
str.encode(cfg.CONF.dns_domain)))) str.encode(cfg.CONF.dns_domain))))
option_list.append( if gateway_ip != constants.METADATA_V4_IP:
dhcp.option(tag=dhcp.DHCP_GATEWAY_ADDR_OPT, option_list.append(
value=bin_server)) dhcp.option(tag=dhcp.DHCP_GATEWAY_ADDR_OPT,
value=bin_server))
# Static routes # Static routes
option_list.append( option_list.append(
dhcp.option(tag=dhcp.DHCP_CLASSLESS_ROUTE_OPT, dhcp.option(tag=dhcp.DHCP_CLASSLESS_ROUTE_OPT,

View File

@@ -147,7 +147,7 @@ class DHCPIPv6Responder(dhcp_base.DHCPResponderBase):
def get_dhcp_options(self, mac, ip_info, req_options, req_type): def get_dhcp_options(self, mac, ip_info, req_options, req_type):
ip_addr = ip_info['ip_address'] 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'] dns_nameservers = ip_info['dns_nameservers']
option_list = [] option_list = []
@@ -210,9 +210,9 @@ class DHCPIPv6Responder(dhcp_base.DHCPResponderBase):
dhcp6.option( dhcp6.option(
code=DHCPV6_OPTION_DNS_RECURSIVE_NS, code=DHCPV6_OPTION_DNS_RECURSIVE_NS,
data=domain_serach, length=len(domain_serach))) data=domain_serach, length=len(domain_serach)))
else: elif gateway_ip != constants.METADATA_V6_IP:
# use gateway as the default DNS server address # 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( option_list.append(
dhcp6.option( dhcp6.option(
code=DHCPV6_OPTION_DNS_RECURSIVE_NS, code=DHCPV6_OPTION_DNS_RECURSIVE_NS,

View File

@@ -61,41 +61,81 @@ class FakeMsg:
self.data = packet.data 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 = { PORT_INFO = {
'device_owner': 'compute:nova', 'device_owner': 'compute:nova',
'admin_state_up': True, 'admin_state_up': True,
'network_id': 'd666ccb3-69e9-46cb-b157-bb3741d87d5a', 'network_id': 'd666ccb3-69e9-46cb-b157-bb3741d87d5a',
'fixed_ips': [ 'fixed_ips': [
{'version': 4, IPV4_INFO,
'host_routes': [ IPV6_INFO
subnet_obj.Route( ],
destination=netaddr.IPNetwork('1.1.1.0/24'), 'mac_address': '00:01:02:03:04:05',
nexthop='192.168.1.100', 'port_id': '9a0e1889-f05f-43c7-a319-e1a723ed1587',
subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f'), 'mtu': 1450
subnet_obj.Route( }
destination=netaddr.IPNetwork('2.2.2.2/32'),
nexthop='192.168.1.101',
subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f')], IPV4_INFO_NO_GATEWAY = {
'subnet_id': 'daed3c3d-d95a-48a8-a8b1-17d408cd760f', **IPV4_INFO,
'dns_nameservers': [ 'gateway_ip': netaddr.IPAddress(constants.METADATA_V4_IP)
subnet_obj.DNSNameServer( }
address='8.8.8.8', IPV6_INFO_NO_GATEWAY = {
order=0, **IPV6_INFO,
subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f'), 'gateway_ip': netaddr.IPAddress(constants.METADATA_V6_IP)
subnet_obj.DNSNameServer( }
address='8.8.4.4',
order=1,
subnet_id='daed3c3d-d95a-48a8-a8b1-17d408cd760f')], NO_GATEWAY_PORT_INFO = {
'cidr': net_utils.AuthenticIPNetwork('192.168.111.0/24'), 'device_owner': 'compute:nova',
'ip_address': '192.168.111.45', 'admin_state_up': True,
'gateway_ip': netaddr.IPAddress('192.168.111.1')}, 'network_id': 'd666ccb3-69e9-46cb-b157-bb3741d87d5a',
{'version': 6, 'fixed_ips': [
'host_routes': [], IPV4_INFO_NO_GATEWAY,
'subnet_id': 'bd013460-b05f-4927-a4c6-5127584b2487', IPV6_INFO_NO_GATEWAY
'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')}
], ],
'mac_address': '00:01:02:03:04:05', 'mac_address': '00:01:02:03:04:05',
'port_id': '9a0e1889-f05f-43c7-a319-e1a723ed1587', 'port_id': '9a0e1889-f05f-43c7-a319-e1a723ed1587',
@@ -128,6 +168,7 @@ class DHCPResponderBaseTestCase(base.BaseTestCase):
self.base_responer.handle_dhcp = mock.Mock() self.base_responer.handle_dhcp = mock.Mock()
self.port_info = PORT_INFO self.port_info = PORT_INFO
self.no_gateway_port_info = NO_GATEWAY_PORT_INFO
def _create_test_dhcp_request_packet(self): def _create_test_dhcp_request_packet(self):
option_list = [] option_list = []

View File

@@ -67,53 +67,70 @@ class DHCPIPv4ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
dhcp_pkt = ret_pkt.get_protocols(dhcp.dhcp) dhcp_pkt = ret_pkt.get_protocols(dhcp.dhcp)
self.assertIsNotNone(dhcp_pkt) self.assertIsNotNone(dhcp_pkt)
def test_get_dhcp_options(self): def _test_get_dhcp_options(self, port_info, has_gateway_ip=False):
expect_bin_routes = (b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01' expect_bin_routes = (
b'\x18\x01\x01\x01\xc0\xa8\x01d ' b'\x18\x01\x01\x01\xc0\xa8\x01d '
b'\x02\x02\x02\x02\xc0\xa8\x01e') 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( expect_offer_options = dhcp.options(
magic_cookie='99.130.83.99', magic_cookie='99.130.83.99',
option_list=[ option_list=offer_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')],
options_len=0) 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, self._compare_option_values(expect_offer_options.option_list,
offer_options.option_list) offer_options.option_list)
expect_ack_options = dhcp.options( expect_ack_options = dhcp.options(
magic_cookie='99.130.83.99', magic_cookie='99.130.83.99',
option_list=[ option_list=ack_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')],
options_len=0) options_len=0)
ack_options = self.dhcp4_responer.get_dhcp_options( 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, self._compare_option_values(expect_ack_options.option_list,
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): def test_get_bin_routes(self):
expect_bin_routes = (b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01' expect_bin_routes = (b'\x00\xc0\xa8o\x01 \xa9\xfe\xa9\xfe\xc0\xa8o\x01'
b'\x18\x01\x01\x01\xc0\xa8\x01d ' 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]['gateway_ip'],
self.port_info['fixed_ips'][0]['host_routes']) self.port_info['fixed_ips'][0]['host_routes'])
self.assertEqual(expect_bin_routes, bin_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)

View File

@@ -114,14 +114,27 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
self.assertEqual(expect_status_code, status_code) self.assertEqual(expect_status_code, status_code)
def test_get_dhcp_options(self): 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): 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): def test_get_dhcp_options_no_gateway(self):
ip_info = self.dhcp6_responer.get_port_ip(self.port_info, ip_version=6) self._test_get_dhcp_options(
mac = self.port_info['mac_address'] 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 = [ option_list = [
dhcp6.option( dhcp6.option(
@@ -135,11 +148,6 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
dhcp6.option(code=13, dhcp6.option(code=13,
data=b'\x00\x00success', data=b'\x00\x00success',
length=9), 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( dhcp6.option(
code=24, code=24,
data=b'\x0eopenstacklocal\x00', data=b'\x0eopenstacklocal\x00',
@@ -148,6 +156,26 @@ class DHCPIPv6ResponderTestCase(dhcp_test_base.DHCPResponderBaseTestCase):
code=39, code=39,
data=b'\x03(host-fda7-a5cc-3460-1--bf.openstacklocal', data=b'\x03(host-fda7-a5cc-3460-1--bf.openstacklocal',
length=42)] 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: if zero_time:
option_list.append( option_list.append(
dhcp6.option( dhcp6.option(