summaryrefslogtreecommitdiff
path: root/src/libsystemd-network
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2022-09-30 03:37:02 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2022-10-02 23:05:05 +0900
commit9d2d346aaeda53c3dc0c4b3077ec2e39fdfa61c4 (patch)
tree1061b0a43496b1108d19527b08e74a905f566547 /src/libsystemd-network
parent045422384093b97d21ae6296bd651555d9eeebea (diff)
downloadsystemd-9d2d346aaeda53c3dc0c4b3077ec2e39fdfa61c4.tar.gz
sd-dhcp6-client: allow to build large packet
Previously, the maximum packet size was hardcoded with 512 + size of the DHCP header. This makes the packet size increased when necessary. See option_append_hdr(). Previously, all functions which append DHCP options took the head of the unused area of the packet, and the size of the free area. However, with this change, the buffer for the whole packet may be reallocated, hence now they take the head of the packet and the offset to the free area. Fixes #24851.
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r--src/libsystemd-network/dhcp6-option.c82
-rw-r--r--src/libsystemd-network/dhcp6-option.h12
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c69
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c26
4 files changed, 91 insertions, 98 deletions
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index d258c9aafa..0b8393ca2c 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -211,26 +211,30 @@ bool dhcp6_option_can_request(uint16_t option) {
}
}
-static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
+static int option_append_hdr(uint8_t **buf, size_t *offset, uint16_t optcode, size_t optlen) {
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
+
+ if (optlen > 0xffff)
+ return -ENOBUFS;
- if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
+ if (optlen + offsetof(DHCP6Option, data) > SIZE_MAX - *offset)
return -ENOBUFS;
- unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode);
- unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen);
+ if (!GREEDY_REALLOC(*buf, *offset + optlen + offsetof(DHCP6Option, data)))
+ return -ENOMEM;
- *buf += offsetof(DHCP6Option, data);
- *buflen -= offsetof(DHCP6Option, data);
+ unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, code), optcode);
+ unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, len), optlen);
+ *offset += offsetof(DHCP6Option, data);
return 0;
}
int dhcp6_option_append(
uint8_t **buf,
- size_t *buflen,
+ size_t *offset,
uint16_t code,
size_t optlen,
const void *optval) {
@@ -239,23 +243,23 @@ int dhcp6_option_append(
assert(optval || optlen == 0);
- r = option_append_hdr(buf, buflen, code, optlen);
+ r = option_append_hdr(buf, offset, code, optlen);
if (r < 0)
return r;
- *buf = mempcpy_safe(*buf, optval, optlen);
- *buflen -= optlen;
+ memcpy_safe(*buf + *offset, optval, optlen);
+ *offset += optlen;
return 0;
}
-int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) {
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options) {
sd_dhcp6_option *options;
int r;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
ORDERED_SET_FOREACH(options, vendor_options) {
_cleanup_free_ uint8_t *p = NULL;
@@ -272,7 +276,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet
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);
+ r = dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
if (r < 0)
return r;
}
@@ -280,12 +284,12 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet
return 0;
}
-static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) {
+static int option_append_ia_address(uint8_t **buf, size_t *offset, const struct iaaddr *address) {
struct iaaddr a;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
assert(address);
/* Do not append T1 and T2. */
@@ -293,15 +297,15 @@ static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct
.address = address->address,
};
- return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a);
}
-static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) {
+static int option_append_pd_prefix(uint8_t **buf, size_t *offset, const struct iapdprefix *prefix) {
struct iapdprefix p;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
assert(prefix);
if (prefix->prefixlen == 0)
@@ -313,18 +317,18 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i
.address = prefix->address,
};
- return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p);
}
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
+int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia) {
+ _cleanup_free_ uint8_t *data = NULL;
struct ia_header header;
- uint8_t data[512], *p;
- size_t len, size;
+ size_t len;
int r;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
assert(ia);
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
@@ -349,28 +353,30 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
assert_not_reached();
}
- p = mempcpy(data, &header, len);
- size = sizeof(data) - len;
+ if (!GREEDY_REALLOC(data, len))
+ return -ENOMEM;
+
+ memcpy(data, &header, len);
LIST_FOREACH(addresses, addr, ia->addresses) {
if (ia->type == SD_DHCP6_OPTION_IA_PD)
- r = option_append_pd_prefix(&p, &size, &addr->iapdprefix);
+ r = option_append_pd_prefix(&data, &len, &addr->iapdprefix);
else
- r = option_append_ia_address(&p, &size, &addr->iaaddr);
+ r = option_append_ia_address(&data, &len, &addr->iaaddr);
if (r < 0)
return r;
}
- return dhcp6_option_append(buf, buflen, ia->type, p - data, data);
+ return dhcp6_option_append(buf, offset, ia->type, len, data);
}
-int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
+int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn) {
uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX];
int r;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
if (isempty(fqdn))
return 0;
@@ -391,16 +397,16 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
if (dns_name_is_single_label(fqdn))
r--;
- return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
}
-int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) {
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class) {
_cleanup_free_ uint8_t *p = NULL;
size_t n = 0;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
if (strv_isempty(user_class))
return 0;
@@ -419,16 +425,16 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *
n += len + 2;
}
- return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, n, p);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_USER_CLASS, n, p);
}
-int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) {
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class) {
_cleanup_free_ uint8_t *p = NULL;
size_t n = 0;
assert(buf);
assert(*buf);
- assert(buflen);
+ assert(offset);
if (strv_isempty(vendor_class))
return 0;
@@ -454,7 +460,7 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
n += len + 2;
}
- return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, n, p);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_CLASS, n, p);
}
int dhcp6_option_parse(
diff --git a/src/libsystemd-network/dhcp6-option.h b/src/libsystemd-network/dhcp6-option.h
index d044222124..36841dd270 100644
--- a/src/libsystemd-network/dhcp6-option.h
+++ b/src/libsystemd-network/dhcp6-option.h
@@ -72,13 +72,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free);
bool dhcp6_option_can_request(uint16_t option);
-int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
+int dhcp6_option_append(uint8_t **buf, size_t *offset, uint16_t code,
size_t optlen, const void *optval);
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
-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 * const *user_class);
-int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class);
-int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options);
+int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia);
+int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn);
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class);
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class);
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options);
int dhcp6_option_parse(
const uint8_t *buf,
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index e27a5c4fe1..375f984940 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -574,8 +574,8 @@ static void client_stop(sd_dhcp6_client *client, int error) {
static int client_append_common_options_in_managed_mode(
sd_dhcp6_client *client,
- uint8_t **opt,
- size_t *optlen,
+ uint8_t **buf,
+ size_t *offset,
const DHCP6IA *ia_na,
const DHCP6IA *ia_pd) {
@@ -587,34 +587,35 @@ static int client_append_common_options_in_managed_mode(
DHCP6_STATE_REQUEST,
DHCP6_STATE_RENEW,
DHCP6_STATE_REBIND));
- assert(opt);
- assert(optlen);
+ assert(buf);
+ assert(*buf);
+ assert(offset);
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) {
- r = dhcp6_option_append_ia(opt, optlen, ia_na);
+ r = dhcp6_option_append_ia(buf, offset, ia_na);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) {
- r = dhcp6_option_append_ia(opt, optlen, ia_pd);
+ r = dhcp6_option_append_ia(buf, offset, ia_pd);
if (r < 0)
return r;
}
- r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn);
+ r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
if (r < 0)
return r;
- r = dhcp6_option_append_user_class(opt, optlen, client->user_class);
+ r = dhcp6_option_append_user_class(buf, offset, client->user_class);
if (r < 0)
return r;
- r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class);
+ r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class);
if (r < 0)
return r;
- r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options);
+ r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options);
if (r < 0)
return r;
@@ -640,14 +641,15 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client)
}
}
-static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *optlen) {
+static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
_cleanup_free_ be16_t *p = NULL;
be16_t *req_opts;
size_t n;
assert(client);
- assert(opt);
- assert(optlen);
+ assert(buf);
+ assert(*buf);
+ assert(offset);
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
@@ -685,18 +687,18 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *opt
if (n == 0)
return 0;
- return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
+ return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
}
int dhcp6_client_send_message(sd_dhcp6_client *client) {
- _cleanup_free_ DHCP6Message *message = NULL;
+ _cleanup_free_ uint8_t *buf = NULL;
struct in6_addr all_servers =
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
struct sd_dhcp6_option *j;
- size_t len, optlen = 512;
- uint8_t *opt;
usec_t elapsed_usec, time_now;
be16_t elapsed_time;
+ DHCP6Message *message;
+ size_t offset;
int r;
assert(client);
@@ -706,16 +708,13 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
- len = sizeof(DHCP6Message) + optlen;
-
- message = malloc0(len);
- if (!message)
+ if (!GREEDY_REALLOC0(buf, offsetof(DHCP6Message, options)))
return -ENOMEM;
- opt = (uint8_t *)(message + 1);
-
+ message = (DHCP6Message*) buf;
message->transaction_id = client->transaction_id;
message->type = client_message_type_from_state(client);
+ offset = offsetof(DHCP6Message, options);
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
@@ -723,12 +722,12 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
case DHCP6_STATE_SOLICITATION:
if (client->rapid_commit) {
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
}
- r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
+ r = client_append_common_options_in_managed_mode(client, &buf, &offset,
&client->ia_na, &client->ia_pd);
if (r < 0)
return r;
@@ -737,7 +736,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
+ r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
client->lease->serverid);
if (r < 0)
@@ -748,7 +747,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
assert(client->lease);
- r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
+ r = client_append_common_options_in_managed_mode(client, &buf, &offset,
client->lease->ia_na, client->lease->ia_pd);
if (r < 0)
return r;
@@ -761,24 +760,24 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
}
if (client->mudurl) {
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6,
+ r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6,
strlen(client->mudurl), client->mudurl);
if (r < 0)
return r;
}
- r = client_append_oro(client, &opt, &optlen);
+ r = client_append_oro(client, &buf, &offset);
if (r < 0)
return r;
assert(client->duid_len > 0);
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
+ r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
if (r < 0)
return r;
ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
- r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
+ r = dhcp6_option_append(&buf, &offset, j->option, j->length, j->data);
if (r < 0)
return r;
}
@@ -788,18 +787,16 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
* been trying to complete a DHCP message exchange. */
elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = htobe16(elapsed_usec);
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
+ r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
- r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
- len - optlen);
+ r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
if (r < 0)
return r;
log_dhcp6_client(client, "Sent %s",
- dhcp6_message_type_to_string(message->type));
-
+ dhcp6_message_type_to_string(client_message_type_from_state(client)));
return 0;
}
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index e5c09f9664..c3c89514f8 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -179,7 +179,7 @@ TEST(parse_domain) {
}
TEST(option) {
- uint8_t packet[] = {
+ static const uint8_t packet[] = {
'F', 'O', 'O', 'H', 'O', 'G', 'E',
0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
'A', 'B', 'C', 'D', 'E', 'F', 'G',
@@ -187,7 +187,7 @@ TEST(option) {
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'B', 'A', 'R',
};
- uint8_t result[] = {
+ static const uint8_t result[] = {
'F', 'O', 'O', 'H', 'O', 'G', 'E',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -195,10 +195,9 @@ TEST(option) {
'B', 'A', 'R',
};
_cleanup_free_ uint8_t *buf = NULL;
- size_t offset, pos, optlen, outlen = sizeof(result);
+ size_t offset, pos, optlen;
const uint8_t *optval;
uint16_t optcode;
- uint8_t *out;
assert_se(sizeof(packet) == sizeof(result));
@@ -227,15 +226,10 @@ TEST(option) {
assert_se(optlen == 7);
assert_se(optval == packet + 11);
+ free(buf);
+ assert_se(buf = memdup(result, sizeof(result)));
pos = 7;
- outlen -= 7;
- out = &result[pos];
-
- assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0);
-
- pos += 4 + optlen;
- assert_se(out == &result[pos]);
- assert_se(*out == 0x00);
+ assert_se(dhcp6_option_append(&buf, &pos, optcode, optlen, optval) >= 0);
assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0);
@@ -243,13 +237,9 @@ TEST(option) {
assert_se(optlen == 9);
assert_se(optval == packet + 22);
- assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0);
-
- pos += 4 + optlen;
- assert_se(out == &result[pos]);
- assert_se(*out == 'B');
+ assert_se(dhcp6_option_append(&buf, &pos, optcode, optlen, optval) >= 0);
- assert_se(memcmp(packet, result, sizeof(packet)) == 0);
+ assert_se(memcmp(packet, buf, sizeof(packet)) == 0);
}
TEST(option_status) {