mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 08:56:15 +09:00
Merge pull request #877 from crawford/dhcp-private-options-v4
networkd: save private-zone DHCP options
This commit is contained in:
@@ -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) \
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user