From bed1cd01904ef89b5d31bd47de0f230214900f16 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sun, 19 Nov 2023 21:43:15 +0100 Subject: dhcp-client: T5760: add constraints for dhclient string options The string data type specifies either an NVT ASCII string enclosed in double quotes, or a series of octets specified in hexadecimal, separated by colons. For example: set interfaces ethernet eth0 dhcp-options client-id CLIENT-FOO or set interfaces ethernet eth0 dhcp-options client-id 43:4c:49:45:54:2d:46:4f:4f As of now there was no input validation performed. --- data/templates/dhcp-client/ipv4.j2 | 15 +++++++++++---- .../include/constraint/dhcp-client-string-option.xml.i | 4 ++++ .../include/interface/dhcp-options.xml.i | 14 ++++++++++++++ smoketest/scripts/cli/base_interfaces_test.py | 13 +++++++++++-- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 interface-definitions/include/constraint/dhcp-client-string-option.xml.i diff --git a/data/templates/dhcp-client/ipv4.j2 b/data/templates/dhcp-client/ipv4.j2 index cc5ddf09c..4a5d5e54d 100644 --- a/data/templates/dhcp-client/ipv4.j2 +++ b/data/templates/dhcp-client/ipv4.j2 @@ -9,14 +9,21 @@ interface "{{ ifname }}" { send host-name "{{ dhcp_options.host_name }}"; {% if dhcp_options.client_id is vyos_defined %} {% set client_id = dhcp_options.client_id %} -{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. If not HEX, use double quotes ASCII format #} -{% if not dhcp_options.client_id.split(':') | length >= 5 %} -{% set client_id = '"' + dhcp_options.client_id + '"' %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not client_id.split(':') | length >= 3 %} +{% set client_id = '"' ~ dhcp_options.client_id ~ '"' %} {% endif %} send dhcp-client-identifier {{ client_id }}; {% endif %} {% if dhcp_options.vendor_class_id is vyos_defined %} - send vendor-class-identifier "{{ dhcp_options.vendor_class_id }}"; +{% set vendor_class_id = dhcp_options.vendor_class_id %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not vendor_class_id.split(':') | length >= 3 %} +{% set vendor_class_id = '"' ~ dhcp_options.vendor_class_id ~ '"' %} +{% endif %} + send vendor-class-identifier {{ vendor_class_id }}; {% endif %} # The request statement causes the client to request that any server responding to the # client send the client its values for the specified options. diff --git a/interface-definitions/include/constraint/dhcp-client-string-option.xml.i b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i new file mode 100644 index 000000000..76e0e5466 --- /dev/null +++ b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i @@ -0,0 +1,4 @@ + +[-_a-zA-Z0-9\s]+ +([a-fA-F0-9][a-fA-F0-9]:){2,}[a-fA-F0-9][a-fA-F0-9] + diff --git a/interface-definitions/include/interface/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i index 8027769ff..fff83fd5c 100644 --- a/interface-definitions/include/interface/dhcp-options.xml.i +++ b/interface-definitions/include/interface/dhcp-options.xml.i @@ -7,6 +7,13 @@ Identifier used by client to identify itself to the DHCP server + + txt + DHCP option string + + + #include + @@ -27,6 +34,13 @@ Identify the vendor client type to the DHCP server + + txt + DHCP option string + + + #include + #include diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 73b4e9764..1edcda4fd 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -158,14 +158,20 @@ class BasicInterfaceTest: if not self._test_dhcp or not self._test_vrf: self.skipTest('not supported') + client_id = 'VyOS-router' distance = '100' + hostname = 'vyos' + vendor_class_id = 'vyos-vendor' for interface in self._interfaces: for option in self._options.get(interface, []): self.cli_set(self._base_path + [interface] + option.split()) self.cli_set(self._base_path + [interface, 'address', 'dhcp']) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'client-id', client_id]) self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'host-name', hostname]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'vendor-class-id', vendor_class_id]) self.cli_commit() @@ -175,8 +181,11 @@ class BasicInterfaceTest: self.assertTrue(dhclient_pid) dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf') - self.assertIn('request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config) - self.assertIn('require subnet-mask;', dhclient_config) + self.assertIn(f'request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config) + self.assertIn(f'require subnet-mask;', dhclient_config) + self.assertIn(f'send host-name "{hostname}";', dhclient_config) + self.assertIn(f'send dhcp-client-identifier "{client_id}";', dhclient_config) + self.assertIn(f'send vendor-class-identifier "{vendor_class_id}";', dhclient_config) # and the commandline has the appropriate options cmdline = read_file(f'/proc/{dhclient_pid}/cmdline') -- cgit v1.2.3 From 260645d0c6ff078cc89601f3a586195902f9c18e Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sun, 19 Nov 2023 21:47:31 +0100 Subject: dhcp-client: T5760: add CLI option to pass user-class parameter Example: set interfaces ethernet eth0 dhcp-options user-class VyOS or set interfaces ethernet eth0 dhcp-options user-class 56:79:4f:53 --- data/templates/dhcp-client/ipv4.j2 | 9 +++++++++ interface-definitions/include/interface/dhcp-options.xml.i | 12 ++++++++++++ smoketest/scripts/cli/base_interfaces_test.py | 3 +++ 3 files changed, 24 insertions(+) diff --git a/data/templates/dhcp-client/ipv4.j2 b/data/templates/dhcp-client/ipv4.j2 index 4a5d5e54d..77905e054 100644 --- a/data/templates/dhcp-client/ipv4.j2 +++ b/data/templates/dhcp-client/ipv4.j2 @@ -24,6 +24,15 @@ interface "{{ ifname }}" { {% set vendor_class_id = '"' ~ dhcp_options.vendor_class_id ~ '"' %} {% endif %} send vendor-class-identifier {{ vendor_class_id }}; +{% endif %} +{% if dhcp_options.user_class is vyos_defined %} +{% set user_class = dhcp_options.user_class %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not user_class.split(':') | length >= 3 %} +{% set user_class = '"' ~ dhcp_options.user_class ~ '"' %} +{% endif %} + send user-class {{ user_class }}; {% endif %} # The request statement causes the client to request that any server responding to the # client send the client its values for the specified options. diff --git a/interface-definitions/include/interface/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i index fff83fd5c..733512a98 100644 --- a/interface-definitions/include/interface/dhcp-options.xml.i +++ b/interface-definitions/include/interface/dhcp-options.xml.i @@ -43,6 +43,18 @@ + + + Identify to the DHCP server, user configurable option + + txt + DHCP option string + + + #include + + + #include #include diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 1edcda4fd..da196133a 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -162,6 +162,7 @@ class BasicInterfaceTest: distance = '100' hostname = 'vyos' vendor_class_id = 'vyos-vendor' + user_class = 'vyos' for interface in self._interfaces: for option in self._options.get(interface, []): @@ -172,6 +173,7 @@ class BasicInterfaceTest: self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance]) self.cli_set(self._base_path + [interface, 'dhcp-options', 'host-name', hostname]) self.cli_set(self._base_path + [interface, 'dhcp-options', 'vendor-class-id', vendor_class_id]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'user-class', user_class]) self.cli_commit() @@ -186,6 +188,7 @@ class BasicInterfaceTest: self.assertIn(f'send host-name "{hostname}";', dhclient_config) self.assertIn(f'send dhcp-client-identifier "{client_id}";', dhclient_config) self.assertIn(f'send vendor-class-identifier "{vendor_class_id}";', dhclient_config) + self.assertIn(f'send user-class "{user_class}";', dhclient_config) # and the commandline has the appropriate options cmdline = read_file(f'/proc/{dhclient_pid}/cmdline') -- cgit v1.2.3