diff options
| author | David Vølker <david@voelker.dk> | 2025-08-15 09:31:49 +0200 |
|---|---|---|
| committer | David Vølker <david@voelker.dk> | 2025-08-15 09:31:49 +0200 |
| commit | 974f9a5d5e13241e2b4ea54152bf835db6b7feff (patch) | |
| tree | b816d2c6c6bea3e670f44e880f32b10d311d353e | |
| parent | 862817a7207c5b5d17c67451bac602d296b88230 (diff) | |
| download | vyos-1x-974f9a5d5e13241e2b4ea54152bf835db6b7feff.tar.gz vyos-1x-974f9a5d5e13241e2b4ea54152bf835db6b7feff.zip | |
kea: T6211: add VRF support for KEA dhcp server
23 files changed, 1661 insertions, 700 deletions
diff --git a/data/templates/dhcp-server/kea-dhcp-ddns.conf.j2 b/data/templates/dhcp-server/kea-dhcp-ddns.conf.j2 index 7b0394a88..e734a37f3 100644 --- a/data/templates/dhcp-server/kea-dhcp-ddns.conf.j2 +++ b/data/templates/dhcp-server/kea-dhcp-ddns.conf.j2 @@ -3,8 +3,13 @@ "ip-address": "127.0.0.1", "port": 53001, "control-socket": { +{% if vrf_context is vyos_defined %} + "socket-type": "unix", + "socket-name": "/run/kea/kea-ddns-{{ vrf_context }}-ctrl-socket" +{% else %} "socket-type": "unix", "socket-name": "/run/kea/kea-ddns-ctrl-socket" +{% endif %} }, "tsig-keys": {{ dynamic_dns_update | kea_dynamic_dns_update_tsig_key_json }}, "forward-ddns" : { diff --git a/data/templates/dhcp-server/kea-dhcp4.conf.j2 b/data/templates/dhcp-server/kea-dhcp4.conf.j2 index d08ca0eaa..60c9cf9b2 100644 --- a/data/templates/dhcp-server/kea-dhcp4.conf.j2 +++ b/data/templates/dhcp-server/kea-dhcp4.conf.j2 @@ -15,8 +15,13 @@ "service-sockets-retry-wait-time": 5000 }, "control-socket": { +{% if vrf_context is vyos_defined %} + "socket-type": "unix", + "socket-name": "/run/kea/dhcp4-{{ vrf_context }}-ctrl-socket" +{% else %} "socket-type": "unix", "socket-name": "/run/kea/dhcp4-ctrl-socket" +{% endif %} }, "lease-database": { "type": "memfile", diff --git a/data/templates/dhcp-server/kea-dhcp6.conf.j2 b/data/templates/dhcp-server/kea-dhcp6.conf.j2 index 4745d693c..4352bbae7 100644 --- a/data/templates/dhcp-server/kea-dhcp6.conf.j2 +++ b/data/templates/dhcp-server/kea-dhcp6.conf.j2 @@ -10,8 +10,13 @@ "service-sockets-retry-wait-time": 5000 }, "control-socket": { +{% if vrf_context is vyos_defined %} + "socket-type": "unix", + "socket-name": "/run/kea/dhcp6-{{ vrf_context }}-ctrl-socket" +{% else %} "socket-type": "unix", "socket-name": "/run/kea/dhcp6-ctrl-socket" +{% endif %} }, "lease-database": { "type": "memfile", diff --git a/interface-definitions/include/dhcp/dhcp-server-common-config.xml.i b/interface-definitions/include/dhcp/dhcp-server-common-config.xml.i new file mode 100644 index 000000000..60eb01489 --- /dev/null +++ b/interface-definitions/include/dhcp/dhcp-server-common-config.xml.i @@ -0,0 +1,342 @@ +<!-- include start from dhcp/dhcp-server-common-config.xml.i --> +#include <include/generic-disable-node.xml.i> +<node name="dynamic-dns-update"> + <properties> + <help>Dynamically update Domain Name System (RFC4702)</help> + </properties> + <children> + #include <include/dhcp/ddns-settings.xml.i> + <tagNode name="tsig-key"> + <properties> + <help>TSIG key definition for DNS updates</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> + </constraint> + <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, hyphen and underscore</constraintErrorMessage> + </properties> + <children> + <leafNode name="algorithm"> + <properties> + <help>TSIG key algorithm</help> + <completionHelp> + <list>md5 sha1 sha224 sha256 sha384 sha512</list> + </completionHelp> + <valueHelp> + <format>md5</format> + <description>MD5 HMAC algorithm</description> + </valueHelp> + <valueHelp> + <format>sha1</format> + <description>SHA1 HMAC algorithm</description> + </valueHelp> + <valueHelp> + <format>sha224</format> + <description>SHA224 HMAC algorithm</description> + </valueHelp> + <valueHelp> + <format>sha256</format> + <description>SHA256 HMAC algorithm</description> + </valueHelp> + <valueHelp> + <format>sha384</format> + <description>SHA384 HMAC algorithm</description> + </valueHelp> + <valueHelp> + <format>sha512</format> + <description>SHA512 HMAC algorithm</description> + </valueHelp> + <constraint> + <regex>(md5|sha1|sha224|sha256|sha384|sha512)</regex> + </constraint> + <constraintErrorMessage>Invalid TSIG key algorithm</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="secret"> + <properties> + <help>TSIG key secret (base64-encoded)</help> + <constraint> + <validator name="base64"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <tagNode name="forward-domain"> + <properties> + <help>Forward DNS domain name</help> + <constraint> + <validator name="fqdn"/> + </constraint> + <constraintErrorMessage>Invalid forward DNS domain name</constraintErrorMessage> + </properties> + <children> + <leafNode name="key-name"> + <properties> + <help>TSIG key name for forward DNS updates</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> + </constraint> + <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, numbers, hyphen and underscore</constraintErrorMessage> + </properties> + </leafNode> + #include <include/dhcp/ddns-dns-server.xml.i> + </children> + </tagNode> + <tagNode name="reverse-domain"> + <properties> + <help>Reverse DNS domain name</help> + <constraint> + <validator name="fqdn"/> + </constraint> + <constraintErrorMessage>Invalid reverse DNS domain name</constraintErrorMessage> + </properties> + <children> + <leafNode name="key-name"> + <properties> + <help>TSIG key name for reverse DNS updates</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> + </constraint> + <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, numbers, hyphen and underscore</constraintErrorMessage> + </properties> + </leafNode> + #include <include/dhcp/ddns-dns-server.xml.i> + </children> + </tagNode> + </children> +</node> +<node name="high-availability"> + <properties> + <help>DHCP high availability configuration</help> + </properties> + <children> + #include <include/source-address-ipv4.xml.i> + <leafNode name="mode"> + <properties> + <help>Configure high availability mode</help> + <completionHelp> + <list>active-active active-passive</list> + </completionHelp> + <valueHelp> + <format>active-active</format> + <description>Both server attend DHCP requests</description> + </valueHelp> + <valueHelp> + <format>active-passive</format> + <description>Only primary server attends DHCP requests</description> + </valueHelp> + <constraint> + <regex>(active-active|active-passive)</regex> + </constraint> + <constraintErrorMessage>Invalid DHCP high availability mode</constraintErrorMessage> + </properties> + <defaultValue>active-active</defaultValue> + </leafNode> + <leafNode name="remote"> + <properties> + <help>IPv4 remote address used for connection</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address of high availability peer</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="name"> + <properties> + <help>Peer name used to identify connection</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> + </constraint> + <constraintErrorMessage>Invalid failover peer name. May only contain letters, numbers and .-_</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="status"> + <properties> + <help>High availability 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 high availability peer status</constraintErrorMessage> + </properties> + </leafNode> + #include <include/pki/ca-certificate.xml.i> + #include <include/pki/certificate.xml.i> + </children> +</node> +<leafNode name="hostfile-update"> + <properties> + <help>Updating /etc/hosts file (per client lease)</help> + <valueless/> + </properties> +</leafNode> +#include <include/listen-address-ipv4.xml.i> +#include <include/listen-interface-multi-broadcast.xml.i> +<tagNode name="shared-network-name"> + <properties> + <help>Name of DHCP shared network</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> + </constraint> + <constraintErrorMessage>Invalid shared network name. May only contain letters, numbers and .-_</constraintErrorMessage> + </properties> + <children> + <node name="dynamic-dns-update"> + <properties> + <help>Dynamically update Domain Name System (RFC4702)</help> + </properties> + <children> + #include <include/dhcp/ddns-settings.xml.i> + </children> + </node> + <leafNode name="authoritative"> + <properties> + <help>Option to make DHCP server authoritative for this physical network</help> + <valueless/> + </properties> + </leafNode> + #include <include/dhcp/option-v4.xml.i> + #include <include/dhcp/ping-check.xml.i> + #include <include/generic-description.xml.i> + #include <include/generic-disable-node.xml.i> + <tagNode name="subnet"> + <properties> + <help>DHCP subnet for shared network</help> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + <constraintErrorMessage>Invalid IPv4 subnet definition</constraintErrorMessage> + </properties> + <children> + #include <include/dhcp/option-v4.xml.i> + #include <include/dhcp/ping-check.xml.i> + #include <include/generic-description.xml.i> + #include <include/generic-disable-node.xml.i> + <node name="dynamic-dns-update"> + <properties> + <help>Dynamically update Domain Name System (RFC4702)</help> + </properties> + <children> + #include <include/dhcp/ddns-settings.xml.i> + </children> + </node> + <leafNode name="exclude"> + <properties> + <help>IP address to exclude from DHCP lease range</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address to exclude from lease range</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + <multi/> + </properties> + </leafNode> + <leafNode name="ignore-client-id"> + <properties> + <help>Ignore client identifier for lease lookups</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="lease"> + <properties> + <help>Lease timeout in seconds</help> + <valueHelp> + <format>u32</format> + <description>DHCP lease time in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + <constraintErrorMessage>DHCP lease time must be between 0 and 4294967295 (49 days)</constraintErrorMessage> + </properties> + <defaultValue>86400</defaultValue> + </leafNode> + <tagNode name="range"> + <properties> + <help>DHCP lease range</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> + </constraint> + <constraintErrorMessage>Invalid range name, may only be alphanumeric, dot and hyphen</constraintErrorMessage> + </properties> + <children> + #include <include/dhcp/option-v4.xml.i> + <leafNode name="start"> + <properties> + <help>First IP address for DHCP lease range</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 start address of pool</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="stop"> + <properties> + <help>Last IP address for DHCP lease range</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 end address of pool</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <tagNode name="static-mapping"> + <properties> + <help>Hostname for static mapping reservation</help> + <constraint> + <validator name="fqdn"/> + </constraint> + <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> + </properties> + <children> + #include <include/dhcp/option-v4.xml.i> + #include <include/generic-description.xml.i> + #include <include/generic-disable-node.xml.i> + #include <include/ip-address.xml.i> + #include <include/interface/mac.xml.i> + #include <include/interface/duid.xml.i> + </children> + </tagNode> + <leafNode name="subnet-id"> + <properties> + <help>Unique ID mapped to leases in the lease file</help> + <valueHelp> + <format>u32</format> + <description>Unique subnet ID</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/dhcp/dhcpv6-server-common-config.xml.i b/interface-definitions/include/dhcp/dhcpv6-server-common-config.xml.i new file mode 100644 index 000000000..798ecac4b --- /dev/null +++ b/interface-definitions/include/dhcp/dhcpv6-server-common-config.xml.i @@ -0,0 +1,276 @@ +<!-- include start from dhcp/dhcpv6-server-common-config.xml.i --> +#include <include/generic-disable-node.xml.i> +#include <include/listen-interface-multi-broadcast.xml.i> +<leafNode name="disable-route-autoinstall"> + <properties> + <help>Do not install routes for delegated prefixes</help> + <valueless/> + </properties> +</leafNode> +<node name="global-parameters"> + <properties> + <help>Additional global parameters for DHCPv6 server</help> + </properties> + <children> + #include <include/name-server-ipv6.xml.i> + </children> +</node> +<leafNode name="preference"> + <properties> + <help>Preference of this DHCPv6 server compared with others</help> + <valueHelp> + <format>u32:0-255</format> + <description>DHCPv6 server preference (0-255)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + <constraintErrorMessage>Preference must be between 0 and 255</constraintErrorMessage> + </properties> +</leafNode> +<tagNode name="shared-network-name"> + <properties> + <help>DHCPv6 shared network name</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> + </constraint> + <constraintErrorMessage>Invalid DHCPv6 shared network name. May only contain letters, numbers and .-_</constraintErrorMessage> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/generic-description.xml.i> + #include <include/generic-interface.xml.i> + #include <include/dhcp/option-v6.xml.i> + <tagNode name="subnet"> + <properties> + <help>IPv6 DHCP subnet for this shared network</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + #include <include/dhcp/option-v6.xml.i> + #include <include/generic-interface.xml.i> + <tagNode name="range"> + <properties> + <help>Parameters setting ranges for assigning IPv6 addresses</help> + <constraint> + #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> + </constraint> + <constraintErrorMessage>Invalid range name, may only be alphanumeric, dot and hyphen</constraintErrorMessage> + </properties> + <children> + #include <include/dhcp/option-v6.xml.i> + <leafNode name="prefix"> + <properties> + <help>IPv6 prefix defining range of addresses to assign</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + <leafNode name="start"> + <properties> + <help>First in range of consecutive IPv6 addresses to assign</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="stop"> + <properties> + <help>Last in range of consecutive IPv6 addresses</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <node name="lease-time"> + <properties> + <help>Parameters relating to the lease time</help> + </properties> + <children> + <leafNode name="default"> + <properties> + <help>Default time (in seconds) that will be assigned to a lease</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>DHCPv6 valid lifetime</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="maximum"> + <properties> + <help>Maximum time (in seconds) that will be assigned to a lease</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Maximum lease time in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + <leafNode name="minimum"> + <properties> + <help>Minimum time (in seconds) that will be assigned to a lease</help> + <valueHelp> + <format>u32:1-4294967295</format> + <description>Minimum lease time in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="prefix-delegation"> + <properties> + <help>Parameters relating to IPv6 prefix delegation</help> + </properties> + <children> + <tagNode name="prefix"> + <properties> + <help>IPv6 prefix to be used in prefix delegation</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 prefix used in prefix delegation</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + <leafNode name="prefix-length"> + <properties> + <help>Length in bits of prefix</help> + <valueHelp> + <format>u32:32-64</format> + <description>Prefix length (32-64)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 32-64"/> + </constraint> + <constraintErrorMessage>Prefix length must be between 32 and 64</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="delegated-length"> + <properties> + <help>Length in bits of prefixes to be delegated</help> + <valueHelp> + <format>u32:32-64</format> + <description>Delegated prefix length (32-64)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 32-96"/> + </constraint> + <constraintErrorMessage>Delegated prefix length must be between 32 and 96</constraintErrorMessage> + </properties> + </leafNode> + <leafNode name="excluded-prefix"> + <properties> + <help>IPv6 prefix to be excluded from prefix delegation</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 prefix excluded from prefix delegation</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="excluded-prefix-length"> + <properties> + <help>Length in bits of excluded prefix</help> + <valueHelp> + <format>u32:33-64</format> + <description>Excluded prefix length (33-128)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 33-128"/> + </constraint> + <constraintErrorMessage>Prefix length must be between 33 and 128</constraintErrorMessage> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </node> + <tagNode name="static-mapping"> + <properties> + <help>Hostname for static mapping reservation</help> + <constraint> + <validator name="fqdn"/> + </constraint> + <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> + </properties> + <children> + #include <include/dhcp/option-v6.xml.i> + #include <include/generic-disable-node.xml.i> + #include <include/interface/mac.xml.i> + #include <include/interface/duid.xml.i> + <leafNode name="ipv6-address"> + <properties> + <help>Client IPv6 address for this static mapping</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address for this static mapping</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> + <leafNode name="ipv6-prefix"> + <properties> + <help>Client IPv6 prefix for this static mapping</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix for this static mapping</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <leafNode name="subnet-id"> + <properties> + <help>Unique ID mapped to leases in the lease file</help> + <valueHelp> + <format>u32</format> + <description>Unique subnet ID</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967295"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in index 78f1cea4e..4d1172e03 100644 --- a/interface-definitions/service_dhcp-server.xml.in +++ b/interface-definitions/service_dhcp-server.xml.in @@ -9,346 +9,7 @@ <priority>911</priority> </properties> <children> - #include <include/generic-disable-node.xml.i> - <node name="dynamic-dns-update"> - <properties> - <help>Dynamically update Domain Name System (RFC4702)</help> - </properties> - <children> - #include <include/dhcp/ddns-settings.xml.i> - <tagNode name="tsig-key"> - <properties> - <help>TSIG key definition for DNS updates</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> - </constraint> - <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, hyphen and underscore</constraintErrorMessage> - </properties> - <children> - <leafNode name="algorithm"> - <properties> - <help>TSIG key algorithm</help> - <completionHelp> - <list>md5 sha1 sha224 sha256 sha384 sha512</list> - </completionHelp> - <valueHelp> - <format>md5</format> - <description>MD5 HMAC algorithm</description> - </valueHelp> - <valueHelp> - <format>sha1</format> - <description>SHA1 HMAC algorithm</description> - </valueHelp> - <valueHelp> - <format>sha224</format> - <description>SHA224 HMAC algorithm</description> - </valueHelp> - <valueHelp> - <format>sha256</format> - <description>SHA256 HMAC algorithm</description> - </valueHelp> - <valueHelp> - <format>sha384</format> - <description>SHA384 HMAC algorithm</description> - </valueHelp> - <valueHelp> - <format>sha512</format> - <description>SHA512 HMAC algorithm</description> - </valueHelp> - <constraint> - <regex>(md5|sha1|sha224|sha256|sha384|sha512)</regex> - </constraint> - <constraintErrorMessage>Invalid TSIG key algorithm</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="secret"> - <properties> - <help>TSIG key secret (base64-encoded)</help> - <constraint> - <validator name="base64"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <tagNode name="forward-domain"> - <properties> - <help>Forward DNS domain name</help> - <constraint> - <validator name="fqdn"/> - </constraint> - <constraintErrorMessage>Invalid forward DNS domain name</constraintErrorMessage> - </properties> - <children> - <leafNode name="key-name"> - <properties> - <help>TSIG key name for forward DNS updates</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> - </constraint> - <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, numbers, hyphen and underscore</constraintErrorMessage> - </properties> - </leafNode> - #include <include/dhcp/ddns-dns-server.xml.i> - </children> - </tagNode> - <tagNode name="reverse-domain"> - <properties> - <help>Reverse DNS domain name</help> - <constraint> - <validator name="fqdn"/> - </constraint> - <constraintErrorMessage>Invalid reverse DNS domain name</constraintErrorMessage> - </properties> - <children> - <leafNode name="key-name"> - <properties> - <help>TSIG key name for reverse DNS updates</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> - </constraint> - <constraintErrorMessage>Invalid TSIG key name. May only contain letters, numbers, numbers, hyphen and underscore</constraintErrorMessage> - </properties> - </leafNode> - #include <include/dhcp/ddns-dns-server.xml.i> - </children> - </tagNode> - </children> - </node> - <node name="high-availability"> - <properties> - <help>DHCP high availability configuration</help> - </properties> - <children> - #include <include/source-address-ipv4.xml.i> - <leafNode name="mode"> - <properties> - <help>Configure high availability mode</help> - <completionHelp> - <list>active-active active-passive</list> - </completionHelp> - <valueHelp> - <format>active-active</format> - <description>Both server attend DHCP requests</description> - </valueHelp> - <valueHelp> - <format>active-passive</format> - <description>Only primary server attends DHCP requests</description> - </valueHelp> - <constraint> - <regex>(active-active|active-passive)</regex> - </constraint> - <constraintErrorMessage>Invalid DHCP high availability mode</constraintErrorMessage> - </properties> - <defaultValue>active-active</defaultValue> - </leafNode> - <leafNode name="remote"> - <properties> - <help>IPv4 remote address used for connection</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address of high availability peer</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="name"> - <properties> - <help>Peer name used to identify connection</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> - </constraint> - <constraintErrorMessage>Invalid failover peer name. May only contain letters, numbers and .-_</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="status"> - <properties> - <help>High availability 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 high availability peer status</constraintErrorMessage> - </properties> - </leafNode> - #include <include/pki/ca-certificate.xml.i> - #include <include/pki/certificate.xml.i> - </children> - </node> - <leafNode name="hostfile-update"> - <properties> - <help>Updating /etc/hosts file (per client lease)</help> - <valueless/> - </properties> - </leafNode> - #include <include/listen-address-ipv4.xml.i> - #include <include/listen-interface-multi-broadcast.xml.i> - <tagNode name="shared-network-name"> - <properties> - <help>Name of DHCP shared network</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> - </constraint> - <constraintErrorMessage>Invalid shared network name. May only contain letters, numbers and .-_</constraintErrorMessage> - </properties> - <children> - <node name="dynamic-dns-update"> - <properties> - <help>Dynamically update Domain Name System (RFC4702)</help> - </properties> - <children> - #include <include/dhcp/ddns-settings.xml.i> - </children> - </node> - <leafNode name="authoritative"> - <properties> - <help>Option to make DHCP server authoritative for this physical network</help> - <valueless/> - </properties> - </leafNode> - #include <include/dhcp/option-v4.xml.i> - #include <include/dhcp/ping-check.xml.i> - #include <include/generic-description.xml.i> - #include <include/generic-disable-node.xml.i> - <tagNode name="subnet"> - <properties> - <help>DHCP subnet for shared network</help> - <valueHelp> - <format>ipv4net</format> - <description>IPv4 address and prefix length</description> - </valueHelp> - <constraint> - <validator name="ipv4-prefix"/> - </constraint> - <constraintErrorMessage>Invalid IPv4 subnet definition</constraintErrorMessage> - </properties> - <children> - #include <include/dhcp/option-v4.xml.i> - #include <include/dhcp/ping-check.xml.i> - #include <include/generic-description.xml.i> - #include <include/generic-disable-node.xml.i> - <node name="dynamic-dns-update"> - <properties> - <help>Dynamically update Domain Name System (RFC4702)</help> - </properties> - <children> - #include <include/dhcp/ddns-settings.xml.i> - </children> - </node> - <leafNode name="exclude"> - <properties> - <help>IP address to exclude from DHCP lease range</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 address to exclude from lease range</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - <multi/> - </properties> - </leafNode> - <leafNode name="ignore-client-id"> - <properties> - <help>Ignore client identifier for lease lookups</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="lease"> - <properties> - <help>Lease timeout in seconds</help> - <valueHelp> - <format>u32</format> - <description>DHCP lease time in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - <constraintErrorMessage>DHCP lease time must be between 0 and 4294967295 (49 days)</constraintErrorMessage> - </properties> - <defaultValue>86400</defaultValue> - </leafNode> - <tagNode name="range"> - <properties> - <help>DHCP lease range</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> - </constraint> - <constraintErrorMessage>Invalid range name, may only be alphanumeric, dot and hyphen</constraintErrorMessage> - </properties> - <children> - #include <include/dhcp/option-v4.xml.i> - <leafNode name="start"> - <properties> - <help>First IP address for DHCP lease range</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 start address of pool</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="stop"> - <properties> - <help>Last IP address for DHCP lease range</help> - <valueHelp> - <format>ipv4</format> - <description>IPv4 end address of pool</description> - </valueHelp> - <constraint> - <validator name="ipv4-address"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <tagNode name="static-mapping"> - <properties> - <help>Hostname for static mapping reservation</help> - <constraint> - <validator name="fqdn"/> - </constraint> - <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> - </properties> - <children> - #include <include/dhcp/option-v4.xml.i> - #include <include/generic-description.xml.i> - #include <include/generic-disable-node.xml.i> - #include <include/ip-address.xml.i> - #include <include/interface/mac.xml.i> - #include <include/interface/duid.xml.i> - </children> - </tagNode> - <leafNode name="subnet-id"> - <properties> - <help>Unique ID mapped to leases in the lease file</help> - <valueHelp> - <format>u32</format> - <description>Unique subnet ID</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </tagNode> + #include <include/dhcp/dhcp-server-common-config.xml.i> </children> </node> </children> diff --git a/interface-definitions/service_dhcpv6-server.xml.in b/interface-definitions/service_dhcpv6-server.xml.in index a6763a345..d2b96061d 100644 --- a/interface-definitions/service_dhcpv6-server.xml.in +++ b/interface-definitions/service_dhcpv6-server.xml.in @@ -8,280 +8,7 @@ <priority>900</priority> </properties> <children> - #include <include/generic-disable-node.xml.i> - #include <include/listen-interface-multi-broadcast.xml.i> - <leafNode name="disable-route-autoinstall"> - <properties> - <help>Do not install routes for delegated prefixes</help> - <valueless/> - </properties> - </leafNode> - <node name="global-parameters"> - <properties> - <help>Additional global parameters for DHCPv6 server</help> - </properties> - <children> - #include <include/name-server-ipv6.xml.i> - </children> - </node> - <leafNode name="preference"> - <properties> - <help>Preference of this DHCPv6 server compared with others</help> - <valueHelp> - <format>u32:0-255</format> - <description>DHCPv6 server preference (0-255)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-255"/> - </constraint> - <constraintErrorMessage>Preference must be between 0 and 255</constraintErrorMessage> - </properties> - </leafNode> - <tagNode name="shared-network-name"> - <properties> - <help>DHCPv6 shared network name</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> - </constraint> - <constraintErrorMessage>Invalid DHCPv6 shared network name. May only contain letters, numbers and .-_</constraintErrorMessage> - </properties> - <children> - #include <include/generic-disable-node.xml.i> - #include <include/generic-description.xml.i> - #include <include/generic-interface.xml.i> - #include <include/dhcp/option-v6.xml.i> - <tagNode name="subnet"> - <properties> - <help>IPv6 DHCP subnet for this shared network</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 address and prefix length</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - <children> - #include <include/dhcp/option-v6.xml.i> - #include <include/generic-interface.xml.i> - <tagNode name="range"> - <properties> - <help>Parameters setting ranges for assigning IPv6 addresses</help> - <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i> - </constraint> - <constraintErrorMessage>Invalid range name, may only be alphanumeric, dot and hyphen</constraintErrorMessage> - </properties> - <children> - #include <include/dhcp/option-v6.xml.i> - <leafNode name="prefix"> - <properties> - <help>IPv6 prefix defining range of addresses to assign</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 address and prefix length</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - <leafNode name="start"> - <properties> - <help>First in range of consecutive IPv6 addresses to assign</help> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address</description> - </valueHelp> - <constraint> - <validator name="ipv6-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="stop"> - <properties> - <help>Last in range of consecutive IPv6 addresses</help> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address</description> - </valueHelp> - <constraint> - <validator name="ipv6-address"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <node name="lease-time"> - <properties> - <help>Parameters relating to the lease time</help> - </properties> - <children> - <leafNode name="default"> - <properties> - <help>Default time (in seconds) that will be assigned to a lease</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>DHCPv6 valid lifetime</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="maximum"> - <properties> - <help>Maximum time (in seconds) that will be assigned to a lease</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Maximum lease time in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="minimum"> - <properties> - <help>Minimum time (in seconds) that will be assigned to a lease</help> - <valueHelp> - <format>u32:1-4294967295</format> - <description>Minimum lease time in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="prefix-delegation"> - <properties> - <help>Parameters relating to IPv6 prefix delegation</help> - </properties> - <children> - <tagNode name="prefix"> - <properties> - <help>IPv6 prefix to be used in prefix delegation</help> - <valueHelp> - <format>ipv6</format> - <description>IPv6 prefix used in prefix delegation</description> - </valueHelp> - <constraint> - <validator name="ipv6-address"/> - </constraint> - </properties> - <children> - <leafNode name="prefix-length"> - <properties> - <help>Length in bits of prefix</help> - <valueHelp> - <format>u32:32-64</format> - <description>Prefix length (32-64)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 32-64"/> - </constraint> - <constraintErrorMessage>Prefix length must be between 32 and 64</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="delegated-length"> - <properties> - <help>Length in bits of prefixes to be delegated</help> - <valueHelp> - <format>u32:32-64</format> - <description>Delegated prefix length (32-64)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 32-96"/> - </constraint> - <constraintErrorMessage>Delegated prefix length must be between 32 and 96</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="excluded-prefix"> - <properties> - <help>IPv6 prefix to be excluded from prefix delegation</help> - <valueHelp> - <format>ipv6</format> - <description>IPv6 prefix excluded from prefix delegation</description> - </valueHelp> - <constraint> - <validator name="ipv6-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="excluded-prefix-length"> - <properties> - <help>Length in bits of excluded prefix</help> - <valueHelp> - <format>u32:33-64</format> - <description>Excluded prefix length (33-128)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 33-128"/> - </constraint> - <constraintErrorMessage>Prefix length must be between 33 and 128</constraintErrorMessage> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </node> - <tagNode name="static-mapping"> - <properties> - <help>Hostname for static mapping reservation</help> - <constraint> - <validator name="fqdn"/> - </constraint> - <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> - </properties> - <children> - #include <include/dhcp/option-v6.xml.i> - #include <include/generic-disable-node.xml.i> - #include <include/interface/mac.xml.i> - #include <include/interface/duid.xml.i> - <leafNode name="ipv6-address"> - <properties> - <help>Client IPv6 address for this static mapping</help> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address for this static mapping</description> - </valueHelp> - <constraint> - <validator name="ipv6-address"/> - </constraint> - </properties> - </leafNode> - <leafNode name="ipv6-prefix"> - <properties> - <help>Client IPv6 prefix for this static mapping</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 prefix for this static mapping</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - <leafNode name="subnet-id"> - <properties> - <help>Unique ID mapped to leases in the lease file</help> - <valueHelp> - <format>u32</format> - <description>Unique subnet ID</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-4294967295"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </tagNode> + #include <include/dhcp/dhcpv6-server-common-config.xml.i> </children> </node> </children> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 03128cb99..3fa95076e 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -116,6 +116,31 @@ </node> </children> </node> + <node name="service"> + <properties> + <help>Enable services in the vrf itself</help> + </properties> + <children> + <node name="dhcp-server" owner="${vyos_conf_scripts_dir}/service_dhcp-server.py $VAR(../../@)"> + <properties> + <help>Dynamic Host Configuration Protocol (DHCP) for DHCP server</help> + <priority>912</priority> + </properties> + <children> + #include <include/dhcp/dhcp-server-common-config.xml.i> + </children> + </node> + <node name="dhcpv6-server" owner="${vyos_conf_scripts_dir}/service_dhcpv6-server.py $VAR(../../@)"> + <properties> + <help>DHCP for IPv6 (DHCPv6) server</help> + <priority>901</priority> + </properties> + <children> + #include <include/dhcp/dhcpv6-server-common-config.xml.i> + </children> + </node> + </children> + </node> <leafNode name="table"> <properties> <help>Routing table associated with this instance</help> diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 2da3bb5dc..da14359f2 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -13,6 +13,22 @@ </properties> <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet --address $4</command> </tagNode> + <tagNode name="vrf"> + <properties> + <help>Clear DHCP server lease for vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCP server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet --vrf $4 --address $6</command> + </tagNode> + </children> + </tagNode> </children> </node> <node name="dhcpv6-server"> @@ -26,6 +42,22 @@ </properties> <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet6 --address $4</command> </tagNode> + <tagNode name="vrf"> + <properties> + <help>Clear DHCPv6 server lease for vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCPv6 server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet6 --vrf $4 --address $6</command> + </tagNode> + </children> + </tagNode> </children> </node> </children> @@ -70,7 +102,7 @@ <properties> <help>Show DHCP server leases</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf ''</command> <children> <tagNode name="origin"> <properties> @@ -79,7 +111,7 @@ <list>local remote</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --origin $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf '' --origin $6</command> </tagNode> <tagNode name="pool"> <properties> @@ -88,7 +120,7 @@ <path>service dhcp-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf '' --pool $6</command> </tagNode> <tagNode name="sort"> <properties> @@ -97,7 +129,7 @@ <list>end hostname ip mac pool remaining start state</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --sort $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf '' --sort $6</command> </tagNode> <tagNode name="state"> <properties> @@ -106,7 +138,7 @@ <list>abandoned active all backup expired free released reset</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --state $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf '' --state $6</command> </tagNode> </children> </node> @@ -114,7 +146,7 @@ <properties> <help>Show DHCP server static mappings</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf ''</command> <children> <tagNode name="pool"> <properties> @@ -123,7 +155,7 @@ <path>service dhcp-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf '' --pool $6</command> </tagNode> <tagNode name="sort"> <properties> @@ -132,7 +164,7 @@ <list>ip mac duid pool</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --sort $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf '' --sort $6</command> </tagNode> </children> </node> @@ -140,7 +172,7 @@ <properties> <help>Show DHCP server statistics</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet --vrf ''</command> <children> <tagNode name="pool"> <properties> @@ -149,10 +181,107 @@ <path>service dhcp-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet --vrf '' --pool $6</command> </tagNode> </children> </node> + <tagNode name="vrf"> + <properties> + <help>Show DHCP (Dynamic Host Configuration Protocol) information for vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <children> + <node name="leases"> + <properties> + <help>Show DHCP server leases</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf $5</command> + <children> + <tagNode name="origin"> + <properties> + <help>Show DHCP server leases granted by local or remote DHCP server</help> + <completionHelp> + <list>local remote</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf $5 --origin $8</command> + </tagNode> + <tagNode name="pool"> + <properties> + <help>Show DHCP server leases for a specific pool</help> + <completionHelp> + <path>service dhcp-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf $5 --pool $8</command> + </tagNode> + <tagNode name="sort"> + <properties> + <help>Show DHCP server leases sorted by the specified key</help> + <completionHelp> + <list>end hostname ip mac pool remaining start state</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf $5 --sort $8</command> + </tagNode> + <tagNode name="state"> + <properties> + <help>Show DHCP server leases with a specific state (can be multiple, comma-separated)</help> + <completionHelp> + <list>abandoned active all backup expired free released reset</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet --vrf $5 --state $8</command> + </tagNode> + </children> + </node> + <node name="static-mappings"> + <properties> + <help>Show DHCP server static mappings</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf $5</command> + <children> + <tagNode name="pool"> + <properties> + <help>Show DHCP server static mappings for a specific pool</help> + <completionHelp> + <path>service dhcp-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf $5 --pool $8</command> + </tagNode> + <tagNode name="sort"> + <properties> + <help>Show DHCP server static mappings sorted by the specified key</help> + <completionHelp> + <list>ip mac duid pool</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet --vrf $5 --sort $8</command> + </tagNode> + </children> + </node> + <node name="statistics"> + <properties> + <help>Show DHCP server statistics</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet --vrf $5</command> + <children> + <tagNode name="pool"> + <properties> + <help>Show DHCP server statistics for a specific pool</help> + <completionHelp> + <path>service dhcp-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet --vrf $5 --pool $8</command> + </tagNode> + </children> + </node> + </children> + </tagNode> </children> </node> </children> @@ -171,7 +300,7 @@ <properties> <help>Show DHCPv6 server leases</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf ''</command> <children> <tagNode name="pool"> <properties> @@ -180,7 +309,7 @@ <path>service dhcpv6-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf '' --pool $6</command> </tagNode> <tagNode name="sort"> <properties> @@ -189,7 +318,7 @@ <list>end duid ip last_communication pool remaining state type</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --sort $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf '' --sort $6</command> </tagNode> <tagNode name="state"> <properties> @@ -198,7 +327,7 @@ <list>abandoned active all backup expired free released reset</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --state $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf '' --state $6</command> </tagNode> </children> </node> @@ -206,7 +335,7 @@ <properties> <help>Show DHCPv6 server static mappings</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf ''</command> <children> <tagNode name="pool"> <properties> @@ -215,7 +344,7 @@ <path>service dhcp-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf '' --pool $6</command> </tagNode> <tagNode name="sort"> <properties> @@ -224,7 +353,7 @@ <list>ip mac duid pool</list> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --sort $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf '' --sort $6</command> </tagNode> </children> </node> @@ -232,7 +361,7 @@ <properties> <help>Show DHCPv6 server statistics</help> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6 --vrf ''</command> <children> <tagNode name="pool"> <properties> @@ -241,10 +370,98 @@ <path>service dhcpv6-server shared-network-name</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6 --pool $6</command> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6 --vrf '' --pool $6</command> </tagNode> </children> </node> + <tagNode name="vrf"> + <properties> + <help>Show DHCPv6 (IPv6 Dynamic Host Configuration Protocol) information for vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <children> + <node name="leases"> + <properties> + <help>Show DHCPv6 server leases</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf $5</command> + <children> + <tagNode name="pool"> + <properties> + <help>Show DHCPv6 server leases for a specific pool</help> + <completionHelp> + <path>service dhcpv6-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf $5 --pool $8</command> + </tagNode> + <tagNode name="sort"> + <properties> + <help>Show DHCPv6 server leases sorted by the specified key</help> + <completionHelp> + <list>end duid ip last_communication pool remaining state type</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf $5 --sort $8</command> + </tagNode> + <tagNode name="state"> + <properties> + <help>Show DHCPv6 server leases with a specific state (can be multiple, comma-separated)</help> + <completionHelp> + <list>abandoned active all backup expired free released reset</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --vrf $5 --state $8</command> + </tagNode> + </children> + </node> + <node name="static-mappings"> + <properties> + <help>Show DHCPv6 server static mappings</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf $5</command> + <children> + <tagNode name="pool"> + <properties> + <help>Show DHCPv6 server static mappings for a specific pool</help> + <completionHelp> + <path>service dhcp-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf $5 --pool $8</command> + </tagNode> + <tagNode name="sort"> + <properties> + <help>Show DHCPv6 server static mappings sorted by the specified key</help> + <completionHelp> + <list>ip mac duid pool</list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_static_mappings --family inet6 --vrf $5 --sort $8</command> + </tagNode> + </children> + </node> + <node name="statistics"> + <properties> + <help>Show DHCPv6 server statistics</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6 --vrf $5</command> + <children> + <tagNode name="pool"> + <properties> + <help>Show DHCPv6 server statistics for a specific pool</help> + <completionHelp> + <path>service dhcpv6-server shared-network-name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py show_server_pool_statistics --family inet6 --vrf $5 --pool $8</command> + </tagNode> + </children> + </node> + </children> + </tagNode> </children> </node> </children> @@ -263,6 +480,17 @@ <help>Restart DHCP server</help> </properties> <command>${vyos_op_scripts_dir}/restart.py restart_service --name dhcp</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Restart DHCP server on vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/restart.py restart_service --name dhcp --vrf $5</command> + </tagNode> + </children> </node> <node name="relay-agent"> <properties> @@ -282,6 +510,17 @@ <help>Restart DHCPv6 server</help> </properties> <command>${vyos_op_scripts_dir}/restart.py restart_service --name dhcpv6</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Restart DHCPv6 server on vrf</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/restart.py restart_service --name dhcpv6 --vrf $5</command> + </tagNode> + </children> </node> <node name="relay-agent"> <properties> diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in index 721460be5..86c07fb02 100644 --- a/op-mode-definitions/monitor-log.xml.in +++ b/op-mode-definitions/monitor-log.xml.in @@ -45,6 +45,14 @@ <help>Monitor last lines of DHCP server log</help> </properties> <command>journalctl --no-hostname --follow --boot --unit kea-dhcp4-server.service</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Monitor last lines of DHCP server log on specific vrf</help> + </properties> + <command>journalctl --no-hostname --follow --boot --unit "kea-dhcp4-server@$6.service"</command> + </tagNode> + </children> </node> <node name="client"> <properties> @@ -75,6 +83,14 @@ <help>Monitor last lines of DHCPv6 server log</help> </properties> <command>journalctl --no-hostname --follow --boot --unit kea-dhcp6-server.service</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Monitor last lines of DHCPv6 server log on specific vrf</help> + </properties> + <command>journalctl --no-hostname --follow --boot --unit "kea-dhcp6-server@$6.service"</command> + </tagNode> + </children> </node> <node name="client"> <properties> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 499e7f84b..b301f3401 100755 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -105,6 +105,14 @@ <help>Show log for DHCP server</help> </properties> <command>journalctl --no-hostname --boot --unit kea-dhcp4-server.service</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Monitor last lines of DHCP server log on specific vrf</help> + </properties> + <command>journalctl --no-hostname --follow --boot --unit "kea-dhcp4-server@$6.service"</command> + </tagNode> + </children> </node> <node name="client"> <properties> @@ -135,6 +143,14 @@ <help>Show log for DHCPv6 server</help> </properties> <command>journalctl --no-hostname --boot --unit kea-dhcp6-server.service</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Monitor last lines of DHCPv6 server log on specific vrf</help> + </properties> + <command>journalctl --no-hostname --boot --unit "kea-dhcp6-server@$6.service"</command> + </tagNode> + </children> </node> <node name="client"> <properties> diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 5eecbbaad..4a8a8b4c8 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -60,7 +60,7 @@ kea6_options = { 'capwap_controller': 'capwap-ac-v6', } -kea_ctrl_socket = '/run/kea/dhcp{inet}-ctrl-socket' +kea_ctrl_socket = '/run/kea/dhcp{inet}{vrf_append}-ctrl-socket' def _format_hex_string(in_str): @@ -403,8 +403,13 @@ def kea_parse_ddns_settings(config): return data -def _ctrl_socket_command(inet, command, args=None): - path = kea_ctrl_socket.format(inet=inet) +def _ctrl_socket_command(inet, vrf_name, command, args=None): + if vrf_name: + vrf_append = f'-{vrf_name}' + else: + vrf_append = '' + + path = kea_ctrl_socket.format(inet=inet, vrf_append=vrf_append) if not os.path.exists(path): return None @@ -430,8 +435,8 @@ def _ctrl_socket_command(inet, command, args=None): return json.loads(result.decode('utf-8')) -def kea_get_leases(inet): - leases = _ctrl_socket_command(inet, f'lease{inet}-get-all') +def kea_get_leases(inet, vrf_name): + leases = _ctrl_socket_command(inet, vrf_name, f'lease{inet}-get-all') if not leases or 'result' not in leases or leases['result'] != 0: return [] @@ -441,6 +446,7 @@ def kea_get_leases(inet): def kea_add_lease( inet, + vrf_name, ip_address, host_name=None, mac_address=None, @@ -466,7 +472,7 @@ def kea_add_lease( if inet == '6' and iaid: args['iaid'] = iaid - result = _ctrl_socket_command(inet, f'lease{inet}-add', args) + result = _ctrl_socket_command(inet, vrf_name, f'lease{inet}-add', args) if result and 'result' in result: return result['result'] == 0 @@ -474,10 +480,10 @@ def kea_add_lease( return False -def kea_delete_lease(inet, ip_address): +def kea_delete_lease(inet, ip_address, vrf_name=''): args = {'ip-address': ip_address} - result = _ctrl_socket_command(inet, f'lease{inet}-del', args) + result = _ctrl_socket_command(inet, vrf_name, f'lease{inet}-del', args) if result and 'result' in result: return result['result'] == 0 @@ -485,8 +491,8 @@ def kea_delete_lease(inet, ip_address): return False -def kea_get_active_config(inet): - config = _ctrl_socket_command(inet, 'config-get') +def kea_get_active_config(inet, vrf_name): + config = _ctrl_socket_command(inet, vrf_name, 'config-get') if not config or 'result' not in config or config['result'] != 0: return None @@ -580,12 +586,12 @@ def kea_get_static_mappings(config, inet, pools=[]) -> list: return mappings -def kea_get_server_leases(config, inet, pools=[], state=[], origin=None) -> list: +def kea_get_server_leases(config, inet, vrf_name, pools=[], state=[], origin=None) -> list: """ Get DHCP server leases from active Kea DHCPv4 or DHCPv6 configuration :return list """ - leases = kea_get_leases(inet) + leases = kea_get_leases(inet, vrf_name) data = [] for lease in leases: diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index e421f04d2..d275ad2ea 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -1415,7 +1415,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): client = f'client{seq}' mac = f'00:50:00:00:00:{seq:02}' ip = inc_ip(subnet, seq) - kea_add_lease(4, ip, host_name=client, mac_address=mac) + kea_add_lease(4, '', ip, host_name=client, mac_address=mac) # 2. Verify that leases are not available in vyos-hostsd tag_regex = re.escape(f'dhcp-server-{subnet.rsplit(".", 1)[0]}') diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 30980f9ec..bba75f98d 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -34,6 +34,8 @@ from vyos.utils.network import is_intf_addr_assigned from vyos.utils.network import interface_exists from vyos.utils.process import cmd from vyos.utils.system import sysctl_read +from vyos.template import inc_ip +from vyos.utils.process import process_named_running base_path = ['vrf'] vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] @@ -72,6 +74,42 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): for vrf in vrfs: self.assertFalse(interface_exists(vrf)) + def walk_path(self, obj, path): + current = obj + + for i, key in enumerate(path): + if isinstance(key, str): + self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}') + self.assertTrue(key in current, msg=f'Failed path: {path}') + elif isinstance(key, int): + self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}') + self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}') + else: + assert False, 'Invalid type' + + current = current[key] + + return current + + def verify_config_object(self, obj, path, value): + base_obj = self.walk_path(obj, path) + self.assertTrue(isinstance(base_obj, list)) + self.assertTrue(any(True for v in base_obj if v == value)) + + def verify_config_value(self, obj, path, key, value): + base_obj = self.walk_path(obj, path) + if isinstance(base_obj, list): + self.assertTrue(any(True for v in base_obj if key in v and v[key] == value)) + elif isinstance(base_obj, dict): + self.assertTrue(key in base_obj) + self.assertEqual(base_obj[key], value) + + def verify_kea_service_running(self, process_name): + tmp = cmd('tail -n 100 /var/log/messages') + self.assertTrue( + process_named_running(process_name), msg=f'Service not running, log: {tmp}' + ) + def test_vrf_vni_and_table_id(self): base_table = '1000' table = base_table @@ -601,5 +639,360 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_delete(['nat']) + def test_dhcp_single_pool(self): + # Prepare the vrf and options + table = '100' + vrf = 'dhcp_smoke' + interface = 'dum8888' + subnet = '192.0.2.0/25' + router = inc_ip(subnet, 1) + dns_1 = inc_ip(subnet, 2) + dns_2 = inc_ip(subnet, 3) + domain_name = 'vyos.net' + + # declare fiels + process_name = 'kea-dhcp4' + kea4_conf = f'/run/kea/kea-{vrf}-dhcp4.conf' + + # create interface + cidr_mask = subnet.split('/')[-1] + self.cli_set( + ['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}'] + ) + self.cli_set(['interfaces', 'dummy', interface, 'vrf', f'{vrf}']) + + # create the vrf with table + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + # set the dhcp scope + base = base_path + ['name', vrf, 'service', 'dhcp-server'] + shared_net_name = 'SMOKE-1' + + range_0_start = inc_ip(subnet, 10) + range_0_stop = inc_ip(subnet, 20) + range_1_start = inc_ip(subnet, 40) + range_1_stop = inc_ip(subnet, 50) + + self.cli_set(base + ['listen-interface', interface]) + + self.cli_set(base + ['shared-network-name', shared_net_name, 'ping-check']) + + pool = base + ['shared-network-name', shared_net_name, 'subnet', subnet] + self.cli_set(pool + ['subnet-id', '1']) + self.cli_set(pool + ['ignore-client-id']) + self.cli_set(pool + ['ping-check']) + # we use the first subnet IP address as default gateway + self.cli_set(pool + ['option', 'default-router', router]) + self.cli_set(pool + ['option', 'name-server', dns_1]) + self.cli_set(pool + ['option', 'name-server', dns_2]) + self.cli_set(pool + ['option', 'domain-name', domain_name]) + + # check validate() - No DHCP address range or active static-mapping set + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) + self.cli_set(pool + ['range', '1', 'start', range_1_start]) + self.cli_set(pool + ['range', '1', 'stop', range_1_stop]) + + # commit changes + self.cli_commit() + + 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'], 'id', 1 + ) + self.verify_config_value( + obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'match-client-id', False + ) + self.verify_config_value( + obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400 + ) + self.verify_config_value( + obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400 + ) + + # Verify ping-check + self.verify_config_value( + obj, + ['Dhcp4', 'shared-networks', 0, 'user-context'], + 'enable-ping-check', + True, + ) + + self.verify_config_value( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'user-context'], + 'enable-ping-check', + True, + ) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name', 'data': domain_name}, + ) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'}, + ) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}, + ) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}, + ) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_1_start} - {range_1_stop}'}, + ) + + # Check for running process + self.verify_kea_service_running(process_name) + + # perform cleanup + self.cli_delete(['interfaces', 'dummy', interface, 'address']) + self.cli_delete(['interfaces', 'dummy', interface, 'vrf']) + self.cli_delete(base) + self.cli_commit() + + def test_dhcpv6_single_pool(self): + # Prepare the vrf and other options + table = '100' + vrf = 'dhcp_smoke' + subnet = '2001:db8:f00::/64' + dns_1 = '2001:db8::1' + dns_2 = '2001:db8::2' + domain = 'vyos.net' + nis_servers = ['2001:db8:ffff::1', '2001:db8:ffff::2'] + interface = 'dum8888' + interface_addr = inc_ip(subnet, 1) + '/64' + + # declare fiels + process_name = 'kea-dhcp6' + kea6_conf = f'/run/kea/kea-{vrf}-dhcp6.conf' + + # create interface + self.cli_set(['interfaces', 'dummy', interface, 'address', f'{interface_addr}']) + self.cli_set(['interfaces', 'dummy', interface, 'vrf', f'{vrf}']) + + # create the vrf with table + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + # set the dhcp scope + base = base_path + ['name', vrf, 'service', 'dhcpv6-server'] + + shared_net_name = 'SMOKE-1' + search_domains = ['foo.vyos.net', 'bar.vyos.net'] + lease_time = '1200' + max_lease_time = '72000' + min_lease_time = '600' + preference = '10' + sip_server = 'sip.vyos.net' + sntp_server = inc_ip(subnet, 100) + range_start = inc_ip(subnet, 256) # ::100 + range_stop = inc_ip(subnet, 65535) # ::ffff + + pool = base + ['shared-network-name', shared_net_name, 'subnet', subnet] + + self.cli_set(base + ['preference', preference]) + self.cli_set(pool + ['interface', interface]) + self.cli_set(pool + ['subnet-id', '1']) + # we use the first subnet IP address as default gateway + self.cli_set(pool + ['lease-time', 'default', lease_time]) + self.cli_set(pool + ['lease-time', 'maximum', max_lease_time]) + self.cli_set(pool + ['lease-time', 'minimum', min_lease_time]) + self.cli_set(pool + ['option', 'capwap-controller', dns_1]) + self.cli_set(pool + ['option', 'name-server', dns_1]) + self.cli_set(pool + ['option', 'name-server', dns_2]) + self.cli_set(pool + ['option', 'name-server', dns_2]) + self.cli_set(pool + ['option', 'nis-domain', domain]) + self.cli_set(pool + ['option', 'nisplus-domain', domain]) + self.cli_set(pool + ['option', 'sip-server', sip_server]) + self.cli_set(pool + ['option', 'sntp-server', sntp_server]) + self.cli_set(pool + ['range', '1', 'start', range_start]) + self.cli_set(pool + ['range', '1', 'stop', range_stop]) + + for server in nis_servers: + self.cli_set(pool + ['option', 'nis-server', server]) + self.cli_set(pool + ['option', 'nisplus-server', server]) + + for search_domain in search_domains: + self.cli_set(pool + ['option', 'domain-search', search_domain]) + + client_base = 1 + for client in ['client1', 'client2', 'client3']: + duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}' + self.cli_set(pool + ['static-mapping', client, 'duid', duid]) + self.cli_set( + pool + + [ + 'static-mapping', + client, + 'ipv6-address', + inc_ip(subnet, client_base), + ] + ) + self.cli_set( + pool + + [ + 'static-mapping', + client, + 'ipv6-prefix', + inc_ip(subnet, client_base << 64) + '/64', + ] + ) + client_base += 1 + + # cannot have both mac-address and duid set + with self.assertRaises(ConfigSessionError): + self.cli_set( + pool + ['static-mapping', 'client1', 'mac', '00:50:00:00:00:11'] + ) + self.cli_commit() + self.cli_delete(pool + ['static-mapping', 'client1', 'mac']) + + # commit changes + self.cli_commit() + + config = read_file(kea6_conf) + obj = loads(config) + + self.verify_config_value( + obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name + ) + self.verify_config_value( + obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet + ) + self.verify_config_value( + obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'interface', interface + ) + self.verify_config_value( + obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'id', 1 + ) + self.verify_config_value( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6'], + 'valid-lifetime', + int(lease_time), + ) + self.verify_config_value( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6'], + 'min-valid-lifetime', + int(min_lease_time), + ) + self.verify_config_value( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6'], + 'max-valid-lifetime', + int(max_lease_time), + ) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'capwap-ac-v6', 'data': dns_1}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'dns-servers', 'data': f'{dns_1}, {dns_2}'}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'domain-search', 'data': ', '.join(search_domains)}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nis-domain-name', 'data': domain}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nis-servers', 'data': ', '.join(nis_servers)}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nisp-domain-name', 'data': domain}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nisp-servers', 'data': ', '.join(nis_servers)}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'sntp-servers', 'data': sntp_server}, + ) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'sip-server-dns', 'data': sip_server}, + ) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'], + {'pool': f'{range_start} - {range_stop}'}, + ) + + client_base = 1 + for client in ['client1', 'client2', 'client3']: + duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}' + ip = inc_ip(subnet, client_base) + prefix = inc_ip(subnet, client_base << 64) + '/64' + + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'reservations'], + { + 'hostname': client, + 'duid': duid, + 'ip-addresses': [ip], + 'prefixes': [prefix], + }, + ) + + client_base += 1 + + # Check for running process + self.verify_kea_service_running(process_name) + + # perform cleanup + self.cli_delete(['interfaces', 'dummy', interface, 'address']) + self.cli_delete(['interfaces', 'dummy', interface, 'vrf']) + self.cli_delete(base) + self.cli_commit() + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py index 99c7e6a1f..ef95d5d80 100755 --- a/src/conf_mode/service_dhcp-server.py +++ b/src/conf_mode/service_dhcp-server.py @@ -16,11 +16,13 @@ import os +from sys import exit +from sys import argv + from glob import glob from ipaddress import ip_address from ipaddress import ip_network from netaddr import IPRange -from sys import exit from vyos.config import Config from vyos.pki import wrap_certificate @@ -41,16 +43,53 @@ from vyos import airbag airbag.enable() -ctrl_socket = '/run/kea/dhcp4-ctrl-socket' -config_file = '/run/kea/kea-dhcp4.conf' -config_file_d2 = '/run/kea/kea-dhcp-ddns.conf' -lease_file = '/config/dhcp/dhcp4-leases.csv' -lease_file_glob = '/config/dhcp/dhcp4-leases*' +ctrl_socket = '' +config_file = '' +config_file_d2 = '' +lease_file = '' +lease_file_glob = '' + +ca_cert_file = '' +cert_file = '' +cert_key_file = '' + user_group = '_kea' -ca_cert_file = '/run/kea/kea-failover-ca.pem' -cert_file = '/run/kea/kea-failover.pem' -cert_key_file = '/run/kea/kea-failover-key.pem' + +def _override_for_vrf(vrf_name): + """ + This function is intended to override global vars when vrf is enabled + """ + global ctrl_socket, config_file, config_file_d2, lease_file, lease_file_glob + global ca_cert_file, cert_file, cert_key_file + + ctrl_socket = f'/run/kea/dhcp4-{vrf_name}-ctrl-socket' + config_file = f'/run/kea/kea-{vrf_name}-dhcp4.conf' + config_file_d2 = f'/run/kea/kea-{vrf_name}-dhcp-ddns.conf' + lease_file = f'/config/dhcp/dhcp4-{vrf_name}-leases.csv' + lease_file_glob = f'/config/dhcp/dhcp4-{vrf_name}-leases*' + + ca_cert_file = f'/run/kea/kea-{vrf_name}-failover-ca.pem' + cert_file = f'/run/kea/kea-{vrf_name}-failover.pem' + cert_key_file = f'/run/kea/kea-{vrf_name}-failover-key.pem' + + +def _reset_vars(): + """ + This function is intended to reset global vars when vrf is not enabled + """ + global ctrl_socket, config_file, config_file_d2, lease_file, lease_file_glob + global ca_cert_file, cert_file, cert_key_file + + ctrl_socket = '/run/kea/dhcp4-ctrl-socket' + config_file = '/run/kea/kea-dhcp4.conf' + config_file_d2 = '/run/kea/kea-dhcp-ddns.conf' + lease_file = '/config/dhcp/dhcp4-leases.csv' + lease_file_glob = '/config/dhcp/dhcp4-leases*' + + ca_cert_file = '/run/kea/kea-failover-ca.pem' + cert_file = '/run/kea/kea-failover.pem' + cert_key_file = '/run/kea/kea-failover-key.pem' def dhcp_slice_range(exclude_list, range_dict): @@ -125,7 +164,19 @@ def get_config(config=None): conf = config else: conf = Config() - base = ['service', 'dhcp-server'] + + # if running in vrf, set base diffrently + if argv and len(argv) > 1: + vrf_name = argv[1] + base = ['vrf', 'name', vrf_name, 'service', 'dhcp-server'] + + # vrf is defined, override other vars aswell + _override_for_vrf(vrf_name) + else: + base = ['service', 'dhcp-server'] + + # vrf is not defined reset vars + _reset_vars() if not conf.exists(base): return None @@ -137,6 +188,10 @@ def get_config(config=None): with_recursive_defaults=True, ) + # add vrf context if present + if argv and len(argv) > 1: + dhcp['vrf_context'] = argv[1] + if 'shared_network_name' in dhcp: for network, network_config in dhcp['shared_network_name'].items(): if 'subnet' in network_config: @@ -524,7 +579,12 @@ def generate(dhcp): def apply(dhcp): - services = ['kea-dhcp4-server', 'kea-dhcp-ddns-server'] + # if running in vrf, set base diffrently + if argv and len(argv) > 1: + vrf_name = argv[1] + services = [f'kea-dhcp4-server@{vrf_name}', f'kea-dhcp-ddns-server@{vrf_name}'] + else: + services = ['kea-dhcp4-server', 'kea-dhcp-ddns-server'] if not dhcp or 'disable' in dhcp: for service in services: @@ -538,7 +598,7 @@ def apply(dhcp): for service in services: action = 'restart' - if service == 'kea-dhcp-ddns-server' and 'dynamic_dns_update' not in dhcp: + if 'kea-dhcp-ddns-server' in service and 'dynamic_dns_update' not in dhcp: action = 'stop' call(f'systemctl {action} {service}.service') diff --git a/src/conf_mode/service_dhcpv6-server.py b/src/conf_mode/service_dhcpv6-server.py index 7af88007c..01f3abfe9 100755 --- a/src/conf_mode/service_dhcpv6-server.py +++ b/src/conf_mode/service_dhcpv6-server.py @@ -16,10 +16,12 @@ import os +from sys import exit +from sys import argv + from glob import glob from ipaddress import ip_address from ipaddress import ip_network -from sys import exit from vyos.config import Config from vyos.template import render @@ -32,26 +34,71 @@ from vyos.utils.dict import dict_search from vyos.utils.network import is_subnet_connected from vyos import ConfigError from vyos import airbag + airbag.enable() -config_file = '/run/kea/kea-dhcp6.conf' -ctrl_socket = '/run/kea/dhcp6-ctrl-socket' -lease_file = '/config/dhcp/dhcp6-leases.csv' -lease_file_glob = '/config/dhcp/dhcp6-leases*' + +config_file = '' +ctrl_socket = '' +lease_file = '' +lease_file_glob = '' + user_group = '_kea' + +def _override_for_vrf(vrf_name): + """ + This function is intended to override some of the global vars + """ + global ctrl_socket, config_file, lease_file, lease_file_glob + + config_file = f'/run/kea/kea-{vrf_name}-dhcp6.conf' + ctrl_socket = f'/run/kea/dhcp6-{vrf_name}-ctrl-socket' + lease_file = f'/config/dhcp/dhcp6-{vrf_name}-leases.csv' + lease_file_glob = f'/config/dhcp/dhcp6-{vrf_name}-leases*' + + +def _reset_vars(): + """ + This function is intended to reset global vars when vrf is not enabled + """ + global ctrl_socket, config_file, lease_file, lease_file_glob + + config_file = '/run/kea/kea-dhcp6.conf' + ctrl_socket = '/run/kea/dhcp6-ctrl-socket' + lease_file = '/config/dhcp/dhcp6-leases.csv' + lease_file_glob = '/config/dhcp/dhcp6-leases*' + + def get_config(config=None): if config: conf = config else: conf = Config() - base = ['service', 'dhcpv6-server'] + + # if running in vrf, set base diffrently + if argv and len(argv) > 1: + vrf_name = argv[1] + base = ['vrf', 'name', vrf_name, 'service', 'dhcpv6-server'] + + # vrf is defined, override other vars aswell + _override_for_vrf(vrf_name) + else: + base = ['service', 'dhcpv6-server'] + + # vrf is not defined reset vars + _reset_vars() if not conf.exists(base): return None - dhcpv6 = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True) + dhcpv6 = conf.get_config_dict( + base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True + ) + + # add vrf context if present + if argv and len(argv) > 1: + dhcpv6['vrf_context'] = argv[1] + return dhcpv6 def verify(dhcpv6): @@ -239,8 +286,14 @@ def generate(dhcpv6): return None def apply(dhcpv6): + # if running in vrf, set base diffrently + if argv and len(argv) > 1: + vrf_name = argv[1] + service_name = f'kea-dhcp6-server@{vrf_name}.service' + else: + service_name = 'kea-dhcp6-server.service' + # bail out early - looks like removal from running config - service_name = 'kea-dhcp6-server.service' if not dhcpv6 or 'disable' in dhcpv6: # DHCP server is removed in the commit call(f'systemctl stop {service_name}') diff --git a/src/etc/sudoers.d/vyos b/src/etc/sudoers.d/vyos index 198b9b9aa..2dd8850c4 100644 --- a/src/etc/sudoers.d/vyos +++ b/src/etc/sudoers.d/vyos @@ -47,6 +47,8 @@ Cmnd_Alias DIAGNOSTICS = /bin/ip vrf exec * /bin/ping *, \ /usr/libexec/vyos/op_mode/* Cmnd_Alias KEA_IP6_ROUTES = /sbin/ip -6 route replace *,\ /sbin/ip -6 route del * +Cmnd_Alias KEA_DHCP = /bin/ip vrf exec * /usr/sbin/kea-dhcp*,\ + /usr/bin/chown * /run/kea/* %operator ALL=NOPASSWD: DATE, IPTABLES, ETHTOOL, IPFLUSH, HWINFO, \ PPPOE_CMDS, PCAPTURE, /usr/sbin/wanpipemon, \ DMIDECODE, DISK, CONNTRACK, IP6TABLES, \ @@ -62,3 +64,4 @@ Cmnd_Alias KEA_IP6_ROUTES = /sbin/ip -6 route replace *,\ %sudo ALL=NOPASSWD: /usr/bin/mokutil _kea ALL=NOPASSWD: KEA_IP6_ROUTES +_kea ALL=NOPASSWD: KEA_DHCP diff --git a/src/etc/systemd/system/kea-dhcp-ddns-server@.service b/src/etc/systemd/system/kea-dhcp-ddns-server@.service new file mode 100644 index 000000000..b6360e573 --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp-ddns-server@.service @@ -0,0 +1,22 @@ +[Unit] +Description=Kea DDNS Service +Documentation=man:kea-dhcp-ddns(8) +Wants=network-online.target +After=vyos-router.service + +[Service] +User=_kea +AmbientCapabilities=CAP_NET_BIND_SERVICE +Environment="KEA_LOCKFILE_DIR=/run/lock/kea" +ConfigurationDirectory=kea +RuntimeDirectory=kea lock/kea +RuntimeDirectoryPreserve=yes +LogsDirectory=kea +LogsDirectoryMode=0750 +StateDirectory=kea +ExecStart=sudo /bin/ip vrf exec %i /usr/sbin/kea-dhcp-ddns -c /run/kea/kea-%i-dhcp-ddns.conf +ExecStartPost=/bin/sh -c 'sleep 10 && sudo /usr/bin/chown _kea:_kea /run/kea/kea-%i-dhcp-ddns* && sudo /usr/bin/chown _kea:_kea /run/kea/logger_lockfile' +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/src/etc/systemd/system/kea-dhcp4-server@.service b/src/etc/systemd/system/kea-dhcp4-server@.service new file mode 100644 index 000000000..f63364275 --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp4-server@.service @@ -0,0 +1,22 @@ +[Unit] +Description=Kea IPv4 DHCP daemon +Documentation=man:kea-dhcp4(8) +Wants=network-online.target +After=vyos-router.service + +[Service] +User=_kea +AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW +Environment="KEA_LOCKFILE_DIR=/run/lock/kea" +ConfigurationDirectory=kea +RuntimeDirectory=kea lock/kea +RuntimeDirectoryPreserve=yes +LogsDirectory=kea +LogsDirectoryMode=0750 +StateDirectory=kea +ExecStart=sudo /bin/ip vrf exec %i /usr/sbin/kea-dhcp4 -c /run/kea/kea-%i-dhcp4.conf +ExecStartPost=/bin/sh -c 'sleep 10 && sudo /usr/bin/chown _kea:_kea /run/kea/dhcp4-%i-ctrl* && sudo /usr/bin/chown _kea:_kea /run/kea/kea-%i-dhcp4* && sudo /usr/bin/chown _kea:_kea /run/kea/logger_lockfile' +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/src/etc/systemd/system/kea-dhcp6-server@.service b/src/etc/systemd/system/kea-dhcp6-server@.service new file mode 100644 index 000000000..1d2330f74 --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp6-server@.service @@ -0,0 +1,22 @@ +[Unit] +Description=Kea IPv6 DHCP daemon +Documentation=man:kea-dhcp6(8) +Wants=network-online.target +After=vyos-router.service + +[Service] +User=_kea +AmbientCapabilities=CAP_NET_BIND_SERVICE +Environment="KEA_LOCKFILE_DIR=/run/lock/kea" +ConfigurationDirectory=kea +RuntimeDirectory=kea lock/kea +RuntimeDirectoryPreserve=yes +LogsDirectory=kea +LogsDirectoryMode=0750 +StateDirectory=kea +ExecStart=sudo /bin/ip vrf exec %i /usr/sbin/kea-dhcp6 -c /run/kea/kea-%i-dhcp6.conf +ExecStartPost=/bin/sh -c 'sleep 10 && sudo /usr/bin/chown _kea:_kea /run/kea/dhcp6-%i-ctrl* && sudo /usr/bin/chown _kea:_kea /run/kea/kea-%i-dhcp6* && sudo /usr/bin/chown _kea:_kea /run/kea/logger_lockfile' +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 725bfc75b..f4b7ac582 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -83,12 +83,12 @@ ArgOrigin = typing.Literal['local', 'remote'] def _get_raw_server_leases( - config, family='inet', pool=None, sorted=None, state=[], origin=None + config, family='inet', vrf='', pool=None, sorted=None, state=[], origin=None ) -> list: inet_suffix = '6' if family == 'inet6' else '4' pools = [pool] if pool else kea_get_dhcp_pools(config, inet_suffix) - mappings = kea_get_server_leases(config, inet_suffix, pools, state, origin) + mappings = kea_get_server_leases(config, inet_suffix, vrf, pools, state, origin) if sorted: if sorted == 'ip': @@ -166,9 +166,13 @@ def _get_formatted_server_leases(raw_data, family='inet'): return output -def _get_pool_size(pool, family='inet'): +def _get_pool_size(pool, family='inet', vrf=''): v = 'v6' if family == 'inet6' else '' - base = f'service dhcp{v}-server shared-network-name {pool}' + # if vrf is set get the correct base for config + if vrf: + base = f'vrf name {vrf} service dhcp{v}-server shared-network-name {pool}' + else: + base = f'service dhcp{v}-server shared-network-name {pool}' size = 0 subnets = config.list_nodes(f'{base} subnet') for subnet in subnets: @@ -185,14 +189,14 @@ def _get_pool_size(pool, family='inet'): return size -def _get_raw_server_pool_statistics(config, family='inet', pool=None): +def _get_raw_server_pool_statistics(config, family='inet', vrf='', pool=None): inet_suffix = '6' if family == 'inet6' else '4' pools = [pool] if pool else kea_get_dhcp_pools(config, inet_suffix) stats = [] for p in pools: - size = _get_pool_size(family=family, pool=p) - leases = len(_get_raw_server_leases(config, family=family, pool=p)) + size = _get_pool_size(family=family, vrf=vrf, pool=p) + leases = len(_get_raw_server_leases(config, family=family, vrf=vrf, pool=p)) use_percentage = round(leases / size * 100) if size != 0 else 0 pool_stats = { 'pool': p, @@ -269,11 +273,20 @@ def _verify_server(func): def _wrapper(*args, **kwargs): config = ConfigTreeQuery() family = kwargs.get('family') + vrf = kwargs.get('vrf') v = 'v6' if family == 'inet6' else '' - unconf_message = f'DHCP{v} server is not configured' + # Check if config does not exist - if not config.exists(f'service dhcp{v}-server'): - raise vyos.opmode.UnconfiguredSubsystem(unconf_message) + if vrf: + unconf_message = f'DHCP{v} server is not configured for VRF {vrf}' + if not config.exists(f'vrf name {vrf} service dhcp{v}-server'): + raise vyos.opmode.UnconfiguredSubsystem(unconf_message) + else: + unconf_message = f'DHCP{v} server is not configured' + if not config.exists(f'service dhcp{v}-server'): + raise vyos.opmode.UnconfiguredSubsystem(unconf_message) + + # return return func(*args, **kwargs) return _wrapper @@ -303,25 +316,42 @@ def _verify_client(func): @_verify_server def show_server_pool_statistics( - raw: bool, family: ArgFamily, pool: typing.Optional[str] + raw: bool, family: ArgFamily, vrf: str, pool: typing.Optional[str] ): v = 'v6' if family == 'inet6' else '' inet_suffix = '6' if family == 'inet6' else '4' - if not is_systemd_service_running(f'kea-dhcp{inet_suffix}-server.service'): + if vrf: + service = f'kea-dhcp{inet_suffix}-server@{vrf}.service' + else: + service = f'kea-dhcp{inet_suffix}-server.service' + + if not is_systemd_service_running(service): Warning(stale_warn_msg) try: - active_config = kea_get_active_config(inet_suffix) + active_config = kea_get_active_config(inet_suffix, vrf) except Exception: - raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') + if vrf: + raise vyos.opmode.DataUnavailable( + f'Cannot fetch DHCP server configuration for VRF {vrf}' + ) + else: + raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') active_pools = kea_get_dhcp_pools(active_config, inet_suffix) if pool and active_pools and pool not in active_pools: - raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') + if vrf: + raise vyos.opmode.IncorrectValue( + f'DHCP{v} pool "{pool}" does not exist for VRF {vrf}!' + ) + else: + raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') - pool_data = _get_raw_server_pool_statistics(active_config, family=family, pool=pool) + pool_data = _get_raw_server_pool_statistics( + active_config, family=family, vrf=vrf, pool=pool + ) if raw: return pool_data else: @@ -332,6 +362,7 @@ def show_server_pool_statistics( def show_server_leases( raw: bool, family: ArgFamily, + vrf: str, pool: typing.Optional[str], sorted: typing.Optional[str], state: typing.Optional[ArgState], @@ -340,18 +371,33 @@ def show_server_leases( v = 'v6' if family == 'inet6' else '' inet_suffix = '6' if family == 'inet6' else '4' - if not is_systemd_service_running(f'kea-dhcp{inet_suffix}-server.service'): + if vrf: + service = f'kea-dhcp{inet_suffix}-server@{vrf}.service' + else: + service = f'kea-dhcp{inet_suffix}-server.service' + + if not is_systemd_service_running(service): Warning(stale_warn_msg) try: - active_config = kea_get_active_config(inet_suffix) + active_config = kea_get_active_config(inet_suffix, vrf) except Exception: - raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') + if vrf: + raise vyos.opmode.DataUnavailable( + f'Cannot fetch DHCP server configuration for VRF {vrf}' + ) + else: + raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') active_pools = kea_get_dhcp_pools(active_config, inet_suffix) if pool and active_pools and pool not in active_pools: - raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') + if vrf: + raise vyos.opmode.IncorrectValue( + f'DHCP{v} pool "{pool}" does not exist for VRF {vrf}!' + ) + else: + raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') sort_valid = sort_valid_inet6 if family == 'inet6' else sort_valid_inet if sorted and sorted not in sort_valid: @@ -363,6 +409,7 @@ def show_server_leases( lease_data = _get_raw_server_leases( config=active_config, family=family, + vrf=vrf, pool=pool, sorted=sorted, state=state, @@ -378,24 +425,40 @@ def show_server_leases( def show_server_static_mappings( raw: bool, family: ArgFamily, + vrf: str, pool: typing.Optional[str], sorted: typing.Optional[str], ): v = 'v6' if family == 'inet6' else '' inet_suffix = '6' if family == 'inet6' else '4' - if not is_systemd_service_running(f'kea-dhcp{inet_suffix}-server.service'): + if vrf: + service = f'kea-dhcp{inet_suffix}-server@{vrf}.service' + else: + service = f'kea-dhcp{inet_suffix}-server.service' + + if not is_systemd_service_running(service): Warning(stale_warn_msg) try: - active_config = kea_get_active_config(inet_suffix) + active_config = kea_get_active_config(inet_suffix, vrf) except Exception: - raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') + if vrf: + raise vyos.opmode.DataUnavailable( + f'Cannot fetch DHCP server configuration for VRF {vrf}' + ) + else: + raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') active_pools = kea_get_dhcp_pools(active_config, inet_suffix) if pool and active_pools and pool not in active_pools: - raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') + if vrf: + raise vyos.opmode.IncorrectValue( + f'DHCP{v} pool "{pool}" does not exist for VRF {vrf}!' + ) + else: + raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') if sorted and sorted not in mapping_sort_valid: raise vyos.opmode.IncorrectValue(f'DHCP{v} sort "{sorted}" is invalid!') @@ -409,13 +472,13 @@ def show_server_static_mappings( return _get_formatted_server_static_mappings(static_mappings) -def _lease_valid(inet, address): - leases = kea_get_leases(inet) +def _lease_valid(inet, vrf, address): + leases = kea_get_leases(inet, vrf) return any(lease['ip-address'] == address for lease in leases) @_verify_server -def clear_dhcp_server_lease(family: ArgFamily, address: str): +def clear_dhcp_server_lease(family: ArgFamily, vrf: str, address: str): v = 'v6' if family == 'inet6' else '' inet = '6' if family == 'inet6' else '4' @@ -423,7 +486,7 @@ def clear_dhcp_server_lease(family: ArgFamily, address: str): print(f'Lease not found on DHCP{v} server') return None - if not kea_delete_lease(inet, address): + if not kea_delete_lease(inet, vrf, address): print(f'Failed to clear lease for "{address}"') return None diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh index 47c276270..556d7dbcb 100755 --- a/src/system/on-dhcp-event.sh +++ b/src/system/on-dhcp-event.sh @@ -30,7 +30,7 @@ get_subnet_domain_name () { from vyos.kea import kea_get_active_config from vyos.utils.dict import dict_search_args -config = kea_get_active_config('4') +config = kea_get_active_config('4', '') shared_networks = dict_search_args(config, 'arguments', f'Dhcp4', 'shared-networks') found = False diff --git a/src/system/sync-dhcp-lease-to-hosts.py b/src/system/sync-dhcp-lease-to-hosts.py index 5c8b18faf..8baa6992a 100755 --- a/src/system/sync-dhcp-lease-to-hosts.py +++ b/src/system/sync-dhcp-lease-to-hosts.py @@ -33,17 +33,17 @@ logs_handler = logging.StreamHandler() logger.addHandler(logs_handler) -def _get_all_server_leases(inet_suffix='4') -> list: +def _get_all_server_leases(inet_suffix='4', vrf='') -> list: mappings = [] try: - active_config = kea_get_active_config(inet_suffix) + active_config = kea_get_active_config(inet_suffix, vrf) except Exception: raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') try: pools = kea_get_dhcp_pools(active_config, inet_suffix) mappings = kea_get_server_leases( - active_config, inet_suffix, pools, state=[], origin=None + active_config, inet_suffix, vrf, pools, state=[], origin=None ) except Exception: raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server leases') |
