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
commit aa64bd2651
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
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)

View File

@ -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,

View File

@ -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,

View File

@ -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 = []

View File

@ -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)

View File

@ -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(