Merge pull request #877 from crawford/dhcp-private-options-v4

networkd: save private-zone DHCP options
This commit is contained in:
Lennart Poettering
2015-08-05 22:20:36 +03:00
7 changed files with 222 additions and 9 deletions

View File

@@ -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) \

View File

@@ -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);

View File

@@ -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,
};

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}
r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len);
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 = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
return r;
}

View File

@@ -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]));