summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl8
-rw-r--r--interface-definitions/dhcp-server.xml.in11
-rw-r--r--python/vyos/template.py41
-rwxr-xr-xsrc/conf_mode/dhcp_server.py16
4 files changed, 64 insertions, 12 deletions
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
index bcf425abd..56a5f4bcd 100644
--- a/data/templates/dhcp-server/dhcpd.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -62,6 +62,14 @@ failover peer "{{ subnet_config.failover.name }}" {
{% endif %}
{% endfor %}
{% endif %}
+{% if listen_address is defined and listen_address is not none %}
+
+# DHCP server serving relay subnet, we need a connector to the real world
+{% for address in listen_address %}
+# Connected subnet statement for listen-address {{ address }}
+subnet {{ address | network_from_ipv4 }} netmask {{ address | netmask_from_ipv4 }} { }
+{% endfor %}
+{% endif %}
# Shared network configration(s)
{% if shared_network_name is defined and shared_network_name is not none %}
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index 2f78f11ea..161eeed28 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -11,13 +11,13 @@
<children>
<leafNode name="disable">
<properties>
- <help>Option to disable DHCP server</help>
+ <help>Disable DHCP server</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="dynamic-dns-update">
<properties>
- <help>DHCP server to dynamically update the Domain Name System (DNS)</help>
+ <help>Dynamically update Domain Name System (RFC4702)</help>
<valueless/>
</properties>
</leafNode>
@@ -32,19 +32,20 @@
</leafNode>
<leafNode name="hostfile-update">
<properties>
- <help>Enable DHCP server updating /etc/hosts (per client lease)</help>
+ <help>Updating /etc/hosts file (per client lease)</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="host-decl-name">
<properties>
- <help>Instruct server to use host declaration name for forward DNS name</help>
+ <help>Use host declaration name for forward DNS name</help>
<valueless/>
</properties>
</leafNode>
+ #include <include/listen-address.xml.i>
<tagNode name="shared-network-name">
<properties>
- <help>DHCP shared network name [REQUIRED]</help>
+ <help>Name of DHCP shared network</help>
<constraint>
<regex>[-_a-zA-Z0-9.]+</regex>
</constraint>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 5993ffd95..63d400642 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -123,23 +123,56 @@ def render(
##################################
@register_filter('address_from_cidr')
-def address_from_cidr(text):
+def address_from_cidr(prefix):
""" Take an IPv4/IPv6 CIDR prefix and convert the network to an "address".
Example:
192.0.2.0/24 -> 192.0.2.0, 2001:db8::/48 -> 2001:db8::
"""
from ipaddress import ip_network
- return str(ip_network(text).network_address)
+ return str(ip_network(prefix).network_address)
@register_filter('netmask_from_cidr')
-def netmask_from_cidr(text):
+def netmask_from_cidr(prefix):
""" Take CIDR prefix and convert the prefix length to a "subnet mask".
Example:
- 192.0.2.0/24 -> 255.255.255.0
- 2001:db8::/48 -> ffff:ffff:ffff::
"""
from ipaddress import ip_network
- return str(ip_network(text).netmask)
+ return str(ip_network(prefix).netmask)
+
+@register_filter('netmask_from_ipv4')
+def netmask_from_ipv4(address):
+ """ Take IP address and search all attached interface IP addresses for the
+ given one. After address has been found, return the associated netmask.
+
+ Example:
+ - 172.18.201.10 -> 255.255.255.128
+ """
+ from netifaces import interfaces, ifaddresses, AF_INET
+ for interface in interfaces():
+ tmp = ifaddresses(interface)
+ if AF_INET in tmp:
+ for af_addr in tmp[AF_INET]:
+ if 'addr' in af_addr:
+ if af_addr['addr'] == address:
+ return af_addr['netmask']
+
+ raise ValueError
+
+@register_filter('network_from_ipv4')
+def network_from_ipv4(address):
+ """ Take IP address and search all attached interface IP addresses for the
+ given one. After address has been found, return the associated network
+ address.
+
+ Example:
+ - 172.18.201.10 has mask 255.255.255.128 -> network is 172.18.201.0
+ """
+ netmask = netmask_from_ipv4(address)
+ from ipaddress import ip_interface
+ cidr_prefix = ip_interface(f'{address}/{netmask}').network
+ return address_from_cidr(cidr_prefix)
@register_filter('is_ip')
def is_ip(addr):
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index 9be586cdf..1ab2d8d16 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -26,6 +26,7 @@ from vyos.template import render
from vyos.util import call
from vyos.util import dict_search
from vyos.validate import is_subnet_connected
+from vyos.validate import is_addr_assigned
from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
@@ -246,10 +247,19 @@ def verify(dhcp):
if net.overlaps(net2):
raise ConfigError('Conflicting subnet ranges: "{net}" overlaps "{net2}"!')
+ for address in (dict_search('listen_address', dhcp) or []):
+ if is_addr_assigned(address):
+ listen_ok = True
+ # no need to probe further networks, we have one that is valid
+ continue
+ else:
+ raise ConfigError(f'listen-address "{address}" not configured on any interface')
+
+
if not listen_ok:
- raise ConfigError('DHCP server configuration error! None of the configured\n' \
- 'subnets have an appropriate primary IP address on any\n'
- 'broadcast interface.')
+ 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!')
return None