From 6b44099b3baff64af1ef58db8e38ecddc8070e9b Mon Sep 17 00:00:00 2001 From: Nandakumar Raghavan Date: Wed, 20 Sep 2023 14:33:38 +0000 Subject: [PATCH] networkd: Add DHCP vendor specific options to dbus API 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 | 3 + src/libsystemd-network/sd-dhcp6-lease.c | 86 +++++++++++++++++++ src/libsystemd-network/test-dhcp6-client.c | 23 +++++ src/network/networkd-json.c | 31 +++++++ src/systemd/sd-dhcp6-lease.h | 3 + 5 files changed, 146 insertions(+) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 0a2434b5ab0..e76a108f60e 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -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); diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 3608586e5dc..69d5a11f57c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -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); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 109b23c6397..ae3cdb86320 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -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) { diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index ef3153642b2..6e53d368d29 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -3,6 +3,7 @@ #include #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); } diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 543dc940255..e18d57817ff 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -23,6 +23,8 @@ #include #include +#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); -- 2.47.3