diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libsystemd-network/dhcp6-internal.h | 10 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-option.c | 66 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 32 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp6-client.c | 101 |
5 files changed, 113 insertions, 97 deletions
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 9bff5c2dcd..9e7d3976aa 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -29,6 +29,13 @@ #include "macro.h" #include "sparse-endian.h" +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + typedef struct DHCP6Address DHCP6Address; struct DHCP6Address { @@ -76,8 +83,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 139b7a4c5c..80925b3948 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -36,12 +36,6 @@ #define DHCP6_OPTION_IA_NA_LEN 12 #define DHCP6_OPTION_IA_TA_LEN 4 -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; - static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { DHCP6Option *option = (DHCP6Option*) *buf; @@ -213,11 +207,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, return 0; } -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia) { - int r; - uint16_t opt, status; - size_t optlen; +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { + uint16_t iatype, optlen; + size_t i, len; + int r = 0, status; + uint16_t opt; size_t iaaddr_offset; DHCP6Address *addr; uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; @@ -225,17 +219,19 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); + iatype = be16toh(iaoption->code); + len = be16toh(iaoption->len); + switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_NA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->ia_na, *buf, sizeof(ia->ia_na)); + memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); lt_t1 = be32toh(ia->ia_na.lifetime_t1); lt_t2 = be32toh(ia->ia_na.lifetime_t2); @@ -250,14 +246,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case SD_DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_TA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->ia_ta.id, *buf, sizeof(ia->ia_ta)); + memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); break; @@ -267,15 +262,21 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } ia->type = iatype; + i = iaaddr_offset; + + while (i < len) { + DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - *buflen -= iaaddr_offset; - *buf += iaaddr_offset; + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { + r = -ENOBUFS; + goto error; + } - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + opt = be16toh(option->code); + optlen = be16toh(option->len); switch (opt) { case SD_DHCP6_OPTION_IAADDR: - addr = new0(DHCP6Address, 1); if (!addr) { r = -ENOMEM; @@ -283,8 +284,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } LIST_INIT(addresses, addr); - - memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); + memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); lt_valid = be32toh(addr->iaaddr.lifetime_valid); lt_pref = be32toh(addr->iaaddr.lifetime_valid); @@ -302,10 +302,12 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < sizeof(status)) - break; + if (optlen < sizeof(status)) { + r = -ENOMSG; + goto error; + } - status = (*buf)[0] << 8 | (*buf)[1]; + status = option->data[0] << 8 | option->data[1]; if (status) { log_dhcp6_client(client, "IA status %d", status); @@ -320,16 +322,9 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; } - *buflen -= optlen; - *buf += optlen; + i += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; - - if (*buflen) - r = -ENOMSG; - if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) { lt_t1 = lt_min / 2; lt_t2 = lt_min / 10 * 8; @@ -341,9 +336,6 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } error: - *buf += *buflen; - *buflen = 0; - return r; } diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 7bbf183996..5f7e809ba1 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -34,6 +34,7 @@ struct DHCP6Message { } _packed_; be32_t transaction_id; }; + uint8_t options[]; } _packed_; typedef struct DHCP6Message DHCP6Message; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 87f37dc0d4..9c758a4d0b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -725,23 +725,32 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + size_t pos = 0; int r; - uint8_t *optval, *option, *id = NULL; - uint16_t optcode, status; - size_t optlen, id_len; bool clientid = false; - be32_t iaid_lease; + uint8_t *id = NULL; + size_t id_len; assert(client); assert(message); assert(len >= sizeof(DHCP6Message)); assert(lease); - option = (uint8_t *)message + sizeof(DHCP6Message); len -= sizeof(DHCP6Message); - while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + uint16_t optcode, optlen, status; + uint8_t *optval; + be32_t iaid_lease; + + if (len < sizeof(DHCP6Option) || len < sizeof(DHCP6Option) + be16toh(option->len)) + return -ENOBUFS; + + optcode = be16toh(option->code); + optlen = be16toh(option->len); + optval = option->data; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -779,7 +788,7 @@ static int client_parse_message( if (optlen != 1) return -EINVAL; - r = dhcp6_lease_set_preference(lease, *optval); + r = dhcp6_lease_set_preference(lease, optval[0]); if (r < 0) return r; @@ -806,8 +815,7 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(&optval, &optlen, optcode, - &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia); if (r < 0 && r != -ENOMSG) return r; @@ -859,11 +867,9 @@ static int client_parse_message( break; } + pos += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; - if (r < 0 || !clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index a0418ecdb9..77084e53c7 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -217,14 +217,13 @@ static uint8_t fqdn_wire[16] = { static int test_advertise_option(sd_event *e) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; DHCP6Message *advertise = (DHCP6Message *)msg_advertise; - uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message); - uint16_t optcode; - size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); + size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0; be32_t val; uint8_t preference = 255; struct in6_addr addr; uint32_t lt_pref, lt_valid; int r; + uint8_t *opt; bool opt_clientid = false; struct in6_addr *addrs; char **domains; @@ -232,14 +231,19 @@ static int test_advertise_option(sd_event *e) { if (verbose) printf("* %s\n", __FUNCTION__); + assert_se(len >= sizeof(DHCP6Message)); + assert_se(dhcp6_lease_new(&lease) >= 0); assert_se(advertise->type == DHCP6_ADVERTISE); assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5); - while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&advertise->options[pos]; + const uint16_t optcode = be16toh(option->code); + const uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: @@ -261,9 +265,7 @@ static int test_advertise_option(sd_event *e) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(dhcp6_option_parse_ia(&optval, &optlen, - optcode, - &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0); break; @@ -309,11 +311,11 @@ static int test_advertise_option(sd_event *e) { default: break; } - } - - assert_se(r == -ENOMSG); + pos += sizeof(*option) + optlen; + } + assert_se(pos == len); assert_se(opt_clientid); sd_dhcp6_lease_reset_address_iter(lease); @@ -415,15 +417,11 @@ static int test_client_send_reply(DHCP6Message *request) { return 0; } -static int test_client_verify_request(DHCP6Message *request, uint8_t *option, - size_t len) { +static int test_client_verify_request(DHCP6Message *request, size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_iana = false, found_serverid = false, found_elapsed_time = false, found_fqdn = false; - int r; struct in6_addr addr; be32_t val; uint32_t lt_pref, lt_valid; @@ -432,8 +430,14 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -458,8 +462,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(!dhcp6_option_parse_ia(&optval, &optlen, - optcode, &lease->ia)); + assert_se(!dhcp6_option_parse_ia(option, &lease->ia)); break; @@ -489,9 +492,10 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); @@ -526,19 +530,21 @@ static int test_client_send_advertise(DHCP6Message *solicit) { return 0; } -static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, - size_t len) { - uint8_t *optval; - uint16_t optcode; - size_t optlen; +static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) { bool found_clientid = false, found_iana = false, found_elapsed_time = false, found_fqdn = false; - int r; + size_t pos = 0; assert_se(solicit->type == DHCP6_SOLICIT); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&solicit->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -578,9 +584,11 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_iana && found_elapsed_time); return 0; @@ -623,17 +631,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); + } static int test_client_verify_information_request(DHCP6Message *information_request, - uint8_t *option, size_t len) { + size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_elapsed_time = false; - int r; struct in6_addr addr; uint32_t lt_pref, lt_valid; @@ -641,8 +647,14 @@ static int test_client_verify_information_request(DHCP6Message *information_requ assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&information_request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -671,9 +683,11 @@ static int test_client_verify_information_request(DHCP6Message *information_requ break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_elapsed_time); sd_dhcp6_lease_reset_address_iter(lease); @@ -689,7 +703,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; DHCP6Message *message; - uint8_t *option; assert_se(s == test_dhcp_fd[0]); assert_se(server_address); @@ -699,21 +712,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); message = (DHCP6Message *)packet; - option = (uint8_t *)(message + 1); - len -= sizeof(DHCP6Message); assert_se(message->transaction_id & 0x00ffffff); if (test_client_message_num == 0) { - test_client_verify_information_request(message, option, len); + test_client_verify_information_request(message, len); test_client_send_reply(message); test_client_message_num++; } else if (test_client_message_num == 1) { - test_client_verify_solicit(message, option, len); + test_client_verify_solicit(message, len); test_client_send_advertise(message); test_client_message_num++; } else if (test_client_message_num == 2) { - test_client_verify_request(message, option, len); + test_client_verify_request(message, len); test_client_send_reply(message); test_client_message_num++; } |