diff --git a/man/systemd.network.xml b/man/systemd.network.xml index b38ad54e03..e87ca1f287 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1642,13 +1642,15 @@ SendOption= - Send an arbitrary option in the DHCPv4 request. Takes a DHCP option number and an arbitrary - data string separated with a colon - (option:value). The - option number must be an interger in the range 1..254. Special characters in the data string may - be escaped using + Send an arbitrary 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 interger 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 option can be specified multiple times. If an empty string is specified, + escapes. This option 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/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index bbf10affc4..7e42a6edd4 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -273,3 +273,13 @@ static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); + +static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = { + [DHCP_OPTION_DATA_UINT8] = "uint8", + [DHCP_OPTION_DATA_UINT16] = "uint16", + [DHCP_OPTION_DATA_UINT32] = "uint32", + [DHCP_OPTION_DATA_STRING] = "string", + [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType); diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 8f280190b9..b194e0bfb7 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -15,6 +15,16 @@ typedef enum DHCPUseDomains { _DHCP_USE_DOMAINS_INVALID = -1, } DHCPUseDomains; +typedef enum DHCPOptionDataType { + DHCP_OPTION_DATA_UINT8, + DHCP_OPTION_DATA_UINT16, + DHCP_OPTION_DATA_UINT32, + DHCP_OPTION_DATA_STRING, + DHCP_OPTION_DATA_IPV4ADDRESS, + _DHCP_OPTION_DATA_MAX, + _DHCP_OPTION_DATA_INVALID, +} DHCPOptionDataType; + typedef struct DUID { /* Value of Type in [DHCP] section */ DUIDType type; @@ -27,6 +37,9 @@ typedef struct DUID { const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; +const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_; +DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_; + CONFIG_PARSER_PROTOTYPE(config_parse_dhcp); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 1ae1d17057..79ac325926 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -480,16 +480,6 @@ int config_parse_dhcp_server_sip( } } -static const char * const dhcp_raw_option_data_type_table[_DHCP_RAW_OPTION_DATA_MAX] = { - [DHCP_RAW_OPTION_DATA_UINT8] = "uint8", - [DHCP_RAW_OPTION_DATA_UINT16] = "uint16", - [DHCP_RAW_OPTION_DATA_UINT32] = "uint32", - [DHCP_RAW_OPTION_DATA_STRING] = "string", - [DHCP_RAW_OPTION_DATA_IPV4ADDRESS] = "ipv4address", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp_raw_option_data_type, DHCPRawOption); - int config_parse_dhcp_server_raw_option_data( const char *unit, const char *filename, @@ -505,11 +495,11 @@ int config_parse_dhcp_server_raw_option_data( _cleanup_(sd_dhcp_raw_option_unrefp) sd_dhcp_raw_option *opt = NULL, *old = NULL; _cleanup_free_ char *word = NULL, *q = NULL; union in_addr_union addr; + DHCPOptionDataType type; Network *network = data; uint16_t uint16_data; uint32_t uint32_data; uint8_t uint8_data; - DHCPRawOption type; const char *p; void *udata; ssize_t sz; @@ -559,16 +549,15 @@ int config_parse_dhcp_server_raw_option_data( return 0; } - r = dhcp_raw_option_data_type_from_string(word); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + type = dhcp_option_data_type_from_string(word); + if (type < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DHCP server send data type, ignoring assignment: %s", p); return 0; } - type = r; switch(type) { - case DHCP_RAW_OPTION_DATA_UINT8:{ + case DHCP_OPTION_DATA_UINT8:{ r = safe_atou8(p, &uint8_data); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, @@ -580,7 +569,7 @@ int config_parse_dhcp_server_raw_option_data( sz = sizeof(uint8_t); break; } - case DHCP_RAW_OPTION_DATA_UINT16:{ + case DHCP_OPTION_DATA_UINT16:{ r = safe_atou16(p, &uint16_data); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, @@ -592,7 +581,7 @@ int config_parse_dhcp_server_raw_option_data( sz = sizeof(uint16_t); break; } - case DHCP_RAW_OPTION_DATA_UINT32: { + case DHCP_OPTION_DATA_UINT32: { r = safe_atou32(p, &uint32_data); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, @@ -605,7 +594,7 @@ int config_parse_dhcp_server_raw_option_data( break; } - case DHCP_RAW_OPTION_DATA_IPV4ADDRESS: { + case DHCP_OPTION_DATA_IPV4ADDRESS: { r = in_addr_from_string(AF_INET, p, &addr); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, @@ -617,7 +606,7 @@ int config_parse_dhcp_server_raw_option_data( sz = sizeof(addr.in.s_addr); break; } - case DHCP_RAW_OPTION_DATA_STRING: + case DHCP_OPTION_DATA_STRING: sz = cunescape(p, 0, &q); if (sz < 0) { log_syntax(unit, LOG_ERR, filename, line, sz, diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index c30162dd5b..e0a7799b1c 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -7,19 +7,6 @@ typedef struct Link Link; -typedef enum DHCPRawOption { - DHCP_RAW_OPTION_DATA_UINT8, - DHCP_RAW_OPTION_DATA_UINT16, - DHCP_RAW_OPTION_DATA_UINT32, - DHCP_RAW_OPTION_DATA_STRING, - DHCP_RAW_OPTION_DATA_IPV4ADDRESS, - _DHCP_RAW_OPTION_DATA_MAX, - _DHCP_RAW_OPTION_DATA_INVALID, -} DHCPRawOption; - -const char *dhcp_raw_option_data_type_to_string(DHCPRawOption d) _const_; -DHCPRawOption dhcp_raw_option_data_type_from_string(const char *d) _pure_; - int dhcp4_server_configure(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 947853e2c4..7504e4eb27 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1580,9 +1580,14 @@ int config_parse_dhcp_send_option( _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt = NULL, *old = NULL; _cleanup_free_ char *word = NULL, *q = NULL; + union in_addr_union addr; + DHCPOptionDataType type; Network *network = data; + uint8_t u, uint8_data; + uint16_t uint16_data; + uint32_t uint32_data; const char *p; - uint8_t u; + void *udata; ssize_t sz; int r; @@ -1618,14 +1623,87 @@ int config_parse_dhcp_send_option( return 0; } - sz = cunescape(p, 0, &q); - if (sz < 0) { - log_syntax(unit, LOG_ERR, filename, line, sz, - "Failed to decode option data, ignoring assignment: %s", p); + free(word); + r = extract_first_word(&p, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid DHCP send option, ignoring assignment: %s", rvalue); return 0; } - r = sd_dhcp_option_new(u, q, sz, &opt); + type = dhcp_option_data_type_from_string(word); + if (type < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid DHCP send data type, ignoring assignment: %s", p); + return 0; + } + + switch(type) { + case DHCP_OPTION_DATA_UINT8:{ + r = safe_atou8(p, &uint8_data); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCPv4 uint8 data, ignoring assignment: %s", p); + return 0; + } + + udata = &uint8_data; + sz = sizeof(uint8_t); + break; + } + case DHCP_OPTION_DATA_UINT16:{ + r = safe_atou16(p, &uint16_data); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCPv4 uint16 data, ignoring assignment: %s", p); + return 0; + } + + udata = &uint16_data; + sz = sizeof(uint16_t); + break; + } + case DHCP_OPTION_DATA_UINT32: { + r = safe_atou32(p, &uint32_data); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCPv4 uint32 data, ignoring assignment: %s", p); + return 0; + } + + udata = &uint32_data; + sz = sizeof(uint32_t); + + break; + } + case DHCP_OPTION_DATA_IPV4ADDRESS: { + r = in_addr_from_string(AF_INET, p, &addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCPv4 ipv4address data, ignoring assignment: %s", p); + return 0; + } + + udata = &addr.in; + sz = sizeof(addr.in.s_addr); + break; + } + case DHCP_OPTION_DATA_STRING: + sz = cunescape(p, 0, &q); + if (sz < 0) { + log_syntax(unit, LOG_ERR, filename, line, sz, + "Failed to decode option data, ignoring assignment: %s", p); + } + + udata = q; + break; + default: + return -EINVAL; + } + + r = sd_dhcp_option_new(u, udata, sz, &opt); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to store DHCP send option '%s', ignoring assignment: %m", rvalue); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7fca93dccb..a99ef94d32 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -175,7 +175,7 @@ DHCPv4.ListenPort, config_parse_uint16, DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCPv4.IPServiceType, config_parse_ip_service_type, 0, offsetof(Network, ip_service_type) -DHCPv4.SendOption, config_parse_dhcp_send_option, 0, 0 +DHCPv4.SendOption, config_parse_dhcp_send_option, 0, 0 DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns) DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp) DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)