]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: Fix split in `SendOption=` on client and server
authorDavid Wood <david.wood@cambridgeconsultants.com>
Fri, 28 Feb 2020 18:28:49 +0000 (18:28 +0000)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 19 Mar 2020 08:08:40 +0000 (09:08 +0100)
When specifying `DHCPv4.SendOption=`, it is used by systemd-networkd to
set the value of that option within the DHCP request that is sent out.
This differs to setting `DHCPServer.SendOption=`, which will place all
the options together as suboptions into the vendor-specific information
(code 43) option.

This commit adds two new config options, `DHCPv4.SendVendorOption=` and
`DHCPServer.SendVendorOption=`. These both have the behaviour of the old
`DHCPServer.SendOption=` flag, and set the value of the suboption in the
vendor-specific information option.

The behaviour of `DHCPServer.SendOption=` is then changed to reflect
that of `DHCPv4.SendOption=`. It will set the value of the corresponding
option in the DHCP request.

12 files changed:
man/systemd.network.xml
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-server.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp-server.h
test/fuzz/fuzz-network-parser/directives.network

index b13fcd189323b4ad8f371b84785d223db6b5600c..5457e668dd282a7aed9cd974859f10ad395aeb9f 100644 (file)
         <varlistentry>
           <term><varname>SendOption=</varname></term>
           <listitem>
-            <para>Send an arbitrary option in the DHCPv4 request. Takes a DHCP option number, data type
+            <para>Send an arbitrary raw option in the DHCPv4 request. Takes a DHCP option number, data type
+            and data separated with a colon
+            (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
+            The option number must be an integer in the range 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 setting can be specified multiple times. If an empty string is specified,
+            then all options specified earlier are cleared. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>SendVendorOption=</varname></term>
+          <listitem>
+            <para>Send an arbitrary vendor option in the DHCPv4 request. Takes a DHCP option number, data type
             and data separated with a colon
             (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
             The option number must be an integer in the range 1..254. The type takes one of <literal>uint8</literal>,
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SendVendorOption=</varname></term>
+        <listitem>
+          <para>Send a vendor option with value via DHCPv4 server. Takes a DHCP option number, data type
+          and data (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
+          The option number is an integer in the range 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 setting 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>
 
index 42c3ceb8b158ea73d7ba49474ddae17a82ea43f7..41901894f577cc3a172eabd197e1269a809bd170 100644 (file)
@@ -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;
 
index 4122d08d9658c821cc5d77c2b828d1fe1f27e1f0..82553e79cadfb8a4ccd678a3d7f6b274e201506c 100644 (file)
@@ -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);
 }
 
index 546b5d02c4ceb7853559f68ec248074a8f4c51c9..a0b779bc096a864c96d2886e72742f77a05ee49a 100644 (file)
@@ -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;
 
index bee75a6930ea690479e9d06b5e4fed00d6ff5928..2ec742b5e3289e7ffcf525b04ea2a46a72fbf6d2 100644 (file)
@@ -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)
index 64190375a4183edeeb5770d9c2077c909252b010..83fb25264ab5fdbd07b57bd75ec8c21e43bb905e 100644 (file)
@@ -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");
         }
index c75a07e4f399e9f62444f157d70f649c7e40f53b..fd996327a55fc5e3e729057de682e493c63b9f69 100644 (file)
@@ -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)
index 248172f8a204b8ce8059685603cf3ab22ec79950..e7ead446c7640227874263790168a09adfa426f6 100644 (file)
@@ -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);
index b6872821240e80465ad47e0c24b625700fd2107b..f747ccaf101812633f0badc35fe88856e2f31e15 100644 (file)
@@ -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;
index 0002ea0e5952d658eae3da8e259f234fcdf61b16..9dd562fa43f646aa0d51631402a96e24e20538e9 100644 (file)
@@ -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);
index 5950506c9bc5caad9ae2f9e6d5b312c5f3274915..55272b5164d5bd11d54ff496e7a1d5e5665f6ec5 100644 (file)
@@ -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);
index b3465ceb8c9f11c3a3529de2a55cabde99eb6731..4418fc01495b92e2b16902e4488939f8c0fee072 100644 (file)
@@ -100,6 +100,7 @@ SendRelease=
 MaxAttempts=
 IPServiceType=
 SendOption=
+SendVendorOption=
 SendDecline=
 RouteMTUBytes=
 [DHCPv6]
@@ -273,6 +274,7 @@ DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
 SendOption=
+SendVendorOption=
 [NextHop]
 Id=
 Gateway=