summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSusant Sahani <ssahani@vmware.com>2019-09-20 04:22:17 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2019-10-31 01:03:43 +0100
commit564ca98484d0a5d2ef974e7872c669b926e159c5 (patch)
treec9c56dfce280f1edeff53b4d3609f973caae0793
parentadd other worthy news (diff)
downloadsystemd-564ca98484d0a5d2ef974e7872c669b926e159c5.tar.xz
systemd-564ca98484d0a5d2ef974e7872c669b926e159c5.zip
networkd: dhcp server Support Vendor specific 43
Implementes https://tools.ietf.org/html/rfc2132 ``` [DHCPServer] SendRawOption=26:uint32:1400 SendRawOption=23:uint8:10 ``` Frame 448: 350 bytes on wire (2800 bits), 350 bytes captured (2800 bits) on interface 0 Linux cooked capture Internet Protocol Version 4, Src: 192.168.5.1, Dst: 192.168.5.11 User Datagram Protocol, Src Port: 67, Dst Port: 68 Dynamic Host Configuration Protocol (ACK) Message type: Boot Reply (2) Hardware type: Ethernet (0x01) Hardware address length: 6 Hops: 0 Transaction ID: 0x71f8de9d Seconds elapsed: 0 Bootp flags: 0x0000 (Unicast) Client IP address: 0.0.0.0 Your (client) IP address: 192.168.5.11 Next server IP address: 0.0.0.0 Relay agent IP address: 0.0.0.0 Client MAC address: 1e:04:f8:b8:2f:d4 (1e:04:f8:b8:2f:d4) Client hardware address padding: 00000000000000000000 Server host name not given Boot file name not given Magic cookie: DHCP Option: (53) DHCP Message Type (ACK) Length: 1 DHCP: ACK (5) Option: (51) IP Address Lease Time Length: 4 IP Address Lease Time: (3600s) 1 hour Option: (1) Subnet Mask (255.255.255.0) Length: 4 Subnet Mask: 255.255.255.0 Option: (3) Router Length: 4 Router: 192.168.5.1 Option: (6) Domain Name Server Length: 4 Domain Name Server: 192.168.5.1 Option: (42) Network Time Protocol Servers Length: 4 Network Time Protocol Server: 192.168.5.1 Option: (101) TCode Length: 13 TZ TCode: Europe/Berlin Option: (43) Vendor-Specific Information Length: 9 Value: 1701311a0431343030 Option: (54) DHCP Server Identifier (192.168.5.1) Length: 4 DHCP Server Identifier: 192.168.5.1 Option: (255) End Option End: 255 ```
-rw-r--r--man/systemd.network.xml13
-rw-r--r--src/libsystemd-network/dhcp-option.c27
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h21
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c72
-rw-r--r--src/network/networkd-dhcp-server.c194
-rw-r--r--src/network/networkd-dhcp-server.h16
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c12
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/systemd/sd-dhcp-server.h10
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network1
11 files changed, 361 insertions, 8 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 538f4b8e7b..0d49bf9589 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1902,6 +1902,19 @@
<filename>/etc/localtime</filename> symlink.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SendRawOption=</varname></term>
+ <listitem>
+ <para>Send a raw option with value via DHCPv4 server. Takes a DHCP option, data type and data
+ (option:type:value). The option ranges [1-254]. The type takes one of <literal>uint8</literal>,
+ <literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, or <literal>string</literal>.
+ Special characters in the data string may be escaped using
+ <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+ escapes</ulink>. This option can be specified multiple times. If an empty string is specified, then all
+ options specified earlier are cleared. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index d2f1f5d806..78baeb7333 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "dhcp-internal.h"
+#include "dhcp-server-internal.h"
#include "memory-util.h"
#include "strv.h"
#include "utf8.h"
@@ -77,6 +78,32 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
*offset += 3 + optlen;
break;
+ case SD_DHCP_OPTION_VENDOR_SPECIFIC: {
+ OrderedHashmap *s = (OrderedHashmap *) optval;
+ struct sd_dhcp_raw_option *p;
+ size_t l = 0;
+ Iterator i;
+
+ ORDERED_HASHMAP_FOREACH(p, s, i)
+ l += p->length + 2;
+
+ if (*offset + l + 2 > size)
+ return -ENOBUFS;
+
+ options[*offset] = code;
+ options[*offset + 1] = l;
+
+ *offset += 2;
+
+ ORDERED_HASHMAP_FOREACH(p, s, i) {
+ options[*offset] = p->type;
+ options[*offset + 1] = p->length;
+ memcpy(&options[*offset + 2], p->data, p->length);
+ *offset += 2 + p->length;
+ }
+
+ break;
+ }
default:
if (*offset + 2 + optlen > size)
return -ENOBUFS;
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 0a64082cd1..e537d9e622 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -13,6 +13,16 @@
#include "log.h"
#include "time-util.h"
+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;
+
typedef struct DHCPClientId {
size_t length;
void *data;
@@ -27,6 +37,15 @@ typedef struct DHCPLease {
usec_t expiration;
} DHCPLease;
+struct sd_dhcp_raw_option {
+ unsigned n_ref;
+
+ uint8_t type;
+ uint8_t length;
+
+ void *data;
+};
+
struct sd_dhcp_server {
unsigned n_ref;
@@ -48,6 +67,8 @@ struct sd_dhcp_server {
struct in_addr *ntp, *dns, *sip;
unsigned n_ntp, n_dns, n_sip;
+ OrderedHashmap *raw_option;
+
bool emit_router;
Hashmap *leases_by_client_id;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index bba82c21dd..9e1a7698fc 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -127,6 +127,46 @@ int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
DHCPLease, dhcp_lease_free);
+static sd_dhcp_raw_option* raw_option_free(sd_dhcp_raw_option *i) {
+ if (!i)
+ return NULL;
+
+ free(i->data);
+ return mfree(i);
+}
+
+_public_ int sd_dhcp_raw_option_new(uint8_t type, char *data, size_t length, sd_dhcp_raw_option **ret) {
+ sd_dhcp_raw_option *p;
+
+ assert_return(ret, -EINVAL);
+
+ p = new(sd_dhcp_raw_option, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (sd_dhcp_raw_option) {
+ .n_ref = 1,
+ .data = memdup(data, length),
+ .length = length,
+ .type = type,
+ };
+
+ if (!p->data)
+ return -ENOMEM;
+
+ *ret = p;
+ return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_raw_option, sd_dhcp_raw_option, raw_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp_raw_options_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ sd_dhcp_raw_option,
+ sd_dhcp_raw_option_unref);
+
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server);
@@ -143,6 +183,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);
+
free(server->bound_leases);
return mfree(server);
}
@@ -452,8 +494,8 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
be32_t address) {
_cleanup_free_ DHCPPacket *packet = NULL;
- size_t offset;
be32_t lease_time;
+ size_t offset;
int r;
r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
@@ -517,6 +559,15 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
return r;
}
+ if (!ordered_hashmap_isempty(server->raw_option)) {
+ 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);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
if (r < 0)
return r;
@@ -1170,3 +1221,22 @@ int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
return 1;
}
+
+int sd_dhcp_server_add_raw_option(sd_dhcp_server *server, sd_dhcp_raw_option *v) {
+ int r;
+
+ assert_return(server, -EINVAL);
+ assert_return(v, -EINVAL);
+
+ r = ordered_hashmap_ensure_allocated(&server->raw_option, &dhcp_raw_options_hash_ops);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = ordered_hashmap_put(server->raw_option, v, v);
+ if (r < 0)
+ return r;
+
+ sd_dhcp_raw_option_ref(v);
+
+ return 1;
+}
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
index efb82d7e9a..77eb46341c 100644
--- a/src/network/networkd-dhcp-server.c
+++ b/src/network/networkd-dhcp-server.c
@@ -2,11 +2,15 @@
#include "sd-dhcp-server.h"
+#include "escape.h"
#include "networkd-dhcp-server.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "parse-util.h"
#include "strv.h"
+#include "string-table.h"
+#include "string-util.h"
static Address* link_find_dhcp_server_address(Link *link) {
Address *address;
@@ -187,9 +191,11 @@ static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
}
int dhcp4_server_configure(Link *link) {
- Address *address;
- Link *uplink = NULL;
bool acquired_uplink = false;
+ sd_dhcp_raw_option *p;
+ Link *uplink = NULL;
+ Address *address;
+ Iterator i;
int r;
address = link_find_dhcp_server_address(link);
@@ -298,6 +304,15 @@ int dhcp4_server_configure(Link *link) {
if (r < 0)
return r;
}
+
+ ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_raw_options, i) {
+ r = sd_dhcp_server_add_raw_option(link->dhcp_server, p);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+
if (!sd_dhcp_server_is_running(link->dhcp_server)) {
r = sd_dhcp_server_start(link->dhcp_server);
if (r < 0)
@@ -464,3 +479,178 @@ int config_parse_dhcp_server_sip(
n->dhcp_server_sip = m;
}
}
+
+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,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _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;
+ 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;
+ uint8_t u;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->dhcp_server_raw_options = ordered_hashmap_free(network->dhcp_server_raw_options);
+ return 0;
+ }
+
+ p = rvalue;
+ 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 server send raw option, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = safe_atou8(word, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCP server send raw option type, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+ if (u < 1 || u >= 255) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid DHCP server send raw option, valid range is 1-254, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ 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 server send raw option, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = dhcp_raw_option_data_type_from_string(word);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Invalid DHCP server send data type, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ type = r;
+ switch(type) {
+ case DHCP_RAW_OPTION_DATA_UINT8:{
+ r = safe_atou8(p, &uint8_data);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCPv4 vendor specific uint8 data, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ udata = &uint8_data;
+ sz = sizeof(uint8_t);
+ break;
+ }
+ case DHCP_RAW_OPTION_DATA_UINT16:{
+ r = safe_atou16(p, &uint16_data);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCPv4 vendor specific uint16 data, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ udata = &uint16_data;
+ sz = sizeof(uint16_t);
+ break;
+ }
+ case DHCP_RAW_OPTION_DATA_UINT32: {
+ r = safe_atou32(p, &uint32_data);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCPv4 vendor specific uint32 data, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ udata = &uint32_data;
+ sz = sizeof(uint32_t);
+
+ break;
+ }
+ case DHCP_RAW_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 vendor specific ipv4address data, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ udata = &addr.in;
+ sz = sizeof(addr.in.s_addr);
+ break;
+ }
+ case DHCP_RAW_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_raw_option_new(u, udata, sz, &opt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store DHCP send raw option '%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ r = ordered_hashmap_ensure_allocated(&network->dhcp_server_raw_options, &dhcp_raw_options_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ /* Overwrite existing option */
+ old = ordered_hashmap_remove(network->dhcp_server_raw_options, UINT_TO_PTR(u));
+ r = ordered_hashmap_put(network->dhcp_server_raw_options, UINT_TO_PTR(u), opt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store DHCP server send raw option '%s'", rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(opt);
+
+ return 0;
+}
diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h
index 7c12538096..c30162dd5b 100644
--- a/src/network/networkd-dhcp-server.h
+++ b/src/network/networkd-dhcp-server.h
@@ -2,11 +2,27 @@
#pragma once
#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-util.h"
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);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_sip);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_raw_option_data);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 446ba9f084..9f53385602 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -200,6 +200,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.SendRawOption, config_parse_dhcp_server_raw_option_data, 0, 0
Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 0956d2a9b7..90d86f35a9 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -11,6 +11,7 @@
#include "fd-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
+#include "networkd-dhcp-server.h"
#include "network-internal.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@@ -144,14 +145,14 @@ static int network_resolve_stacked_netdevs(Network *network) {
}
int network_verify(Network *network) {
- Address *address, *address_next;
- Route *route, *route_next;
- FdbEntry *fdb, *fdb_next;
+ RoutingPolicyRule *rule, *rule_next;
Neighbor *neighbor, *neighbor_next;
AddressLabel *label, *label_next;
- Prefix *prefix, *prefix_next;
- RoutingPolicyRule *rule, *rule_next;
NextHop *nexthop, *nextnop_next;
+ Address *address, *address_next;
+ Prefix *prefix, *prefix_next;
+ Route *route, *route_next;
+ FdbEntry *fdb, *fdb_next;
assert(network);
assert(network->filename);
@@ -680,6 +681,7 @@ static Network *network_free(Network *network) {
set_free_free(network->dnssec_negative_trust_anchors);
ordered_hashmap_free(network->dhcp_send_options);
+ ordered_hashmap_free(network->dhcp_server_raw_options);
return mfree(network);
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 0d4559774c..9258bf55f7 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -16,6 +16,7 @@
#include "networkd-brvlan.h"
#include "networkd-dhcp-common.h"
#include "networkd-dhcp4.h"
+#include "networkd-dhcp-server.h"
#include "networkd-fdb.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-rx.h"
@@ -114,6 +115,7 @@ struct Network {
Set *dhcp_black_listed_ip;
Set *dhcp_request_options;
OrderedHashmap *dhcp_send_options;
+ OrderedHashmap *dhcp_server_raw_options;
/* DHCPv6 Client support*/
bool dhcp6_use_dns;
diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
index 2252a4aa88..ba1bc4458a 100644
--- a/src/systemd/sd-dhcp-server.h
+++ b/src/systemd/sd-dhcp-server.h
@@ -27,13 +27,20 @@
_SD_BEGIN_DECLARATIONS;
+extern const struct hash_ops dhcp_raw_options_hash_ops;
+
typedef struct sd_dhcp_server sd_dhcp_server;
+typedef struct sd_dhcp_raw_option sd_dhcp_raw_option;
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server);
sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server);
+int sd_dhcp_raw_option_new(uint8_t type, char *data, size_t lengt, sd_dhcp_raw_option **ret);
+sd_dhcp_raw_option *sd_dhcp_raw_option_ref(sd_dhcp_raw_option *ra);
+sd_dhcp_raw_option *sd_dhcp_raw_option_unref(sd_dhcp_raw_option *ra);
+
int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t priority);
int sd_dhcp_server_detach_event(sd_dhcp_server *client);
sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
@@ -51,12 +58,15 @@ int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], u
int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n);
int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled);
+int sd_dhcp_server_add_raw_option(sd_dhcp_server *server, sd_dhcp_raw_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);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_raw_option, sd_dhcp_raw_option_unref);
_SD_END_DECLARATIONS;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index f01a20118b..3469dd443e 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -259,6 +259,7 @@ MaxLeaseTimeSec=
DefaultLeaseTimeSec=
EmitTimezone=
DNS=
+SendRawOption=
[NextHop]
Id=
Gateway=