diff --git a/man/systemd.network.xml b/man/systemd.network.xml index b13fcd1893..5457e668dd 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1635,7 +1635,22 @@ SendOption= - Send an arbitrary option in the DHCPv4 request. Takes a DHCP option number, data type + Send an arbitrary raw option in the DHCPv4 request. Takes a DHCP option number, data type + and data separated with a colon + (option:type:value). + The option number must be an integer in the range 1..254. The type takes one of uint8, + uint16, uint32, ipv4address, or + string. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + + + SendVendorOption= + + Send an arbitrary vendor option in the DHCPv4 request. Takes a DHCP option number, data type and data separated with a colon (option:type:value). The option number must be an integer in the range 1..254. The type takes one of uint8, @@ -1926,6 +1941,20 @@ + + SendVendorOption= + + Send a vendor option with value via DHCPv4 server. Takes a DHCP option number, data type + and data (option:type:value). + The option number is an integer in the range 1..254. The type takes one of uint8, + uint16, uint32, ipv4address, or + string. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 42c3ceb8b1..41901894f5 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -58,7 +58,8 @@ struct sd_dhcp_server { struct in_addr *ntp, *dns, *sip; unsigned n_ntp, n_dns, n_sip; - OrderedHashmap *raw_option; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; bool emit_router; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 4122d08d96..82553e79ca 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -89,7 +89,8 @@ struct sd_dhcp_client { usec_t start_time; uint64_t attempt; uint64_t max_attempts; - OrderedHashmap *options; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -540,17 +541,17 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt return 0; } -int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) { +int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) { int r; assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops); + r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops); if (r < 0) return r; - r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v); + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); if (r < 0) return r; @@ -558,6 +559,25 @@ int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) { return 0; } +int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) { + int r; + + assert_return(client, -EINVAL); + 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; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); @@ -884,13 +904,22 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - ORDERED_HASHMAP_FOREACH(j, client->options, i) { + ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, j->option, j->length, j->data); if (r < 0) return r; } + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp_option_append( + &discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_VENDOR_SPECIFIC, + ordered_hashmap_size(client->vendor_options), client->vendor_options); + if (r < 0) + return r; + } + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -2073,7 +2102,8 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { free(client->hostname); free(client->vendor_class_identifier); client->user_class = strv_free(client->user_class); - ordered_hashmap_free(client->options); + ordered_hashmap_free(client->extra_options); + ordered_hashmap_free(client->vendor_options); return mfree(client); } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 546b5d02c4..a0b779bc09 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -143,7 +143,8 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { hashmap_free(server->leases_by_client_id); - ordered_hashmap_free(server->raw_option); + ordered_hashmap_free(server->extra_options); + ordered_hashmap_free(server->vendor_options); free(server->bound_leases); return mfree(server); @@ -455,6 +456,8 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) { _cleanup_free_ DHCPPacket *packet = NULL; be32_t lease_time; + sd_dhcp_option *j; + Iterator i; size_t offset; int r; @@ -519,11 +522,18 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, return r; } - if (!ordered_hashmap_isempty(server->raw_option)) { + ORDERED_HASHMAP_FOREACH(j, server->extra_options, i) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + j->option, j->length, j->data); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(server->vendor_options)) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, SD_DHCP_OPTION_VENDOR_SPECIFIC, - ordered_hashmap_size(server->raw_option), server->raw_option); + ordered_hashmap_size(server->vendor_options), server->vendor_options); if (r < 0) return r; } @@ -1188,11 +1198,29 @@ int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) { assert_return(server, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&server->raw_option, &dhcp_option_hash_ops); + r = ordered_hashmap_ensure_allocated(&server->extra_options, &dhcp_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(server->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; + + sd_dhcp_option_ref(v); + return 0; +} + +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_hashmap_ensure_allocated(&server->vendor_options, &dhcp_option_hash_ops); if (r < 0) return -ENOMEM; - r = ordered_hashmap_put(server->raw_option, v, v); + r = ordered_hashmap_put(server->vendor_options, v, v); if (r < 0) return r; diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index bee75a6930..2ec742b5e3 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -312,6 +312,14 @@ int dhcp4_server_configure(Link *link) { return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); } + ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) { + 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"); + } + if (!sd_dhcp_server_is_running(link->dhcp_server)) { r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 64190375a4..83fb25264a 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1430,7 +1430,17 @@ int dhcp4_configure(Link *link) { } ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options, i) { - r = sd_dhcp_client_set_dhcp_option(link->dhcp_client, send_option); + r = sd_dhcp_client_add_option(link->dhcp_client, send_option); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m"); + } + + ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options, i) { + r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option); + if (r == -EEXIST) + continue; if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m"); } diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index c75a07e4f3..fd996327a5 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -184,6 +184,7 @@ DHCPv4.SendDecline, config_parse_bool, DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, ip_service_type) DHCPv4.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_options) +DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options) DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu) DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns) DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp) @@ -211,6 +212,7 @@ DHCPServer.EmitTimezone, config_parse_bool, DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) 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) Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 248172f8a2..e7ead446c7 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -723,7 +723,9 @@ static Network *network_free(Network *network) { set_free_free(network->dnssec_negative_trust_anchors); ordered_hashmap_free(network->dhcp_client_send_options); + ordered_hashmap_free(network->dhcp_client_send_vendor_options); ordered_hashmap_free(network->dhcp_server_send_options); + ordered_hashmap_free(network->dhcp_server_send_vendor_options); ordered_hashmap_free(network->ipv6_tokens); return mfree(network); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b687282124..f747ccaf10 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -121,7 +121,9 @@ struct Network { Set *dhcp_black_listed_ip; Set *dhcp_request_options; OrderedHashmap *dhcp_client_send_options; + OrderedHashmap *dhcp_client_send_vendor_options; OrderedHashmap *dhcp_server_send_options; + OrderedHashmap *dhcp_server_send_vendor_options; /* DHCPv6 Client support*/ bool dhcp6_use_dns; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 0002ea0e59..9dd562fa43 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -179,7 +179,8 @@ int sd_dhcp_client_set_service_type( sd_dhcp_client *client, int type); -int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v); +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_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 5950506c9b..55272b5164 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -53,6 +53,7 @@ int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], u int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); 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_max_lease_time(sd_dhcp_server *server, uint32_t t); int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index b3465ceb8c..4418fc0149 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -100,6 +100,7 @@ SendRelease= MaxAttempts= IPServiceType= SendOption= +SendVendorOption= SendDecline= RouteMTUBytes= [DHCPv6] @@ -273,6 +274,7 @@ DefaultLeaseTimeSec= EmitTimezone= DNS= SendOption= +SendVendorOption= [NextHop] Id= Gateway=