diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index ced85fd3af..2880bc4770 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -102,7 +102,7 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_status(DHCP6Option *option, size_t len); -int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code); 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 0ec36507cd..f763474e6e 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -417,13 +417,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } -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; +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) { uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; + uint16_t iatype, optlen; + size_t iaaddr_offset; + int r = 0, status; + size_t i, len; + uint16_t opt; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); @@ -533,11 +533,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); if (status < 0) return status; + if (status > 0) { + if (ret_status_code) + *ret_status_code = status; + log_dhcp6_client(client, "IA status %s", dhcp6_message_status_to_string(status)); - return -EINVAL; + return 0; } break; @@ -581,7 +585,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } - return 0; + if (ret_status_code) + *ret_status_code = 0; + + return 1; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 13d8f65288..76a16a18c1 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -998,10 +998,11 @@ static int client_parse_message( size_t len, sd_dhcp6_lease *lease) { + uint16_t ia_na_status = 0, ia_pd_status = 0; uint32_t lt_t1 = ~0, lt_t2 = ~0; + usec_t irt = IRT_DEFAULT; bool clientid = false; size_t pos = 0; - usec_t irt = IRT_DEFAULT; int r; assert(client); @@ -1015,8 +1016,8 @@ static int client_parse_message( DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; be32_t iaid_lease; + int status; uint8_t *optval; - int status; if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; @@ -1093,10 +1094,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -1121,10 +1127,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->pd); + r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -1188,6 +1199,11 @@ static int client_parse_message( pos += offsetof(DHCP6Option, data) + optlen; } + if (ia_na_status > 0 && ia_pd_status > 0) { + log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring."); + return -EINVAL; + } + if (!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 4b40e31c12..56c8c978e5 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -247,17 +247,17 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); - assert_se(r == -EINVAL); + r = dhcp6_option_parse_ia(option, &ia, NULL); + assert_se(r == 0); assert_se(ia.addresses == NULL); option->len = htobe16(17); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r == -ENOBUFS); assert_se(ia.addresses == NULL); option->len = htobe16(sizeof(DHCP6Option)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r == -ENOBUFS); assert_se(ia.addresses == NULL); @@ -265,7 +265,7 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r >= 0); assert_se(ia.addresses == NULL); @@ -273,7 +273,7 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r >= 0); assert_se(ia.addresses != NULL); dhcp6_lease_free_ia(&ia); @@ -282,8 +282,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &pd); - assert_se(r == 0); + r = dhcp6_option_parse_ia(option, &pd, NULL); + assert_se(r >= 0); assert_se(pd.addresses != NULL); assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); @@ -294,8 +294,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &pd); - assert_se(r == 0); + r = dhcp6_option_parse_ia(option, &pd, NULL); + assert_se(r >= 0); assert_se(pd.addresses != NULL); dhcp6_lease_free_ia(&pd); @@ -364,15 +364,15 @@ static int test_advertise_option(sd_event *e) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; DHCP6Message *advertise = (DHCP6Message *)msg_advertise; 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; const struct in6_addr *addrs; + uint8_t preference = 255; + struct in6_addr addr; char **domains; + uint8_t *opt; + int r; + be32_t val; log_debug("/* %s */", __func__); @@ -410,7 +410,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(option, &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0); break; @@ -563,12 +563,12 @@ static int test_client_send_reply(DHCP6Message *request) { static int test_client_verify_request(DHCP6Message *request, size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - size_t pos = 0; bool found_clientid = false, found_iana = false, found_serverid = false, found_elapsed_time = false, found_fqdn = false; - struct in6_addr addr; - be32_t val; uint32_t lt_pref, lt_valid; + struct in6_addr addr; + size_t pos = 0; + be32_t val; log_debug("/* %s */", __func__); @@ -606,7 +606,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(!dhcp6_option_parse_ia(option, &lease->ia)); + assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0); break;