From eecec6b5caeaef14a03ddbb1d09f9c599273b998 Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Sun, 6 Dec 2020 13:44:56 +0100
Subject: dhcp: T2562: add "listen-address" CLI node for better DHCP relay
 support

Running ISC DHCP server as backend server for multiple pools served to relay
agents requires DHCPd to explicitly listen on give interfaces or a "transit"
subnet declaration facing the network where we receive the DHCPREQ messages on.

This implements a new "listen-address" CLI node, the given address is validated
if it is assigned to the system and upon success, a proper "subnet { }" statement
is added into dhcpd.conf
---
 python/vyos/template.py | 41 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)

(limited to 'python/vyos')

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):
-- 
cgit v1.2.3