diff options
-rw-r--r-- | data/templates/dhcp-server/dhcpd.conf.tmpl | 37 | ||||
-rw-r--r-- | interface-definitions/dhcp-server.xml.in | 98 | ||||
-rwxr-xr-x | src/conf_mode/dhcp_server.py | 36 | ||||
-rwxr-xr-x | src/migration-scripts/dhcp-server/5-to-6 | 25 |
4 files changed, 97 insertions, 99 deletions
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 108c9cc85..54fff3ded 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -31,32 +31,25 @@ option wpad-url code 252 = text; {% endfor %} {% endif %} -{% if shared_network_name is defined and shared_network_name is not none %} -{% for network, network_config in shared_network_name.items() if network_config.disable is not defined %} -{% if network_config.subnet is defined and network_config.subnet is not none %} -{% for subnet, subnet_config in network_config.subnet.items() %} -{% if subnet_config.failover is defined and subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %} -# Failover configuration for {{ subnet }} -failover peer "{{ subnet_config.failover.name }}" { -{% if subnet_config.failover.status == 'primary' %} +{% if failover is defined and failover is not none %} +{% set dhcp_failover_name = 'VyOS-DHCP-failover-peer' %} +# DHCP failover configuration +failover peer "{{ dhcp_failover_name }}" { +{% if failover.status == 'primary' %} primary; mclt 1800; split 128; -{% elif subnet_config.failover.status == 'secondary' %} +{% elif failover.status == 'secondary' %} secondary; -{% endif %} - address {{ subnet_config.failover.local_address }}; +{% endif %} + address {{ failover.source_address }}; port 520; - peer address {{ subnet_config.failover.peer_address }}; + peer address {{ failover.remote }}; peer port 520; max-response-delay 30; max-unacked-updates 10; load balance max seconds 3; } -{% endif %} -{% endfor %} -{% endif %} -{% endfor %} {% endif %} {% if listen_address is defined and listen_address is not none %} @@ -184,23 +177,17 @@ shared-network {{ network | replace('_','-') }} { } {% endfor %} {% endif %} -{% if subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %} pool { - failover peer "{{ subnet_config.failover.name }}"; +{% if subnet_config.enable_failover is defined %} + failover peer "{{ dhcp_failover_name }}"; deny dynamic bootp clients; +{% endif %} {% if subnet_config.range is defined and subnet_config.range is not none %} {% for range, range_options in subnet_config.range.items() %} range {{ range_options.start }} {{ range_options.stop }}; {% endfor %} {% endif %} } -{% else %} -{% if subnet_config.range is defined and subnet_config.range is not none %} -{% for range, range_options in subnet_config.range.items() %} - range {{ range_options.start }} {{ range_options.stop }}; -{% endfor %} -{% endif %} -{% endif %} } {% endfor %} {% endif %} diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index e629d96ab..960b8a4f0 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -16,6 +16,46 @@ <valueless/> </properties> </leafNode> + <node name="failover"> + <properties> + <help>DHCP failover configuration</help> + </properties> + <children> + #include <include/source-address-ipv4.xml.i> + <leafNode name="remote"> + <properties> + <help>IPv4 remote address used for connectio</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address of failover peer</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="status"> + <properties> + <help>Failover hierarchy</help> + <completionHelp> + <list>primary secondary</list> + </completionHelp> + <valueHelp> + <format>primary</format> + <description>Configure this server to be the primary node</description> + </valueHelp> + <valueHelp> + <format>secondary</format> + <description>Configure this server to be the secondary node</description> + </valueHelp> + <constraint> + <regex>^(primary|secondary)$</regex> + </constraint> + <constraintErrorMessage>Invalid DHCP failover peer status</constraintErrorMessage> + </properties> + </leafNode> + </children> + </node> <leafNode name="global-parameters"> <properties> <help>Additional global parameters for DHCP server. You must @@ -118,6 +158,12 @@ #include <include/name-server-ipv4.xml.i> #include <include/dhcp-domain-name.xml.i> #include <include/dhcp-server-domain-search.xml.i> + <leafNode name="enable-failover"> + <properties> + <help>Enable DHCP failover support for this subnet</help> + <valueless/> + </properties> + </leafNode> <leafNode name="exclude"> <properties> <help>IP address to exclude from DHCP lease range</help> @@ -131,58 +177,6 @@ <multi/> </properties> </leafNode> - <node name="failover"> - <properties> - <help>DHCP failover parameters</help> - </properties> - <children> - <leafNode name="local-address"> - <properties> - <help>IP address for failover peer to connect [REQUIRED]</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address to exclude from lease range</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="name"> - <properties> - <help>DHCP failover peer name [REQUIRED]</help> - <constraint> - <regex>[-_a-zA-Z0-9.]+</regex> - </constraint> - <constraintErrorMessage>Invalid failover peer name. May only contain letters, numbers and .-_</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="peer-address"> - <properties> - <help>IP address of failover peer [REQUIRED]</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address of failover peer</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="status"> - <properties> - <help>DHCP failover peer status (primary|secondary) [REQUIRED]</help> - <completionHelp> - <list>primary secondary</list> - </completionHelp> - <constraint> - <regex>^(primary|secondary)$</regex> - </constraint> - <constraintErrorMessage>Invalid DHCP failover peer status</constraintErrorMessage> - </properties> - </leafNode> - </children> - </node> <leafNode name="ip-forwarding"> <properties> <help>Enable IP forwarding on client</help> diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 8d6cef8b7..5b3809017 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -148,9 +148,9 @@ def verify(dhcp): 'At least one DHCP shared network must be configured.') # Inspect shared-network/subnet - failover_names = [] listen_ok = False subnets = [] + failover_ok = False # A shared-network requires a subnet definition for network, network_config in dhcp['shared_network_name'].items(): @@ -159,11 +159,19 @@ def verify(dhcp): 'lease subnet must be configured.') for subnet, subnet_config in network_config['subnet'].items(): + # All delivered static routes require a next-hop to be set if 'static_route' in subnet_config: for route, route_option in subnet_config['static_route'].items(): if 'next_hop' not in route_option: raise ConfigError(f'DHCP static-route "{route}" requires router to be defined!') + # DHCP failover needs at least one subnet that uses it + if 'enable_failover' in subnet_config: + if 'failover' not in dhcp: + raise ConfigError(f'Can not enable failover for "{subnet}" in "{network}".\n' \ + 'Failover is not configured globally!') + failover_ok = True + # Check if DHCP address range is inside configured subnet declaration if 'range' in subnet_config: networks = [] @@ -192,23 +200,6 @@ def verify(dhcp): tmp = IPRange(range_config['start'], range_config['stop']) networks.append(tmp) - if 'failover' in subnet_config: - for key in ['local_address', 'peer_address', 'name', 'status']: - if key not in subnet_config['failover']: - raise ConfigError(f'Missing DHCP failover parameter "{key}"!') - - # Failover names must be uniquie - if subnet_config['failover']['name'] in failover_names: - name = subnet_config['failover']['name'] - raise ConfigError(f'DHCP failover names must be unique:\n' \ - f'{name} has already been configured!') - failover_names.append(subnet_config['failover']['name']) - - # Failover requires start/stop ranges for pool - if 'range' not in subnet_config: - raise ConfigError(f'DHCP failover requires at least one start-stop range to be configured\n'\ - f'within shared-network "{network}, {subnet}" for using failover!') - # Exclude addresses must be in bound if 'exclude' in subnet_config: for exclude in subnet_config['exclude']: @@ -252,6 +243,15 @@ def verify(dhcp): if net.overlaps(net2): raise ConfigError('Conflicting subnet ranges: "{net}" overlaps "{net2}"!') + if 'failover' in dhcp: + if not failover_ok: + raise ConfigError('DHCP failover must be enabled for at least one subnet!') + + for key in ['source_address', 'remote', 'status']: + if key not in dhcp['failover']: + tmp = key.replace('_', '-') + raise ConfigError(f'DHCP failover requires "{tmp}" to be specified!') + for address in (dict_search('listen_address', dhcp) or []): if is_addr_assigned(address): listen_ok = True diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 index 7f447ac17..39bbb9f50 100755 --- a/src/migration-scripts/dhcp-server/5-to-6 +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -29,16 +29,16 @@ file_name = sys.argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['service', 'dhcp-server', 'shared-network-name'] +base = ['service', 'dhcp-server'] config = ConfigTree(config_file) -if not config.exists(base): +if not config.exists(base + ['shared-network-name']): # Nothing to do exit(0) # Run this for every instance if 'shared-network-name' -for network in config.list_nodes(base): - base_network = base + [network] +for network in config.list_nodes(base + ['shared-network-name']): + base_network = base + ['shared-network-name', network] if not config.exists(base_network + ['subnet']): continue @@ -60,6 +60,23 @@ for network in config.list_nodes(base): if config.exists(base_subnet + ['dns-server']): config.rename(base_subnet + ['dns-server'], 'name-server') + + # T3672: ISC DHCP server only supports one failover peer + if config.exists(base_subnet + ['failover']): + # There can only be one failover configuration, if none is present + # we add the first one + if not config.exists(base + ['failover']): + local = config.return_value(base_subnet + ['failover', 'local-address']) + remote = config.return_value(base_subnet + ['failover', 'peer-address']) + status = config.return_value(base_subnet + ['failover', 'status']) + + config.set(base + ['failover', 'remote'], value=remote) + config.set(base + ['failover', 'source-address'], value=local) + config.set(base + ['failover', 'status'], value=status) + + config.delete(base_subnet + ['failover']) + config.set(base_subnet + ['enable-failover']) + try: with open(file_name, 'w') as f: f.write(config.to_string()) |