summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/basic/list.h26
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h12
-rw-r--r--src/libsystemd-network/dhcp-protocol.h2
-rw-r--r--src/libsystemd-network/network-internal.c27
-rw-r--r--src/libsystemd-network/network-internal.h3
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c117
-rw-r--r--src/test/test-list.c44
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]));