diff --git a/src/libsystemd-network/dhcp-client-internal.h b/src/libsystemd-network/dhcp-client-internal.h index fab4ff24aa..8e23f7d931 100644 --- a/src/libsystemd-network/dhcp-client-internal.h +++ b/src/libsystemd-network/dhcp-client-internal.h @@ -8,6 +8,7 @@ #include "network-common.h" #include "sd-forward.h" #include "socket-util.h" +#include "tlv-util.h" typedef enum DHCPState { DHCP_STATE_STOPPED, @@ -65,8 +66,8 @@ struct sd_dhcp_client { uint64_t discover_attempt; uint64_t request_attempt; uint64_t max_discover_attempts; - OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; + TLV *extra_options; + TLV *vendor_options; sd_event_source *timeout_t1; sd_event_source *timeout_t2; sd_event_source *timeout_expire; @@ -90,6 +91,9 @@ int dhcp_client_set_state_callback( void *userdata); int dhcp_client_get_state(sd_dhcp_client *client); +int dhcp_client_set_extra_options(sd_dhcp_client *client, TLV *options); +int dhcp_client_set_vendor_options(sd_dhcp_client *client, TLV *options); + int client_receive_message_raw( sd_event_source *s, int fd, diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 74bc21e95b..75470cb3d6 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -11,7 +11,6 @@ #include "dns-domain.h" #include "hostname-util.h" #include "memory-util.h" -#include "ordered-set.h" #include "string-util.h" #include "strv.h" #include "utf8.h" @@ -107,33 +106,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, *offset += 3 + optlen; break; - case SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION: { - /* When called with raw data (optlen > 0), e.g. from SendOption=, append as a plain TLV. - * The structured handling below expects optval to be an OrderedSet*. */ - if (optlen > 0) - return dhcp_option_append_tlv(options, size, offset, code, optlen, optval); - - OrderedSet *s = (OrderedSet *) optval; - struct sd_dhcp_option *p; - size_t l = 0; - - ORDERED_SET_FOREACH(p, s) - l += p->length + 2; - - if (*offset + l + 2 > size) - return -ENOBUFS; - - options[*offset] = code; - options[*offset + 1] = l; - *offset += 2; - - ORDERED_SET_FOREACH(p, s) { - r = dhcp_option_append_tlv(options, size, offset, p->option, p->length, p->data); - if (r < 0) - return r; - } - break; - } case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: { /* When called with raw data (optlen > 0), e.g. from SendOption=, append as a plain TLV. * The structured handling below expects optval to be an sd_dhcp_server*. */ diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 8b7c856d21..a8e2cbf5c7 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -13,6 +13,7 @@ #include "sd-forward.h" #include "network-common.h" #include "sparse-endian.h" +#include "tlv-util.h" typedef enum DHCPRawOption { DHCP_RAW_OPTION_DATA_UINT8, @@ -53,8 +54,8 @@ typedef struct sd_dhcp_server { char *boot_server_name; char *boot_filename; - OrderedSet *extra_options; - OrderedSet *vendor_options; + TLV *extra_options; + TLV *vendor_options; bool emit_router; struct in_addr router_address; @@ -99,6 +100,9 @@ typedef struct DHCPRequest { triple_timestamp timestamp; } DHCPRequest; +int dhcp_server_set_extra_options(sd_dhcp_server *server, TLV *options); +int dhcp_server_set_vendor_options(sd_dhcp_server *server, TLV *options); + int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp); int dhcp_server_send_packet(sd_dhcp_server *server, diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 45a0eb8674..0b13e925f2 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -19,6 +19,7 @@ #include "event-util.h" #include "hostname-util.h" #include "iovec-util.h" +#include "iovec-wrapper.h" #include "memory-util.h" #include "network-common.h" #include "random-util.h" @@ -502,39 +503,18 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt return 0; } -int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) { - int r; +int dhcp_client_set_extra_options(sd_dhcp_client *client, TLV *options) { + assert(client); + assert(!sd_dhcp_client_is_running(client)); - assert_return(client, -EINVAL); - assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - assert_return(v, -EINVAL); - - r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v); - if (r < 0) - return r; - - sd_dhcp_option_ref(v); - return 0; + return unref_and_replace_new_ref(client->extra_options, options, tlv_ref, tlv_unref); } -int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) { - int r; +int dhcp_client_set_vendor_options(sd_dhcp_client *client, TLV *options) { + assert(client); + assert(!sd_dhcp_client_is_running(client)); - assert_return(client, -EINVAL); - assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - assert_return(v, -EINVAL); - - r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops); - if (r < 0) - return -ENOMEM; - - r = ordered_hashmap_put(client->vendor_options, v, v); - if (r < 0) - return r; - - sd_dhcp_option_ref(v); - - return 1; + return unref_and_replace_new_ref(client->vendor_options, options, tlv_ref, tlv_unref); } int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { @@ -912,7 +892,6 @@ static int client_append_fqdn_option( } static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) { - sd_dhcp_option *j; int r; assert(client); @@ -962,18 +941,31 @@ static int client_append_common_discover_request_options(sd_dhcp_client *client, return r; } - ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, - j->option, j->length, j->data); - if (r < 0) - return r; + if (client->extra_options) { + void *key; + struct iovec_wrapper *iovw; + HASHMAP_FOREACH_KEY(iovw, key, client->extra_options->entries) { + uint32_t tag = PTR_TO_UINT32(key); + + FOREACH_ARRAY(iov, iovw->iovec, iovw->count) { + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + tag, iov->iov_len, iov->iov_base); + if (r < 0) + return r; + } + } } - if (!ordered_hashmap_isempty(client->vendor_options)) { + if (!tlv_isempty(client->vendor_options)) { + _cleanup_(iovec_done) struct iovec iov = {}; + r = tlv_build(client->vendor_options, &iov); + if (r < 0) + return r; + r = dhcp_option_append( &packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, - /* optlen= */ 0, client->vendor_options); + iov.iov_len, iov.iov_base); if (r < 0) return r; } @@ -2306,8 +2298,8 @@ static sd_dhcp_client* dhcp_client_free(sd_dhcp_client *client) { free(client->vendor_class_identifier); free(client->mudurl); client->user_class = strv_free(client->user_class); - ordered_hashmap_free(client->extra_options); - ordered_hashmap_free(client->vendor_options); + tlv_unref(client->extra_options); + tlv_unref(client->vendor_options); free(client->ifname); return mfree(client); } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 7519189229..48c481809f 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -16,11 +16,12 @@ #include "dns-domain.h" #include "errno-util.h" #include "fd-util.h" +#include "hashmap.h" #include "in-addr-util.h" #include "iovec-util.h" +#include "iovec-wrapper.h" #include "memory-util.h" #include "network-common.h" -#include "ordered-set.h" #include "path-util.h" #include "siphash24.h" #include "socket-util.h" @@ -138,8 +139,8 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { server->static_leases_by_address = hashmap_free(server->static_leases_by_address); server->static_leases_by_client_id = hashmap_free(server->static_leases_by_client_id); - ordered_set_free(server->extra_options); - ordered_set_free(server->vendor_options); + tlv_unref(server->extra_options); + tlv_unref(server->vendor_options); free(server->agent_circuit_id); free(server->agent_remote_id); @@ -618,7 +619,6 @@ static int server_send_offer_or_ack( }; _cleanup_free_ DHCPPacket *packet = NULL; - sd_dhcp_option *j; be32_t lease_time; size_t offset; int r; @@ -718,18 +718,31 @@ static int server_send_offer_or_ack( return r; } - ORDERED_SET_FOREACH(j, server->extra_options) { - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - j->option, j->length, j->data); - if (r < 0) - return r; + if (server->extra_options) { + void *key; + struct iovec_wrapper *iovw; + HASHMAP_FOREACH_KEY(iovw, key, server->extra_options->entries) { + uint32_t tag = PTR_TO_UINT32(key); + + FOREACH_ARRAY(iov, iovw->iovec, iovw->count) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + tag, iov->iov_len, iov->iov_base); + if (r < 0) + return r; + } + } } - if (!ordered_set_isempty(server->vendor_options)) { + if (!tlv_isempty(server->vendor_options)) { + _cleanup_(iovec_done) struct iovec iov = {}; + r = tlv_build(server->vendor_options, &iov); + if (r < 0) + return r; + r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION, - /* optlen= */ 0, server->vendor_options); + iov.iov_len, iov.iov_base); if (r < 0) return r; } @@ -1611,33 +1624,14 @@ int sd_dhcp_server_set_router(sd_dhcp_server *server, const struct in_addr *rout return 0; } -int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) { - int r; - - assert_return(server, -EINVAL); - assert_return(v, -EINVAL); - - r = ordered_set_ensure_put(&server->extra_options, &dhcp_option_hash_ops, v); - if (r < 0) - return r; - - sd_dhcp_option_ref(v); - return 0; +int dhcp_server_set_extra_options(sd_dhcp_server *server, TLV *options) { + assert(server); + return unref_and_replace_new_ref(server->extra_options, options, tlv_ref, tlv_unref); } -int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v) { - int r; - - assert_return(server, -EINVAL); - assert_return(v, -EINVAL); - - r = ordered_set_ensure_put(&server->vendor_options, &dhcp_option_hash_ops, v); - if (r < 0) - return r; - - sd_dhcp_option_ref(v); - - return 1; +int dhcp_server_set_vendor_options(sd_dhcp_server *server, TLV *options) { + assert(server); + return unref_and_replace_new_ref(server->vendor_options, options, tlv_ref, tlv_unref); } int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata) { diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 3e7cca991b..d7353d07ed 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -11,12 +11,12 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-locator.h" -#include "dhcp-option.h" #include "dhcp6-option.h" #include "escape.h" #include "extract-word.h" #include "hexdecoct.h" #include "in-addr-prefix-util.h" +#include "iovec-util.h" #include "networkd-dhcp-common.h" #include "networkd-dhcp-prefix-delegation.h" #include "networkd-link.h" @@ -696,7 +696,7 @@ int config_parse_dhcp_user_or_vendor_class( } } -int config_parse_dhcp_send_option( +int config_parse_dhcp6_send_option( const char *unit, const char *filename, unsigned line, @@ -708,17 +708,15 @@ int config_parse_dhcp_send_option( void *data, void *userdata) { - _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL; _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL; - _unused_ _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *old4 = NULL; _unused_ _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *old6 = NULL; uint32_t uint32_data, enterprise_identifier = 0; _cleanup_free_ char *word = NULL, *q = NULL; - OrderedHashmap **options = ASSERT_PTR(data); + OrderedHashmap **dhcp6_options = ASSERT_PTR(data); uint16_t u16, uint16_data; union in_addr_union addr; DHCPOptionDataType type; - uint8_t u8, uint8_data; + uint8_t uint8_data; const void *udata; const char *p; ssize_t sz; @@ -729,12 +727,12 @@ int config_parse_dhcp_send_option( assert(rvalue); if (isempty(rvalue)) { - *options = ordered_hashmap_free(*options); + *dhcp6_options = ordered_hashmap_free(*dhcp6_options); return 0; } p = rvalue; - if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) { + if (streq(lvalue, "SendVendorOption")) { r = extract_first_word(&p, &word, ":", 0); if (r == -ENOMEM) return log_oom(); @@ -762,30 +760,16 @@ int config_parse_dhcp_send_option( return 0; } - if (ltype == AF_INET6) { - r = safe_atou16(word, &u16); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid DHCP option, ignoring assignment: %s", rvalue); - return 0; - } - if (u16 < 1 || u16 >= UINT16_MAX) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue); - return 0; - } - } else { - r = safe_atou8(word, &u8); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid DHCP option, ignoring assignment: %s", rvalue); - return 0; - } - if (u8 < 1 || u8 >= UINT8_MAX) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue); - return 0; - } + r = safe_atou16(word, &u16); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid DHCP option, ignoring assignment: %s", rvalue); + return 0; + } + if (u16 < 1 || u16 >= UINT16_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue); + return 0; } word = mfree(word); @@ -887,49 +871,193 @@ int config_parse_dhcp_send_option( return -EINVAL; } - if (ltype == AF_INET6) { - r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); - return 0; - } - - r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops); - if (r < 0) - return log_oom(); - - /* Overwrite existing option */ - old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16)); - r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); - return 0; - } - TAKE_PTR(opt6); - } else { - r = sd_dhcp_option_new(u8, udata, sz, &opt4); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); - return 0; - } - - r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops); - if (r < 0) - return log_oom(); - - /* Overwrite existing option */ - old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8)); - r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); - return 0; - } - TAKE_PTR(opt4); + r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; } + + r = ordered_hashmap_ensure_allocated(dhcp6_options, &dhcp6_option_hash_ops); + if (r < 0) + return log_oom(); + + /* Overwrite existing option */ + old6 = ordered_hashmap_get(*dhcp6_options, UINT_TO_PTR(u16)); + r = ordered_hashmap_replace(*dhcp6_options, UINT_TO_PTR(u16), opt6); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; + } + TAKE_PTR(opt6); + + return 0; +} + +int config_parse_dhcp_option( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + struct iovec *iov = ASSERT_PTR(data); + bool check_length = ltype; + int r; + + if (isempty(rvalue)) { + iovec_done(iov); + return 0; + } + + _cleanup_free_ char *word = NULL; + const char *p = rvalue; + r = extract_first_word(&p, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0 || isempty(p)) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + DHCPOptionDataType type = dhcp_option_data_type_from_string(word); + if (type < 0) + return log_syntax_parse_error(unit, filename, line, type, lvalue, rvalue); + + switch (type) { + case DHCP_OPTION_DATA_UINT8:{ + uint8_t u; + + r = safe_atou8(p, &u); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u))); + if (r < 0) + return log_oom(); + + return 1; + } + case DHCP_OPTION_DATA_UINT16:{ + uint16_t u; + + r = safe_atou16(p, &u); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + u = htobe16(u); + r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u))); + if (r < 0) + return log_oom(); + + return 1; + } + case DHCP_OPTION_DATA_UINT32: { + uint32_t u; + + r = safe_atou32(p, &u); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + u = htobe32(u); + r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&u, sizeof(u))); + if (r < 0) + return log_oom(); + + return 1; + } + case DHCP_OPTION_DATA_IPV4ADDRESS: { + union in_addr_union a; + + r = in_addr_from_string(AF_INET, p, &a); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&a.in, sizeof(a.in))); + if (r < 0) + return log_oom(); + + return 1; + } + case DHCP_OPTION_DATA_IPV6ADDRESS: { + union in_addr_union a; + + r = in_addr_from_string(AF_INET6, p, &a); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + r = iovec_done_and_memdup(iov, &IOVEC_MAKE(&a.in6, sizeof(a.in6))); + if (r < 0) + return log_oom(); + + return 1; + } + case DHCP_OPTION_DATA_STRING: { + _cleanup_free_ char *s = NULL; + ssize_t sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &s); + if (sz < 0) + return log_syntax_parse_error(unit, filename, line, sz, lvalue, rvalue); + if (check_length && sz > UINT8_MAX) + return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue); + + iovec_done(iov); + *iov = IOVEC_MAKE(TAKE_PTR(s), sz); + return 1; + } + default: + return -EINVAL; + } +} + +int config_parse_dhcp_option_tlv( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + TLV *options = ASSERT_PTR(data); + int r; + + if (isempty(rvalue)) { + tlv_done(options); + return 0; + } + + _cleanup_free_ char *word = NULL; + const char *p = rvalue; + r = extract_first_word(&p, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0 || isempty(p)) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + uint8_t code; + r = safe_atou8(word, &code); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + if (code < 1 || code >= UINT8_MAX) + return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue); + + _cleanup_(iovec_done) struct iovec iov = {}; + r = config_parse_dhcp_option(unit, filename, line, section, section_line, lvalue, ltype, p, &iov, userdata); + if (r <= 0) + return r; + + r = tlv_append_iov(options, code, &iov); + if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store '%s=%s', ignoring assignment: %m", lvalue, rvalue); + return 0; } diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 9eb9331923..0b88d7790c 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -82,7 +82,9 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_table); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_or_vendor_class); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_option); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_send_option); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_option); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_option_tlv); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options); CONFIG_PARSER_PROTOTYPE(config_parse_duid_type); CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_type); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index c88488258f..9903c6b9d3 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -7,6 +7,7 @@ #include "conf-parser.h" #include "dhcp-protocol.h" +#include "dhcp-server-internal.h" #include "dhcp-server-lease-internal.h" #include "dns-domain.h" #include "errno-util.h" @@ -567,7 +568,6 @@ static int dhcp_server_set_domain(Link *link) { static int dhcp4_server_configure(Link *link) { bool acquired_uplink = false; - sd_dhcp_option *p; DHCPStaticLease *static_lease; Link *uplink = NULL; Address *address; @@ -721,21 +721,13 @@ static int dhcp4_server_configure(Link *link) { else if (r < 0) return log_link_error_errno(link, r, "Failed to set domain name for DHCP server: %m"); - ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) { - r = sd_dhcp_server_add_option(link->dhcp_server, p); - if (r == -EEXIST) - continue; - if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); - } + r = dhcp_server_set_extra_options(link->dhcp_server, &link->network->dhcp_server_extra_options); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 extra options: %m"); - ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options) { - r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p); - if (r == -EEXIST) - continue; - if (r < 0) - return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); - } + r = dhcp_server_set_vendor_options(link->dhcp_server, &link->network->dhcp_server_vendor_options); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 vendor options: %m"); HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) { r = sd_dhcp_server_set_static_lease( diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index dc6b78e326..7304a049b6 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1477,7 +1477,6 @@ static bool link_dhcp4_ipv6_only_mode(Link *link) { } static int dhcp4_configure(Link *link) { - sd_dhcp_option *send_option; void *request_options; int r; @@ -1619,21 +1618,13 @@ static int dhcp4_configure(Link *link) { return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for '%u': %m", option); } - ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options) { - r = sd_dhcp_client_add_option(link->dhcp_client, send_option); - if (r == -EEXIST) - continue; - if (r < 0) - return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m"); - } + r = dhcp_client_set_extra_options(link->dhcp_client, &link->network->dhcp_extra_options); + if (r < 0) + return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set extra options: %m"); - ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options) { - r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option); - if (r == -EEXIST) - continue; - if (r < 0) - return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m"); - } + r = dhcp_client_set_vendor_options(link->dhcp_client, &link->network->dhcp_vendor_options); + if (r < 0) + return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set vendor options: %m"); r = dhcp4_set_hostname(link); if (r < 0) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index aaf974e312..0f1c4e57c4 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -293,8 +293,8 @@ DHCPv4.DenyList, config_parse_in_addr_prefixes, DHCPv4.AllowList, config_parse_in_addr_prefixes, AF_INET, offsetof(Network, dhcp_allow_listed_ip) DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, dhcp_ip_service_type) DHCPv4.SocketPriority, config_parse_dhcp_socket_priority, 0, 0 -DHCPv4.SendOption, config_parse_dhcp_send_option, AF_INET, offsetof(Network, dhcp_client_send_options) -DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options) +DHCPv4.SendOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_extra_options) +DHCPv4.SendVendorOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_vendor_options) DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu) DHCPv4.InitialCongestionWindow, config_parse_tcp_window, 0, offsetof(Network, dhcp_initial_congestion_window) DHCPv4.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, offsetof(Network, dhcp_advertised_receive_window) @@ -320,11 +320,11 @@ DHCPv6.Hostname, config_parse_hostname, DHCPv6.RequestOptions, config_parse_dhcp_request_options, AF_INET6, 0 DHCPv6.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_user_class) DHCPv6.VendorClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_vendor_class) -DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options) +DHCPv6.SendVendorOption, config_parse_dhcp6_send_option, 0, offsetof(Network, dhcp6_client_send_vendor_options) DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_prefix_hint, 0, 0 DHCPv6.UnassignedSubnetPolicy, config_parse_dhcp_pd_prefix_route_type, 0, offsetof(Network, dhcp6_pd_prefix_route_type) DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_client_start_mode) -DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options) +DHCPv6.SendOption, config_parse_dhcp6_send_option, 0, offsetof(Network, dhcp6_client_send_options) DHCPv6.IAID, config_parse_iaid, AF_INET6, 0 DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid) DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid) @@ -387,8 +387,8 @@ DHCPServer.EmitDomain, config_parse_bool, DHCPServer.Domain, config_parse_dns_name, 0, offsetof(Network, dhcp_server_domain) DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) -DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options) -DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options) +DHCPServer.SendOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_server_extra_options) +DHCPServer.SendVendorOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_server_vendor_options) DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface) DHCPServer.BootServerAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_boot_server_address) DHCPServer.BootServerName, config_parse_dns_name, 0, offsetof(Network, dhcp_server_boot_server_name) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 3ffe1640e7..0397e77f81 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -397,6 +397,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_use_gateway = -1, .dhcp_send_hostname = true, .dhcp_send_release = true, + .dhcp_extra_options = TLV_INIT(TLV_DHCP4), + .dhcp_vendor_options = TLV_INIT(TLV_DHCP4_SUBOPTION), .dhcp_route_metric = DHCP_ROUTE_METRIC, .dhcp_use_rapid_commit = -1, .dhcp_client_identifier = _DHCP_CLIENT_ID_INVALID, @@ -436,6 +438,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_server_emit_router = true, .dhcp_server_emit_timezone = true, .dhcp_server_rapid_commit = true, + .dhcp_server_extra_options = TLV_INIT(TLV_DHCP4), + .dhcp_server_vendor_options = TLV_INIT(TLV_DHCP4_SUBOPTION), .dhcp_server_persist_leases = _DHCP_SERVER_PERSIST_LEASES_INVALID, .router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC, @@ -771,8 +775,8 @@ static Network *network_free(Network *network) { free(network->dhcp_server_uplink_name); for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++) free(network->dhcp_server_emit[t].addresses); - ordered_hashmap_free(network->dhcp_server_send_options); - ordered_hashmap_free(network->dhcp_server_send_vendor_options); + tlv_done(&network->dhcp_server_extra_options); + tlv_done(&network->dhcp_server_vendor_options); free(network->dhcp_server_local_lease_domain); /* DHCP client */ @@ -784,8 +788,8 @@ static Network *network_free(Network *network) { set_free(network->dhcp_allow_listed_ip); strv_free(network->dhcp_user_class); set_free(network->dhcp_request_options); - ordered_hashmap_free(network->dhcp_client_send_options); - ordered_hashmap_free(network->dhcp_client_send_vendor_options); + tlv_done(&network->dhcp_extra_options); + tlv_done(&network->dhcp_vendor_options); free(network->dhcp_netlabel); nft_set_context_clear(&network->dhcp_nft_set_context); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 9a36c312f8..26012612a1 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -23,6 +23,7 @@ #include "networkd-sysctl.h" #include "networkd-wwan-bus.h" #include "resolve-util.h" +#include "tlv-util.h" typedef enum KeepConfiguration { KEEP_CONFIGURATION_NO = 0, @@ -168,8 +169,8 @@ typedef struct Network { Set *dhcp_deny_listed_ip; Set *dhcp_allow_listed_ip; Set *dhcp_request_options; - OrderedHashmap *dhcp_client_send_options; - OrderedHashmap *dhcp_client_send_vendor_options; + TLV dhcp_extra_options; + TLV dhcp_vendor_options; char *dhcp_netlabel; NFTSetContext dhcp_nft_set_context; @@ -226,8 +227,8 @@ typedef struct Network { usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; uint32_t dhcp_server_pool_offset; uint32_t dhcp_server_pool_size; - OrderedHashmap *dhcp_server_send_options; - OrderedHashmap *dhcp_server_send_vendor_options; + TLV dhcp_server_extra_options; + TLV dhcp_server_vendor_options; struct in_addr dhcp_server_boot_server_address; char *dhcp_server_boot_server_name; char *dhcp_server_boot_filename; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 378271aef8..f7c5602b66 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -150,9 +150,6 @@ int sd_dhcp_client_set_bootp( int bootp); int sd_dhcp_client_set_send_release(sd_dhcp_client *client, int enable); -int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v); -int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v); - int sd_dhcp_client_is_running(sd_dhcp_client *client); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index ef13776201..c17cd6b7b6 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -76,8 +76,6 @@ int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], s int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n); int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n); -int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v); -int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_set_static_lease( sd_dhcp_server *server, const struct in_addr *address,