diff options
| -rw-r--r-- | src/basic/list.h | 26 | ||||
| -rw-r--r-- | src/libsystemd-network/dhcp-lease-internal.h | 12 | ||||
| -rw-r--r-- | src/libsystemd-network/dhcp-protocol.h | 2 | ||||
| -rw-r--r-- | src/libsystemd-network/network-internal.c | 27 | ||||
| -rw-r--r-- | src/libsystemd-network/network-internal.h | 3 | ||||
| -rw-r--r-- | src/libsystemd-network/sd-dhcp-lease.c | 117 | ||||
| -rw-r--r-- | src/test/test-list.c | 44 | 
7 files changed, 222 insertions, 9 deletions
| diff --git a/src/basic/list.h b/src/basic/list.h index 2939216adb..760abcdab3 100644 --- a/src/basic/list.h +++ b/src/basic/list.h @@ -123,6 +123,32 @@                  }                                                       \          } while(false) +/* Insert an item before another one (a = where, b = what) */ +#define LIST_INSERT_BEFORE(name,head,a,b)                               \ +        do {                                                            \ +                typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ +                assert(_b);                                             \ +                if (!_a) {                                              \ +                        if (!*_head) {                                  \ +                                _b->name##_next = NULL;                 \ +                                _b->name##_prev = NULL;                 \ +                                *_head = _b;                            \ +                        } else {                                        \ +                                typeof(*(head)) *_tail = (head);        \ +                                while (_tail->name##_next)              \ +                                        _tail = _tail->name##_next;     \ +                                _b->name##_next = NULL;                 \ +                                _b->name##_prev = _tail;                \ +                                _tail->name##_next = _b;                \ +                        }                                               \ +                } else {                                                \ +                        if ((_b->name##_prev = _a->name##_prev))        \ +                                _b->name##_prev->name##_next = _b;      \ +                        _b->name##_next = _a;                           \ +                        _a->name##_prev = _b;                           \ +                }                                                       \ +        } while(false) +  #define LIST_JUST_US(name,item)                                         \          (!(item)->name##_prev && !(item)->name##_next)                  \ diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 6e00b1ad30..5a3fcddb1b 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -27,6 +27,7 @@  #include "refcnt.h"  #include "util.h" +#include "list.h"  #include "dhcp-protocol.h" @@ -38,6 +39,14 @@ struct sd_dhcp_route {          unsigned char dst_prefixlen;  }; +struct sd_dhcp_raw_option { +        LIST_FIELDS(struct sd_dhcp_raw_option, options); + +        uint8_t tag; +        uint8_t length; +        void *data; +}; +  struct sd_dhcp_lease {          RefCount n_ref; @@ -74,11 +83,14 @@ struct sd_dhcp_lease {          size_t client_id_len;          uint8_t *vendor_specific;          size_t vendor_specific_len; +        LIST_HEAD(struct sd_dhcp_raw_option, private_options);  };  int dhcp_lease_new(sd_dhcp_lease **ret);  int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,                                void *user_data); +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, +                                     const uint8_t *data, uint8_t len);  int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index aa37e9b0b5..4308723f91 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -138,5 +138,7 @@ enum {          DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,          DHCP_OPTION_CLIENT_IDENTIFIER           = 61,          DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121, +        DHCP_OPTION_PRIVATE_BASE                = 224, +        DHCP_OPTION_PRIVATE_LAST                = 254,          DHCP_OPTION_END                         = 255,  }; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index d579755cc8..3d78bf8b35 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -509,3 +509,30 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t          return 0;  } + +int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size) { +        _cleanup_free_ char *hex_buf = NULL; + +        assert(f); +        assert(key); +        assert(data); + +        hex_buf = hexmem(data, size); +        if (hex_buf == NULL) +                return -ENOMEM; + +        fprintf(f, "%s=%s\n", key, hex_buf); + +        return 0; +} + +int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string) { +        assert(data); +        assert(data_len); +        assert(string); + +        if (strlen(string) % 2) +                return -EINVAL; + +        return unhexmem(string, strlen(string), (void **)data, data_len); +} diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 06aba893ce..7aaecbb10d 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -74,3 +74,6 @@ struct sd_dhcp_route;  void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);  int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); + +int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size); +int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string); diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index febf9f87f3..0d1e3746aa 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -203,6 +203,14 @@ sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {  sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {          if (lease && REFCNT_DEC(lease->n_ref) == 0) { +                while (lease->private_options) { +                        struct sd_dhcp_raw_option *option = lease->private_options; + +                        LIST_REMOVE(options, lease->private_options, option); + +                        free(option->data); +                        free(option); +                }                  free(lease->hostname);                  free(lease->domainname);                  free(lease->dns); @@ -607,11 +615,49 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,                  }                 break; + +        default: +                if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST) +                    break; + +                r = dhcp_lease_insert_private_option(lease, code, option, len); +                if (r < 0) +                        return r;          }          return 0;  } +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, +                                     const uint8_t *data, uint8_t len) { +        struct sd_dhcp_raw_option *cur, *option; + +        LIST_FOREACH(options, cur, lease->private_options) { +                if (tag < cur->tag) +                        break; +                else if (tag == cur->tag) { +                        log_error("Ignoring duplicate option, tagged %d.", tag); +                        return 0; +                } +        } + +        option = new(struct sd_dhcp_raw_option, 1); +        if (!option) +                return -ENOMEM; + +        option->tag = tag; +        option->length = len; +        option->data = memdup(data, len); +        if (!option->data) { +                free(option); +                return -ENOMEM; +        } + +        LIST_INSERT_BEFORE(options, lease->private_options, cur, option); + +        return 0; +} +  int dhcp_lease_new(sd_dhcp_lease **ret) {          sd_dhcp_lease *lease; @@ -621,6 +667,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {          lease->router = INADDR_ANY;          lease->n_ref = REFCNT_INIT; +        LIST_HEAD_INIT(lease->private_options);          *ret = lease;          return 0; @@ -629,6 +676,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {  int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {          _cleanup_free_ char *temp_path = NULL;          _cleanup_fclose_ FILE *f = NULL; +        struct sd_dhcp_raw_option *option;          struct in_addr address;          const struct in_addr *addresses;          const uint8_t *client_id, *data; @@ -730,6 +778,14 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {                  fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);          } +        LIST_FOREACH(options, option, lease->private_options) { +                char key[strlen("OPTION_000")]; +                snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag); +                r = serialize_dhcp_option(f, key, option->data, option->length); +                if (r < 0) +                        goto fail; +        } +          r = fflush_and_check(f);          if (r < 0)                  goto fail; @@ -754,9 +810,11 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {                              *server_address = NULL, *next_server = NULL,                              *dns = NULL, *ntp = NULL, *mtu = NULL,                              *routes = NULL, *client_id_hex = NULL, -                            *vendor_specific_hex = NULL; +                            *vendor_specific_hex = NULL, +                            *options[DHCP_OPTION_PRIVATE_LAST - +                                     DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };          struct in_addr addr; -        int r; +        int r, i;          assert(lease_file);          assert(ret); @@ -780,6 +838,37 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {                             "ROUTES", &routes,                             "CLIENTID", &client_id_hex,                             "VENDOR_SPECIFIC", &vendor_specific_hex, +                           "OPTION_224", &options[0], +                           "OPTION_225", &options[1], +                           "OPTION_226", &options[2], +                           "OPTION_227", &options[3], +                           "OPTION_228", &options[4], +                           "OPTION_229", &options[5], +                           "OPTION_230", &options[6], +                           "OPTION_231", &options[7], +                           "OPTION_232", &options[8], +                           "OPTION_233", &options[9], +                           "OPTION_234", &options[10], +                           "OPTION_235", &options[11], +                           "OPTION_236", &options[12], +                           "OPTION_237", &options[13], +                           "OPTION_238", &options[14], +                           "OPTION_239", &options[15], +                           "OPTION_240", &options[16], +                           "OPTION_241", &options[17], +                           "OPTION_242", &options[18], +                           "OPTION_243", &options[19], +                           "OPTION_244", &options[20], +                           "OPTION_245", &options[21], +                           "OPTION_246", &options[22], +                           "OPTION_247", &options[23], +                           "OPTION_248", &options[24], +                           "OPTION_249", &options[25], +                           "OPTION_250", &options[26], +                           "OPTION_251", &options[27], +                           "OPTION_252", &options[28], +                           "OPTION_253", &options[29], +                           "OPTION_254", &options[30],                             NULL);          if (r < 0) {                  if (r == -ENOENT) @@ -854,19 +943,29 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {          }          if (client_id_hex) { -                if (strlen(client_id_hex) % 2) -                        return -EINVAL; - -                r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len); +                r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);                  if (r < 0)                          return r;          }          if (vendor_specific_hex) { -                if (strlen(vendor_specific_hex) % 2) -                        return -EINVAL; +                r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex); +                if (r < 0) +                        return r; +        } + +        for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) { +                uint8_t *data; +                size_t len; + +                if (!options[i]) +                        continue; + +                r = deserialize_dhcp_option(&data, &len, options[i]); +                if (r < 0) +                        return r; -                r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len); +                r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);                  if (r < 0)                          return r;          } diff --git a/src/test/test-list.c b/src/test/test-list.c index f6da1a7053..160064d06a 100644 --- a/src/test/test-list.c +++ b/src/test/test-list.c @@ -99,6 +99,50 @@ int main(int argc, const char *argv[]) {          assert_se(items[1].item_prev == &items[3]);          assert_se(items[3].item_prev == NULL); +        LIST_REMOVE(item, head, &items[1]); +        assert_se(LIST_JUST_US(item, &items[1])); + +        assert_se(items[0].item_next == NULL); +        assert_se(items[2].item_next == &items[0]); +        assert_se(items[3].item_next == &items[2]); + +        assert_se(items[0].item_prev == &items[2]); +        assert_se(items[2].item_prev == &items[3]); +        assert_se(items[3].item_prev == NULL); + +        LIST_INSERT_BEFORE(item, head, &items[2], &items[1]); +        assert_se(items[0].item_next == NULL); +        assert_se(items[2].item_next == &items[0]); +        assert_se(items[1].item_next == &items[2]); +        assert_se(items[3].item_next == &items[1]); + +        assert_se(items[0].item_prev == &items[2]); +        assert_se(items[2].item_prev == &items[1]); +        assert_se(items[1].item_prev == &items[3]); +        assert_se(items[3].item_prev == NULL); + +        LIST_REMOVE(item, head, &items[0]); +        assert_se(LIST_JUST_US(item, &items[0])); + +        assert_se(items[2].item_next == NULL); +        assert_se(items[1].item_next == &items[2]); +        assert_se(items[3].item_next == &items[1]); + +        assert_se(items[2].item_prev == &items[1]); +        assert_se(items[1].item_prev == &items[3]); +        assert_se(items[3].item_prev == NULL); + +        LIST_INSERT_BEFORE(item, head, NULL, &items[0]); +        assert_se(items[0].item_next == NULL); +        assert_se(items[2].item_next == &items[0]); +        assert_se(items[1].item_next == &items[2]); +        assert_se(items[3].item_next == &items[1]); + +        assert_se(items[0].item_prev == &items[2]); +        assert_se(items[2].item_prev == &items[1]); +        assert_se(items[1].item_prev == &items[3]); +        assert_se(items[3].item_prev == NULL); +          LIST_REMOVE(item, head, &items[0]);          assert_se(LIST_JUST_US(item, &items[0])); | 
