]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp6: Introduce vendor specific information
authorSusant Sahani <ssahani@vmware.com>
Fri, 22 May 2020 06:39:14 +0000 (08:39 +0200)
committerSusant Sahani <ssahani@vmware.com>
Fri, 29 May 2020 11:36:42 +0000 (13:36 +0200)
RFC: 8415
21.17.  Vendor-specific Information Option

   This option is used by clients and servers to exchange vendor-
   specific information.

   The format of the Vendor-specific Information option is:

       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |      OPTION_VENDOR_OPTS       |           option-len          |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                       enterprise-number                       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      .                                                               .
      .                       vendor-option-data                      .
      .                                                               .
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

           Figure 30: Vendor-specific Information Option Format

      option-code          OPTION_VENDOR_OPTS (17).

      option-len           4 + length of vendor-option-data field.

      enterprise-number    The vendor's registered Enterprise Number as
                           maintained by IANA [IANA-PEN].  A 4-octet
                           field containing an unsigned integer.

      vendor-option-data   Vendor options, interpreted by
                           vendor-specific code on the clients and
                           servers.  A variable-length field (4 octets
                           less than the value in the option-len field).

 The definition of the information carried in this option is vendor
   specific.  The vendor is indicated in the enterprise-number field.
   Use of vendor-specific information allows enhanced operation,
   utilizing additional features in a vendor's DHCP implementation.  A
   DHCP client that does not receive requested vendor-specific
   information will still configure the node's IPv6 stack to be
   functional.

   The vendor-option-data field MUST be encoded as a sequence of
   code/length/value fields of format identical to the DHCP options (see
   Section 21.1).  The sub-option codes are defined by the vendor
   identified in the enterprise-number field and are not managed by
   IANA.  Each of the sub-options is formatted as follows:

       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |          sub-opt-code         |         sub-option-len        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      .                                                               .
      .                        sub-option-data                        .
      .                                                               .
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                 Figure 31: Vendor-specific Options Format

      sub-opt-code         The code for the sub-option.  A 2-octet
                           field.

      sub-option-len       An unsigned integer giving the length of the
                           sub-option-data field in this sub-option in
                           octets.  A 2-octet field.

      sub-option-data      The data area for the sub-option.  The
                           length, in octets, is specified by
                           sub-option-len.

   Multiple instances of the Vendor-specific Information option may
   appear in a DHCP message.  Each instance of the option is interpreted
   according to the option codes defined by the vendor identified by the
   Enterprise Number in that option.  Servers and clients MUST NOT send
   more than one instance of the Vendor-specific Information option with
   the same Enterprise Number.  Each instance of the Vendor-specific
   Information option MAY contain multiple sub-options.

A client that is interested in receiving a Vendor-specific
   Information option:

   -  MUST specify the Vendor-specific Information option in an Option
      Request option.

   -  MAY specify an associated Vendor Class option (see Section 21.16).

   -  MAY specify the Vendor-specific Information option with
      appropriate data.

   Servers only return the Vendor-specific Information options if
   specified in Option Request options from clients and:

   -  MAY use the Enterprise Numbers in the associated Vendor Class
      options to restrict the set of Enterprise Numbers in the
      Vendor-specific Information options returned.

   -  MAY return all configured Vendor-specific Information options.

   -  MAY use other information in the packet or in its configuration to
      determine which set of Enterprise Numbers in the Vendor-specific
      Information options to return.

src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/sd-dhcp6-client.c
src/systemd/meson.build
src/systemd/sd-dhcp6-client.h
src/systemd/sd-dhcp6-option.h

index 2880bc4770f62b0f54e6886bb5c8a2329b9e04a9..b0d1216eed847fbd80424f2fc66dc99c96376d43 100644 (file)
 #include "sd-event.h"
 
 #include "list.h"
+#include "hashmap.h"
 #include "macro.h"
 #include "sparse-endian.h"
 
 typedef struct sd_dhcp6_option {
         unsigned n_ref;
 
+        uint32_t enterprise_identifier;
         uint16_t option;
         void *data;
         size_t length;
@@ -99,6 +101,7 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Add
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
 int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
 int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
index f763474e6ec58b4f94a2d4c86be9109f30193ca4..fa43587686b224d6a41ffda415380ed7fa377892 100644 (file)
@@ -79,6 +79,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
         return 0;
 }
 
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
+        sd_dhcp6_option *options;
+        Iterator i;
+        int r;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_options);
+
+        ORDERED_HASHMAP_FOREACH(options, vendor_options, i) {
+                _cleanup_free_ uint8_t *p = NULL;
+                size_t total;
+
+                total = 4 + 2 + 2 + options->length;
+
+                p = malloc(total);
+                if (!p)
+                        return -ENOMEM;
+
+                unaligned_write_be32(p, options->enterprise_identifier);
+                unaligned_write_be16(p + 4, options->option);
+                unaligned_write_be16(p + 6, options->length);
+                memcpy(p + 8, options->data, options->length);
+
+                r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
         uint16_t len;
         uint8_t *ia_hdr;
@@ -683,7 +716,7 @@ static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
         return mfree(i);
 }
 
-int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret) {
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
         assert_return(ret, -EINVAL);
         assert_return(length == 0 || data, -EINVAL);
 
@@ -698,6 +731,7 @@ int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhc
         *p = (sd_dhcp6_option) {
                 .n_ref = 1,
                 .option = option,
+                .enterprise_identifier = enterprise_identifier,
                 .length = length,
                 .data = TAKE_PTR(q),
         };
index 76a16a18c10d793975ea925789934e9c7696d4bd..8d13aef4e74a224bc74c31d9c6dbf13b094b86cf 100644 (file)
@@ -81,6 +81,7 @@ struct sd_dhcp6_client {
         usec_t information_request_time_usec;
         usec_t information_refresh_time_usec;
         OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
 };
 
 static const uint16_t default_req_opts[] = {
@@ -225,6 +226,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
         return 0;
 }
 
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->vendor_options, v, v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp6_option_ref(v);
+
+        return 1;
+}
+
 static int client_ensure_duid(sd_dhcp6_client *client) {
         if (client->duid_len != 0)
                 return 0;
@@ -622,6 +642,13 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen,
+                                                       client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
@@ -680,6 +707,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -726,6 +759,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
index 63d0829b67dcb9941442a1e4e19525e1401b1201..62baf7784e492f90c8baea9bcef1d50395a8140c 100644 (file)
@@ -24,6 +24,7 @@ _not_installed_headers = '''
         sd-dhcp-client.h
         sd-dhcp-lease.h
         sd-dhcp-option.h
+        sd-dhcp6-option.h
         sd-dhcp-server.h
         sd-ipv4acd.h
         sd-ipv4ll.h
index c342be4b294b7ef972908c64e5fff5e8a21e764c..58a17a5a3f4fdd800e97ef2cbbc154926e449d95 100644 (file)
@@ -143,7 +143,10 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
                                         int *request);
 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
                                         int request);
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
+                                       uint32_t transaction_id);
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
+                                      sd_dhcp6_option *v);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
index c0f1c4df08b227e7e3c03b5f9d3d770e74d90694..88a4986315123d7bcab8e6986dcaaf09d451a88b 100644 (file)
@@ -26,7 +26,7 @@ _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_dhcp6_option sd_dhcp6_option;
 
-int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret);
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
 sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
 sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);