Fix DHCP extension if subnet has no gateway_ip
The subnet gateway_ip can be none, so the DHCP agent extension will get failed to assemble the response packet. This patch adds default gateway IP address for DHCP, for IPv4 it is 169.254.169.254, for IPv6 it is fe80::a9fe:a9fe. Partially-Implements: bp/distributed-dhcp-for-ml2-ovs Related-Bug: #2107552 Change-Id: I709b73c242285930c4ade5bb9e6f167495b90d80
This commit is contained in:
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 = []
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user