diff options
-rw-r--r-- | src/libsystemd-network/dhcp-internal.h | 4 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-option.c | 88 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-packet.c | 6 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-client.c | 20 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-option.c | 62 |
5 files changed, 140 insertions, 40 deletions
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 56423a2cce..76d357a6ed 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -36,7 +36,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len); -int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, +int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, uint8_t code, size_t optlen, const void *optval); typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, @@ -46,7 +46,7 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *user_data); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, - uint8_t options[], size_t optlen, size_t *optoffset); + size_t optlen, size_t *optoffset); uint16_t dhcp_packet_checksum(void *buf, size_t len); diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 24f678c100..b6110c5f16 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -26,8 +26,8 @@ #include "dhcp-internal.h" -int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, - uint8_t code, size_t optlen, const void *optval) { +static int option_append(uint8_t options[], size_t size, size_t *offset, + uint8_t code, size_t optlen, const void *optval) { assert(options); assert(offset); @@ -39,7 +39,7 @@ int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, case DHCP_OPTION_PAD: case DHCP_OPTION_END: - if (size - *offset < 1) + if (size < *offset + 1) return -ENOBUFS; options[*offset] = code; @@ -47,14 +47,17 @@ int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, break; default: - if (size - *offset < optlen + 2) + if (size < *offset + optlen + 2) return -ENOBUFS; - assert(optval); - options[*offset] = code; options[*offset + 1] = optlen; - memcpy(&options[*offset + 2], optval, optlen); + + if (optlen) { + assert(optval); + + memcpy(&options[*offset + 2], optval, optlen); + } *offset += optlen + 2; @@ -64,6 +67,77 @@ int dhcp_option_append(uint8_t options[], size_t size, size_t *offset, return 0; } +int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, + uint8_t overload, + uint8_t code, size_t optlen, const void *optval) { + size_t file_offset = 0, sname_offset =0; + bool file, sname; + int r; + + assert(message); + assert(offset); + + file = overload & DHCP_OVERLOAD_FILE; + sname = overload & DHCP_OVERLOAD_SNAME; + + if (*offset < size) { + /* still space in the options array */ + r = option_append(message->options, size, offset, code, optlen, optval); + if (r >= 0) + return 0; + else if (r == -ENOBUFS && (file || sname)) { + /* did not fit, but we have more buffers to try + close the options array and move the offset to its end */ + r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size; + } else + return r; + } + + if (overload & DHCP_OVERLOAD_FILE) { + file_offset = *offset - size; + + if (file_offset < sizeof(message->file)) { + /* still space in the 'file' array */ + r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + file_offset; + return 0; + } else if (r == -ENOBUFS && sname) { + /* did not fit, but we have more buffers to try + close the file array and move the offset to its end */ + r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size + sizeof(message->file); + } else + return r; + } + } + + if (overload & DHCP_OVERLOAD_SNAME) { + sname_offset = *offset - size - (file ? sizeof(message->file) : 0); + + if (sname_offset < sizeof(message->sname)) { + /* still space in the 'sname' array */ + r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + (file ? sizeof(message->file) : 0) + sname_offset; + return 0; + } else { + /* no space, or other error, give up */ + return r; + } + } + } + + return -ENOBUFS; +} + static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, uint8_t *message_type, dhcp_option_cb_t cb, void *user_data) { diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 10b645759e..3e2fb96c27 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -38,7 +38,7 @@ #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, - uint8_t type, uint8_t options[], size_t optlen, size_t *optoffset) { + uint8_t type, size_t optlen, size_t *optoffset) { size_t offset = 0; int r; @@ -50,8 +50,8 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, message->xid = htobe32(xid); message->magic = htobe32(DHCP_MAGIC_COOKIE); - r = dhcp_option_append(message->options, optlen, &offset, - DHCP_OPTION_MESSAGE_TYPE, 1, &type); + r = dhcp_option_append(message, optlen, &offset, 0, + DHCP_OPTION_MESSAGE_TYPE, 1, &type); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index cee4ccf784..ac97e00772 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -268,7 +268,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, assert(type == DHCP_DISCOVER || type == DHCP_REQUEST); r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, - message->options, optlen, optoffset); + optlen, optoffset); if (r < 0) return r; @@ -284,7 +284,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ - r = dhcp_option_append(message->options, optlen, optoffset, + r = dhcp_option_append(message, optlen, optoffset, 0, DHCP_OPTION_CLIENT_IDENTIFIER, sizeof(client->client_id), &client->client_id); if (r < 0) @@ -299,7 +299,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, it MUST include that list in any subsequent DHCPREQUEST messages. */ - r = dhcp_option_append(message->options, optlen, optoffset, + r = dhcp_option_append(message, optlen, optoffset, 0, DHCP_OPTION_PARAMETER_REQUEST_LIST, client->req_opts_size, client->req_opts); if (r < 0) @@ -315,7 +315,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPMessage *message, */ max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE + DHCP_MIN_OPTIONS_SIZE); - r = dhcp_option_append(message->options, optlen, optoffset, + r = dhcp_option_append(message, optlen, optoffset, 0, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, 2, &max_size); if (r < 0) @@ -373,14 +373,14 @@ static int client_send_discover(sd_dhcp_client *client) { option to suggest the lease time it would like. */ if (client->last_addr != INADDR_ANY) { - r = dhcp_option_append(discover->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->last_addr); if (r < 0) return r; } - r = dhcp_option_append(discover->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, DHCP_OPTION_END, 0, NULL); /* We currently ignore: @@ -424,13 +424,13 @@ static int client_send_request(sd_dhcp_client *client) { filled in with the yiaddr value from the chosen DHCPOFFER. */ - r = dhcp_option_append(request->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_SERVER_IDENTIFIER, 4, &client->lease->server_address); if (r < 0) return r; - r = dhcp_option_append(request->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->lease->address); if (r < 0) @@ -443,7 +443,7 @@ static int client_send_request(sd_dhcp_client *client) { option MUST be filled in with client’s notion of its previously assigned address. ’ciaddr’ MUST be zero. */ - r = dhcp_option_append(request->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->last_addr); if (r < 0) @@ -476,7 +476,7 @@ static int client_send_request(sd_dhcp_client *client) { return -EINVAL; } - r = dhcp_option_append(request->dhcp.options, optlen, &optoffset, + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_END, 0, NULL); if (r < 0) return r; diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 3dd7102d82..4482a6783c 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -92,7 +92,7 @@ static void test_message_init(void) message = malloc0(len); assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, - DHCP_DISCOVER, message->options, optlen, &optoffset) >= 0); + DHCP_DISCOVER, optlen, &optoffset) >= 0); assert_se(message->xid == htobe32(0x12345678)); assert_se(message->op == BOOTREQUEST); @@ -287,10 +287,6 @@ static void test_options(struct option_desc *desc) printf("DHCP type %s\n", dhcp_type(res)); } -static uint8_t result[64] = { - 'A', 'B', 'C', 'D', -}; - static uint8_t options[64] = { 'A', 'B', 'C', 'D', 160, 2, 0x11, 0x12, @@ -304,24 +300,34 @@ static uint8_t options[64] = { static void test_option_set(void) { + DHCPMessage *result; size_t offset = 0, len, pos; unsigned i; - assert_se(dhcp_option_append(result, 0, &offset, DHCP_OPTION_PAD, - 0, NULL) == -ENOBUFS); + result = malloc0(sizeof(DHCPMessage) + 11); + assert_se(result); + + result->options[0] = 'A'; + result->options[1] = 'B'; + result->options[2] = 'C'; + result->options[3] = 'D'; + + assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD, + 0, NULL) == -ENOBUFS); assert_se(offset == 0); offset = 4; - assert_se(dhcp_option_append(result, 1, &offset, DHCP_OPTION_PAD, - 0, NULL) >= 0); + assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD, + 0, NULL) == -ENOBUFS); + assert_se(offset == 4); + assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD, + 0, NULL) >= 0); assert_se(offset == 5); offset = pos = 4; - len = 60; - while (pos < 64 && options[pos] != DHCP_OPTION_END) { - offset = pos; - - assert_se(dhcp_option_append(result, len, &offset, + len = 11; + while (pos < len && options[pos] != DHCP_OPTION_END) { + assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME, options[pos], options[pos + 1], &options[pos + 2]) >= 0); @@ -331,14 +337,34 @@ static void test_option_set(void) else pos += 2 + options[pos + 1]; - assert_se(offset == pos); + if (pos < len) + assert_se(offset == pos); } - for (i = 0; i < pos; i++) { + for (i = 0; i < 9; i++) { if (verbose) - printf("%2d: 0x%02x(0x%02x)\n", i, result[i], + printf("%2d: 0x%02x(0x%02x) (options)\n", i, result->options[i], options[i]); - assert_se(result[i] == options[i]); + assert_se(result->options[i] == options[i]); + } + + if (verbose) + printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9], + DHCP_OPTION_END); + + assert_se(result->options[9] == DHCP_OPTION_END); + + if (verbose) + printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10], + DHCP_OPTION_PAD); + + assert_se(result->options[10] == DHCP_OPTION_PAD); + + for (i = 0; i < pos - 8; i++) { + if (verbose) + printf("%2d: 0x%02x(0x%02x) (sname)\n", i, result->sname[i], + options[i + 9]); + assert_se(result->sname[i] == options[i + 9]); } if (verbose) |