]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: Add DHCP vendor specific options to dbus API 29247/head
authorNandakumar Raghavan <naraghavan@microsoft.com>
Wed, 20 Sep 2023 14:33:38 +0000 (14:33 +0000)
committerNandakumar Raghavan <naraghavan@microsoft.com>
Fri, 27 Oct 2023 12:11:09 +0000 (12:11 +0000)
Add DHCP vendor specific options to expose in dbus API.
This will be added to the JSON output when we query
org.freedesktop.network1.Manager object.

src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/test-dhcp6-client.c
src/network/networkd-json.c
src/systemd/sd-dhcp6-lease.h

index 0a2434b5ab0e0221b7cdf24f46444e37e27991f0..e76a108f60ea05b74ec407e2ddb4c94634d31f2e 100644 (file)
@@ -12,6 +12,7 @@
 #include "dhcp6-option.h"
 #include "dhcp6-protocol.h"
 #include "macro.h"
+#include "set.h"
 #include "time-util.h"
 
 struct sd_dhcp6_lease {
@@ -45,6 +46,8 @@ struct sd_dhcp6_lease {
         size_t sntp_count;
         char *fqdn;
         char *captive_portal;
+        struct sd_dhcp6_option **sorted_vendor_options;
+        Set *vendor_options;
 };
 
 int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
index 3608586e5dcf0382761e1dff2657b31656905419..69d5a11f57ca3e378ef139a32b2b555af0d0a13b 100644 (file)
@@ -587,6 +587,83 @@ int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
         return 0;
 }
 
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
+        int r;
+
+        assert_return(lease, -EINVAL);
+
+        if (set_isempty(lease->vendor_options))
+                return -ENODATA;
+
+        if (ret) {
+                if (!lease->sorted_vendor_options) {
+                        r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
+                        if (r < 0)
+                                return r;
+                }
+
+                *ret = lease->sorted_vendor_options;
+        }
+
+        return set_size(lease->vendor_options);
+}
+
+static int dhcp6_lease_insert_vendor_option(
+                sd_dhcp6_lease *lease,
+                uint16_t option_code,
+                const void *data,
+                size_t len,
+                uint16_t enterprise_id) {
+
+        _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
+
+        assert(lease);
+
+        option = new(sd_dhcp6_option, 1);
+        if (!option)
+                return -ENOMEM;
+
+        *option = (sd_dhcp6_option) {
+                .n_ref = 1,
+                .enterprise_identifier = enterprise_id,
+                .option = option_code,
+                .length = len,
+        };
+        option->data = memdup_suffix0(data, len);
+        if (!option->data)
+                return -ENOMEM;
+
+        return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
+}
+
+static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+        int r;
+        uint32_t enterprise_id;
+
+        assert(lease);
+        assert(optval || optlen == 0);
+
+        if (optlen < sizeof(be32_t))
+                return -EBADMSG;
+
+        enterprise_id = unaligned_read_be32(optval);
+
+        for (size_t offset = 4; offset < optlen;) {
+                const uint8_t *subval;
+                size_t sublen;
+                uint16_t subopt;
+
+                r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
+                if (r < 0)
+                        return r;
+        }
+        return 0;
+}
+
 static int dhcp6_lease_parse_message(
                 sd_dhcp6_client *client,
                 sd_dhcp6_lease *lease,
@@ -772,6 +849,13 @@ static int dhcp6_lease_parse_message(
 
                         irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
                         break;
+
+                case SD_DHCP6_OPTION_VENDOR_OPTS:
+                        r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
+                        if (r < 0)
+                                log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
+
+                        break;
                 }
         }
 
@@ -812,6 +896,8 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         if (!lease)
                 return NULL;
 
+        set_free(lease->vendor_options);
+        free(lease->sorted_vendor_options);
         free(lease->clientid);
         free(lease->serverid);
         dhcp6_ia_free(lease->ia_na);
index 109b23c6397c421526f58838b7c1ea64e7dc65c7..ae3cdb86320185624829cb840830359411bd876e 100644 (file)
@@ -53,6 +53,8 @@
         0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2
 #define SERVER_ID_BYTES                                                 \
         0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53
+#define VENDOR_SUBOPTION_BYTES                                         \
+        0x01
 
 static const struct in6_addr local_address =
         { { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } };
@@ -70,10 +72,18 @@ static const struct in6_addr ntp1 = { { { NTP1_BYTES } } };
 static const struct in6_addr ntp2 = { { { NTP2_BYTES } } };
 static const uint8_t client_id[] = { CLIENT_ID_BYTES };
 static const uint8_t server_id[] = { SERVER_ID_BYTES };
+static uint8_t vendor_suboption_data[] = { VENDOR_SUBOPTION_BYTES };
 static const struct ether_addr mac = {
         .ether_addr_octet = { 'A', 'B', 'C', '1', '2', '3' },
 };
 static int test_fd[2] = EBADF_PAIR;
+static sd_dhcp6_option vendor_suboption = {
+        .n_ref = 1,
+        .enterprise_identifier = 32,
+        .option = 247,
+        .data = vendor_suboption_data,
+        .length = 1,
+};
 static int test_ifindex = 42;
 static unsigned test_client_sent_message_count = 0;
 static sd_dhcp6_client *client_ref = NULL;
@@ -100,6 +110,7 @@ TEST(client_basic) {
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER) >= 0);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) >= 0);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVER) >= 0);
+        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_VENDOR_OPTS) >= 0);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN) >= 0);
         assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NIS_SERVER) >= 0);
@@ -736,6 +747,9 @@ static const uint8_t msg_reply[] = {
         /* Client FQDN */
         0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
         0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
+        /* Vendor specific options */
+        0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
+        0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
 };
 
 static const uint8_t msg_advertise[] = {
@@ -815,6 +829,9 @@ static const uint8_t msg_advertise[] = {
         /* Client FQDN */
         0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
         0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
+        /* Vendor specific options */
+        0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
+        0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
 };
 
 static void test_client_verify_information_request(const DHCP6Message *msg, size_t len) {
@@ -855,6 +872,7 @@ static void test_client_verify_request(const DHCP6Message *msg, size_t len) {
 
 static void test_lease_common(sd_dhcp6_client *client) {
         sd_dhcp6_lease *lease;
+        sd_dhcp6_option **suboption;
         const struct in6_addr *addrs;
         const char *str;
         char **strv;
@@ -888,6 +906,11 @@ static void test_lease_common(sd_dhcp6_client *client) {
         assert_se(lease->sntp_count == 2);
         assert_se(in6_addr_equal(&lease->sntp[0], &sntp1));
         assert_se(in6_addr_equal(&lease->sntp[1], &sntp2));
+
+        assert_se(sd_dhcp6_lease_get_vendor_options(lease, &suboption) > 0);
+        assert_se((*suboption)->enterprise_identifier == vendor_suboption.enterprise_identifier);
+        assert_se((*suboption)->option == vendor_suboption.option);
+        assert_se(*(uint8_t*)(*suboption)->data == *(uint8_t*)vendor_suboption.data);
 }
 
 static void test_lease_managed(sd_dhcp6_client *client) {
index ef3153642b2c2252dee635132f9591f2e3b333f3..6e53d368d293048d80bbafa561160b0f008359ae 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/nexthop.h>
 
 #include "dhcp-server-internal.h"
+#include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dns-domain.h"
 #include "ip-protocol-list.h"
@@ -1036,6 +1037,32 @@ static int dhcp_server_append_json(Link *link, JsonVariant **v) {
         return json_variant_set_field_non_null(v, "DHCPServer", w);
 }
 
+static int dhcp6_client_vendor_options_append_json(Link *link, JsonVariant **v) {
+        _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+        sd_dhcp6_option **options = NULL;
+        int r, n_vendor_options;
+
+        assert(link);
+        assert(v);
+
+        if (!link->dhcp6_lease)
+                return 0;
+
+        n_vendor_options = sd_dhcp6_lease_get_vendor_options(link->dhcp6_lease, &options);
+
+        FOREACH_ARRAY(option, options, n_vendor_options) {
+                r = json_variant_append_arrayb(&array,
+                                            JSON_BUILD_OBJECT(
+                                                            JSON_BUILD_PAIR_UNSIGNED("EnterpriseId", (*option)->enterprise_identifier),
+                                                            JSON_BUILD_PAIR_UNSIGNED("SubOptionCode", (*option)->option),
+                                                            JSON_BUILD_PAIR_HEX("SubOptionData", (*option)->data, (*option)->length)));
+                if (r < 0)
+                        return 0;
+        }
+
+        return json_variant_set_field_non_null(v, "VendorSpecificOptions", array);
+}
+
 static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         usec_t ts, t1, t2;
@@ -1125,6 +1152,10 @@ static int dhcp6_client_append_json(Link *link, JsonVariant **v) {
         if (r < 0)
                 return r;
 
+        r = dhcp6_client_vendor_options_append_json(link, &w);
+        if (r < 0)
+                return r;
+
         return json_variant_set_field_non_null(v, "DHCPv6Client", w);
 }
 
index 543dc940255c6984411f848ae667278d9dcbb3dd..e18d57817fff4e41adcce40f06f399bd86f95a76 100644 (file)
@@ -23,6 +23,8 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
+#include "sd-dhcp6-option.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
@@ -77,6 +79,7 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
 int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret);
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret);
 
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
 sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);