From f9b1323a1464af2da539bf279bec77fdea771b0c Mon Sep 17 00:00:00 2001
From: Christian Breunig <christian@breunig.cc>
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.

(cherry picked from commit bed1cd01904ef89b5d31bd47de0f230214900f16)
---
 data/templates/dhcp-client/ipv4.j2 | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

(limited to 'data')

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


From 30bbf2278f2b30d4cb67331292c27d09f9a81bf0 Mon Sep 17 00:00:00 2001
From: Christian Breunig <christian@breunig.cc>
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

(cherry picked from commit 260645d0c6ff078cc89601f3a586195902f9c18e)
---
 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(+)

(limited to 'data')

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 @@
         </constraint>
       </properties>
     </leafNode>
+    <leafNode name="user-class">
+      <properties>
+        <help>Identify to the DHCP server, user configurable option</help>
+        <valueHelp>
+          <format>txt</format>
+          <description>DHCP option string</description>
+        </valueHelp>
+        <constraint>
+          #include <include/constraint/dhcp-client-string-option.xml.i>
+        </constraint>
+      </properties>
+    </leafNode>
     #include <include/interface/no-default-route.xml.i>
     #include <include/interface/default-route-distance.xml.i>
     <leafNode name="reject">
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 81240f78b..6862207e3 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