From daffee2cbf001dab13799f5b2b69330162491214 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Sun, 7 Jan 2024 09:24:10 +0100 Subject: dhcp: T3316: Move options to separate node and extend scopes --- python/vyos/kea.py | 56 ++++++++++++++++++++++++++++++++++--------------- python/vyos/template.py | 15 +++++++++---- 2 files changed, 50 insertions(+), 21 deletions(-) (limited to 'python/vyos') diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 819fe16a9..2ca73044b 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -92,17 +92,28 @@ def kea_parse_options(config): options.append({'name': 'pcode', 'data': tz_string}) options.append({'name': 'tcode', 'data': config['time_zone']}) + unifi_controller = dict_search_args(config, 'vendor_option', 'ubiquiti', 'unifi_controller') + if unifi_controller: + options.append({ + 'name': 'unifi-controller', + 'data': unifi_controller, + 'space': 'ubnt' + }) + return options def kea_parse_subnet(subnet, config): out = {'subnet': subnet} - options = kea_parse_options(config) + options = [] + + if 'option' in config: + out['option-data'] = kea_parse_options(config['option']) - if 'bootfile_name' in config: - out['boot-file-name'] = config['bootfile_name'] + if 'bootfile_name' in config['option']: + out['boot-file-name'] = config['option']['bootfile_name'] - if 'bootfile_server' in config: - out['next-server'] = config['bootfile_server'] + if 'bootfile_server' in config['option']: + out['next-server'] = config['option']['bootfile_server'] if 'lease' in config: out['valid-lifetime'] = int(config['lease']) @@ -112,7 +123,20 @@ def kea_parse_subnet(subnet, config): pools = [] for num, range_config in config['range'].items(): start, stop = range_config['start'], range_config['stop'] - pools.append({'pool': f'{start} - {stop}'}) + pool = { + 'pool': f'{start} - {stop}' + } + + if 'option' in range_config: + pool['option-data'] = kea_parse_options(range_config['option']) + + if 'bootfile_name' in range_config['option']: + pool['boot-file-name'] = range_config['option']['bootfile_name'] + + if 'bootfile_server' in range_config['option']: + pool['next-server'] = range_config['option']['bootfile_server'] + + pools.append(pool) out['pools'] = pools if 'static_mapping' in config: @@ -134,19 +158,17 @@ def kea_parse_subnet(subnet, config): if 'ip_address' in host_config: reservation['ip-address'] = host_config['ip_address'] - reservations.append(reservation) - out['reservations'] = reservations + if 'option' in host_config: + reservation['option-data'] = kea_parse_options(host_config['option']) - unifi_controller = dict_search_args(config, 'vendor_option', 'ubiquiti', 'unifi_controller') - if unifi_controller: - options.append({ - 'name': 'unifi-controller', - 'data': unifi_controller, - 'space': 'ubnt' - }) + if 'bootfile_name' in host_config['option']: + reservation['boot-file-name'] = host_config['option']['bootfile_name'] - if options: - out['option-data'] = options + if 'bootfile_server' in host_config['option']: + reservation['next-server'] = host_config['option']['bootfile_server'] + + reservations.append(reservation) + out['reservations'] = reservations return out diff --git a/python/vyos/template.py b/python/vyos/template.py index 29ea0889b..c0c09f690 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -842,15 +842,22 @@ def kea_shared_network_json(shared_networks): 'authoritative': ('authoritative' in config), 'subnet4': [] } - options = kea_parse_options(config) + + if 'option' in config: + network['option-data'] = kea_parse_options(config['option']) + + if 'bootfile_name' in config['option']: + network['boot-file-name'] = config['option']['bootfile_name'] + + if 'bootfile_server' in config['option']: + network['next-server'] = config['option']['bootfile_server'] if 'subnet' in config: for subnet, subnet_config in config['subnet'].items(): + if 'disable' in subnet_config: + continue network['subnet4'].append(kea_parse_subnet(subnet, subnet_config)) - if options: - network['option-data'] = options - out.append(network) return dumps(out, indent=4) -- cgit v1.2.3 From 74ddb29c6c9ce31450234e77fd39c73d0d51c3c5 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:41:23 +0100 Subject: dhcp: T3316: Fix `listen-address` handling and add `listen-interface` as supported by Kea --- data/templates/dhcp-server/kea-dhcp4.conf.j2 | 8 ++++++++ .../include/listen-interface-multi-broadcast.xml.i | 18 ++++++++++++++++++ interface-definitions/service_dhcp-server.xml.in | 1 + python/vyos/template.py | 17 +++++++++++++++++ python/vyos/utils/network.py | 4 ++-- smoketest/scripts/cli/test_service_dhcp-server.py | 9 +++++++-- src/conf_mode/service_dhcp-server.py | 9 ++++++++- 7 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 interface-definitions/include/listen-interface-multi-broadcast.xml.i (limited to 'python/vyos') diff --git a/data/templates/dhcp-server/kea-dhcp4.conf.j2 b/data/templates/dhcp-server/kea-dhcp4.conf.j2 index 6ab13ab27..629fa952a 100644 --- a/data/templates/dhcp-server/kea-dhcp4.conf.j2 +++ b/data/templates/dhcp-server/kea-dhcp4.conf.j2 @@ -1,8 +1,16 @@ { "Dhcp4": { "interfaces-config": { +{% if listen_address is vyos_defined %} + "interfaces": {{ listen_address | kea_address_json }}, + "dhcp-socket-type": "udp", +{% elif listen_interface is vyos_defined %} + "interfaces": {{ listen_interface | tojson }}, + "dhcp-socket-type": "raw", +{% else %} "interfaces": [ "*" ], "dhcp-socket-type": "raw", +{% endif %} "service-sockets-max-retries": 5, "service-sockets-retry-wait-time": 5000 }, diff --git a/interface-definitions/include/listen-interface-multi-broadcast.xml.i b/interface-definitions/include/listen-interface-multi-broadcast.xml.i new file mode 100644 index 000000000..b3d5a3ecc --- /dev/null +++ b/interface-definitions/include/listen-interface-multi-broadcast.xml.i @@ -0,0 +1,18 @@ + + + + Interface for DHCP Relay Agent to listen for requests + + + + + txt + Interface name + + + #include + + + + + diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in index a5cee62d1..27485b6d4 100644 --- a/interface-definitions/service_dhcp-server.xml.in +++ b/interface-definitions/service_dhcp-server.xml.in @@ -74,6 +74,7 @@ #include + #include Name of DHCP shared network diff --git a/python/vyos/template.py b/python/vyos/template.py index c0c09f690..1368f1f61 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -786,6 +786,23 @@ def range_to_regex(num_range): regex = range_to_regex(num_range) return f'({regex})' +@register_filter('kea_address_json') +def kea_address_json(addresses): + from json import dumps + from vyos.utils.network import is_addr_assigned + + out = [] + + for address in addresses: + ifname = is_addr_assigned(address, return_ifname=True) + + if not ifname: + continue + + out.append(f'{ifname}/{address}') + + return dumps(out) + @register_filter('kea_failover_json') def kea_failover_json(config): from json import dumps diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py index 997ee6309..b782e0bd8 100644 --- a/python/vyos/utils/network.py +++ b/python/vyos/utils/network.py @@ -308,7 +308,7 @@ def is_ipv6_link_local(addr): return False -def is_addr_assigned(ip_address, vrf=None) -> bool: +def is_addr_assigned(ip_address, vrf=None, return_ifname=False) -> bool | str: """ Verify if the given IPv4/IPv6 address is assigned to any interface """ from netifaces import interfaces from vyos.utils.network import get_interface_config @@ -323,7 +323,7 @@ def is_addr_assigned(ip_address, vrf=None) -> bool: continue if is_intf_addr_assigned(interface, ip_address): - return True + return interface if return_ifname else True return False diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 99ac406cd..ef6191fb1 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -32,6 +32,7 @@ CTRL_PROCESS_NAME = 'kea-ctrl-agent' KEA4_CONF = '/run/kea/kea-dhcp4.conf' KEA4_CTRL = '/run/kea/dhcp4-ctrl-socket' base_path = ['service', 'dhcp-server'] +interface = 'dum8765' subnet = '192.0.2.0/25' router = inc_ip(subnet, 1) dns_1 = inc_ip(subnet, 2) @@ -46,11 +47,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): cls.cli_delete(cls, base_path) cidr_mask = subnet.split('/')[-1] - cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}']) + cls.cli_set(cls, ['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}']) @classmethod def tearDownClass(cls): - cls.cli_delete(cls, ['interfaces', 'dummy', 'dum8765']) + cls.cli_delete(cls, ['interfaces', 'dummy', interface]) super(TestServiceDHCPServer, cls).tearDownClass() def tearDown(self): @@ -95,6 +96,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): range_1_start = inc_ip(subnet, 40) range_1_stop = inc_ip(subnet, 50) + self.cli_set(base_path + ['listen-interface', interface]) + pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['option', 'default-router', router]) @@ -116,6 +119,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): config = read_file(KEA4_CONF) obj = loads(config) + self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [interface]) self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400) @@ -607,6 +611,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): config = read_file(KEA4_CONF) obj = loads(config) + self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [f'{interface}/{router}']) self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'RELAY') self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', relay_subnet) diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py index 7ebc560ba..329e18993 100755 --- a/src/conf_mode/service_dhcp-server.py +++ b/src/conf_mode/service_dhcp-server.py @@ -31,6 +31,7 @@ from vyos.utils.file import chmod_775 from vyos.utils.file import makedir from vyos.utils.file import write_file from vyos.utils.process import call +from vyos.utils.network import interface_exists from vyos.utils.network import is_subnet_connected from vyos.utils.network import is_addr_assigned from vyos import ConfigError @@ -294,12 +295,18 @@ def verify(dhcp): else: raise ConfigError(f'listen-address "{address}" not configured on any interface') - if not listen_ok: raise ConfigError('None of the configured subnets have an appropriate primary IP address on any\n' 'broadcast interface configured, nor was there an explicit listen-address\n' 'configured for serving DHCP relay packets!') + if 'listen_address' in dhcp and 'listen_interface' in dhcp: + raise ConfigError(f'Cannot define listen-address and listen-interface at the same time') + + for interface in (dict_search('listen_interface', dhcp) or []): + if not interface_exists(interface): + raise ConfigError(f'listen-interface "{interface}" does not exist') + return None def generate(dhcp): -- cgit v1.2.3 From 39bf15289ca10ff5b61eb4070292ffb13f53e94e Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:44:16 +0100 Subject: dhcp: T3316: Workaround to append domain suffix to hostfile entries --- python/vyos/kea.py | 4 ++-- src/system/on-dhcp-event.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'python/vyos') diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 2ca73044b..3d8cf3637 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -25,7 +25,7 @@ from vyos.template import netmask_from_cidr from vyos.utils.dict import dict_search_args from vyos.utils.file import file_permissions from vyos.utils.file import read_file -from vyos.utils.process import cmd +from vyos.utils.process import run kea4_options = { 'name_server': 'domain-name-servers', @@ -315,7 +315,7 @@ def _ctrl_socket_command(path, command, args=None): return None if file_permissions(path) != '0775': - cmd(f'sudo chmod 775 {path}') + run(f'sudo chmod 775 {path}') with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: sock.connect(path) diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh index 3534fe601..e1a9f1884 100755 --- a/src/system/on-dhcp-event.sh +++ b/src/system/on-dhcp-event.sh @@ -17,6 +17,32 @@ fi action=$1 hostsd_client="/usr/bin/vyos-hostsd-client" +get_subnet_domain_name () { + python3 <