From 84483215715162038215b90c86290c5bb4e1c651 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 5 Feb 2022 14:24:42 +0900 Subject: [PATCH 01/66] sd-dhcp6-client: add missing address existence check This adds similar check as 58da18251f468de9de4cc7b36804c924e2fd4421. --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 84bc739bba..5a15f15921 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -819,7 +819,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { case DHCP6_STATE_REBIND: message->type = DHCP6_MESSAGE_REBIND; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); if (r < 0) return r; From 6fcf356b9ab8490350190c532981b256534bd162 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 5 Feb 2022 14:29:33 +0900 Subject: [PATCH 02/66] sd-dhcp6-client: trigger assertion whn invalid IA type is provided If the condition hits, then it is an error in coding, instead of a user misconfiguration or invalid receivd message. --- src/libsystemd-network/dhcp6-option.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 28fe036a40..2ba6bd2aaa 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -317,7 +317,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { break; default: - return -EINVAL; + assert_not_reached(); } if (*buflen < offsetof(DHCP6Option, data) + len) From 5e4d135c60aa947b70fa95fbd71f93a333db2b1e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 5 Feb 2022 14:19:59 +0900 Subject: [PATCH 03/66] sd-dhcp6-client: introduce two helpers to create message --- src/libsystemd-network/sd-dhcp6-client.c | 264 +++++++++-------------- 1 file changed, 106 insertions(+), 158 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5a15f15921..4a5bb463e6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -662,16 +662,94 @@ static void client_stop(sd_dhcp6_client *client, int error) { client_reset(client); } +static int client_append_common_options_in_managed_mode( + sd_dhcp6_client *client, + uint8_t **opt, + size_t *optlen, + const DHCP6IA *ia_na, + const DHCP6IA *ia_pd, + const DHCP6Address *hint_pd_prefix) { + + int r; + + assert(client); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + assert(opt); + assert(optlen); + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { + r = dhcp6_option_append_ia(opt, optlen, ia_na); + if (r < 0) + return r; + } + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { + r = dhcp6_option_append_pd(opt, optlen, ia_pd, hint_pd_prefix); + if (r < 0) + return r; + } + + if (client->fqdn) { + r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->user_class) { + r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + if (r < 0) + return r; + } + + return 0; +} + +static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) { + assert(client); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + return DHCP6_MESSAGE_INFORMATION_REQUEST; + case DHCP6_STATE_SOLICITATION: + return DHCP6_MESSAGE_SOLICIT; + case DHCP6_STATE_REQUEST: + return DHCP6_MESSAGE_REQUEST; + case DHCP6_STATE_RENEW: + return DHCP6_MESSAGE_RENEW; + case DHCP6_STATE_REBIND: + return DHCP6_MESSAGE_REBIND; + default: + return -EINVAL; + } +} + static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; + DHCP6MessageType message_type; struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; - int r; usec_t elapsed_usec; be16_t elapsed_time; + int r; assert(client); @@ -685,184 +763,47 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { message->transaction_id = client->transaction_id; - switch(client->state) { + message_type = client_message_type_from_state(client); + if (message_type < 0) + return message_type; + + message->type = message_type; + + switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - message->type = DHCP6_MESSAGE_INFORMATION_REQUEST; - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - break; case DHCP6_STATE_SOLICITATION: - message->type = DHCP6_MESSAGE_SOLICIT; - - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->ia_na); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, - client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); - if (r < 0) - return r; - } - + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, &client->ia_na, + &client->ia_pd, &client->hint_pd_prefix); + if (r < 0) + return r; break; case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - if (client->state == DHCP6_STATE_REQUEST) - message->type = DHCP6_MESSAGE_REQUEST; - else - message->type = DHCP6_MESSAGE_RENEW; - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } - - break; - + _fallthrough_; case DHCP6_STATE_REBIND: - message->type = DHCP6_MESSAGE_REBIND; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } + assert(client->lease); + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + client->lease->ia.addresses ? &client->lease->ia : NULL, + client->lease->pd.addresses ? &client->lease->pd : NULL, + NULL); + if (r < 0) + return r; break; case DHCP6_STATE_STOPPED: @@ -872,6 +813,13 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { assert_not_reached(); } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); + if (r < 0) + return r; + } + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, client->req_opts_len * sizeof(be16_t), client->req_opts); From 43b7a412b11119151535f64d9cfaa6456877070a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 4 Feb 2022 10:15:49 +0900 Subject: [PATCH 04/66] sd-dhcp6-client: fix typo ia_pd -> ia_na Fortunately, currently both ia_pd and ia_na in sd_dhcp6_client are equivalent now. --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 4a5bb463e6..f84cebcef4 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1153,7 +1153,7 @@ int client_parse_message( break; } - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia); + r = dhcp6_option_parse_ia(client, client->ia_na.ia_na.id, optcode, optlen, optval, &ia); if (r == -ENOMEM) return r; if (r < 0) From 7c999d38fabb7ea8b589348499698f973b3447a2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 4 Feb 2022 10:17:44 +0900 Subject: [PATCH 05/66] sd-dhcp6-client: rename ia -> ia_na, pd -> ia_pd in sd_dhcp6_lease The previous naming is quite misleading. --- src/libsystemd-network/dhcp6-lease-internal.h | 4 +- src/libsystemd-network/sd-dhcp6-client.c | 48 +++++++++---------- src/libsystemd-network/sd-dhcp6-lease.c | 10 ++-- src/libsystemd-network/test-dhcp6-client.c | 4 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 8ae2ecd6d9..0e3540d95e 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -23,8 +23,8 @@ struct sd_dhcp6_lease { triple_timestamp timestamp; struct in6_addr server_address; - DHCP6IA ia; - DHCP6IA pd; + DHCP6IA ia_na; + DHCP6IA ia_pd; DHCP6Address *addr_iter; DHCP6Address *prefix_iter; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index f84cebcef4..ae7e3bf446 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -799,8 +799,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { assert(client->lease); r = client_append_common_options_in_managed_mode(client, &opt, &optlen, - client->lease->ia.addresses ? &client->lease->ia : NULL, - client->lease->pd.addresses ? &client->lease->pd : NULL, + client->lease->ia_na.addresses ? &client->lease->ia_na : NULL, + client->lease->ia_pd.addresses ? &client->lease->ia_pd : NULL, NULL); if (r < 0) return r; @@ -971,7 +971,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { uint32_t expire = 0; - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); + r = dhcp6_lease_ia_rebind_expire(&client->lease->ia_na, &expire); if (r < 0) { client_stop(client, r); return 0; @@ -1159,16 +1159,16 @@ int client_parse_message( if (r < 0) continue; - if (lease->ia.addresses) { + if (lease->ia_na.addresses) { log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); continue; } - lease->ia = ia; + lease->ia_na = ia; ia = (DHCP6IA) {}; - lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_na.ia_na.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_na.ia_na.lifetime_t2)); break; } @@ -1186,16 +1186,16 @@ int client_parse_message( if (r < 0) continue; - if (lease->pd.addresses) { + if (lease->ia_pd.addresses) { log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); continue; } - lease->pd = ia; + lease->ia_pd = ia; ia = (DHCP6IA) {}; - lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd.ia_pd.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd.ia_pd.lifetime_t2)); break; } @@ -1267,17 +1267,17 @@ int client_parse_message( return log_dhcp6_client_errno(client, r, "%s has no server id", dhcp6_message_type_to_string(message->type)); - if (!lease->ia.addresses && !lease->pd.addresses) + if (!lease->ia_na.addresses && !lease->ia_pd.addresses) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - if (lease->ia.addresses) { - lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); + if (lease->ia_na.addresses) { + lease->ia_na.ia_na.lifetime_t1 = htobe32(lt_t1); + lease->ia_na.ia_na.lifetime_t2 = htobe32(lt_t2); } - if (lease->pd.addresses) { - lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); + if (lease->ia_pd.addresses) { + lease->ia_pd.ia_pd.lifetime_t1 = htobe32(lt_t1); + lease->ia_pd.ia_pd.lifetime_t2 = htobe32(lt_t2); } } @@ -1540,16 +1540,16 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, assert_return(client, -EINVAL); assert_return(client->lease, -EINVAL); - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia_na.addresses) { + *lifetime_t1 = be32toh(client->lease->ia_na.ia_na.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_na.ia_na.lifetime_t2); return 0; } - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->ia_pd.addresses) { + *lifetime_t1 = be32toh(client->lease->ia_pd.ia_pd.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_pd.ia_pd.lifetime_t2); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index a47e6a0199..4f5b5385b2 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -193,7 +193,7 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { if (lease) - lease->addr_iter = lease->ia.addresses; + lease->addr_iter = lease->ia_na.addresses; } int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, @@ -224,7 +224,7 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { if (lease) - lease->prefix_iter = lease->pd.addresses; + lease->prefix_iter = lease->ia_pd.addresses; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -402,8 +402,8 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { free(lease->clientid); free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); + dhcp6_lease_free_ia(&lease->ia_na); + dhcp6_lease_free_ia(&lease->ia_pd); free(lease->dns); free(lease->fqdn); strv_free(lease->domains); @@ -425,7 +425,7 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) { lease->n_ref = 1; - LIST_HEAD_INIT(lease->ia.addresses); + LIST_HEAD_INIT(lease->ia_na.addresses); *ret = lease; return 0; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index bcd0134a8d..274109d13a 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -527,7 +527,7 @@ static void test_advertise_option(sd_event *e) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia_na) >= 0); break; } @@ -708,7 +708,7 @@ static void test_client_verify_request(DHCP6Message *request, size_t len) { assert_se(!memcmp(optval + 8, &val, sizeof(val))); /* Then, this should refuse all addresses. */ - assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia) == -ENODATA); + assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia_na) == -ENODATA); break; From 049fddfa7d18a5111bf359ca014037c8cbdcf043 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 4 Feb 2022 10:40:33 +0900 Subject: [PATCH 06/66] sd-dhcp6-lease: convert assert_return() -> assert() in non-public functions --- src/libsystemd-network/dhcp6-lease-internal.h | 4 +- src/libsystemd-network/sd-dhcp6-client.c | 8 +- src/libsystemd-network/sd-dhcp6-lease.c | 129 +++++++++--------- 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 0e3540d95e..ffe88f31c7 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -48,9 +48,9 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret); int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret); int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ae7e3bf446..eb3b003722 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1365,9 +1365,13 @@ static int client_receive_advertise( if (r < 0) return r; - r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (client->lease) { + r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (r < 0) + return r; + } - if (r < 0 || pref_advertise > pref_lease) { + if (!client->lease || pref_advertise > pref_lease) { sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); r = 0; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 4f5b5385b2..3cb30348a9 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -36,8 +36,8 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { DHCP6Address *addr; uint32_t valid = 0, t; - assert_return(ia, -EINVAL); - assert_return(expire, -EINVAL); + assert(ia); + assert(expire); LIST_FOREACH(addresses, addr, ia->addresses) { t = be32toh(addr->iaaddr.lifetime_valid); @@ -74,9 +74,9 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { uint8_t *clientid; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id); + assert(len > 0); clientid = memdup(id, len); if (!clientid) @@ -89,7 +89,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->clientid) return -ENODATA; @@ -105,9 +105,9 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { uint8_t *serverid; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id); + assert(len > 0); serverid = memdup(id, len); if (!serverid) @@ -120,7 +120,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->serverid) return -ENODATA; @@ -129,65 +129,58 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re *ret_id = lease->serverid; if (ret_len) *ret_len = lease->serverid_len; - return 0; } int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { - assert_return(lease, -EINVAL); + assert(lease); lease->preference = preference; - return 0; } -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(preference, -EINVAL); - - if (!lease) - return -EINVAL; - - *preference = lease->preference; +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) { + assert(lease); + assert(ret); + *ret = lease->preference; return 0; } int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { - assert_return(lease, -EINVAL); + assert(lease); lease->rapid_commit = true; - return 0; } -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { - assert_return(lease, -EINVAL); - assert_return(rapid_commit, -EINVAL); - - *rapid_commit = lease->rapid_commit; +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) { + assert(lease); + assert(ret); + *ret = lease->rapid_commit; return 0; } -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_address( + sd_dhcp6_lease *lease, + struct in6_addr *ret_addr, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->addr_iter) return -ENOMSG; - memcpy(addr, &lease->addr_iter->iaaddr.address, - sizeof(struct in6_addr)); - *lifetime_preferred = - be32toh(lease->addr_iter->iaaddr.lifetime_preferred); - *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + if (ret_addr) + *ret_addr = lease->addr_iter->iaaddr.address; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); lease->addr_iter = lease->addr_iter->addresses_next; - return 0; } @@ -196,29 +189,28 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia_na.addresses; } -int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, - uint8_t *prefix_len, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_pd( + sd_dhcp6_lease *lease, + struct in6_addr *ret_prefix, + uint8_t *ret_prefix_len, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(prefix, -EINVAL); - assert_return(prefix_len, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->prefix_iter) return -ENOMSG; - memcpy(prefix, &lease->prefix_iter->iapdprefix.address, - sizeof(struct in6_addr)); - *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; - *lifetime_preferred = - be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); - *lifetime_valid = - be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + if (ret_prefix) + *ret_prefix = lease->prefix_iter->iapdprefix.address; + if (ret_prefix_len) + *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); lease->prefix_iter = lease->prefix_iter->addresses_next; - return 0; } @@ -228,8 +220,8 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -253,8 +245,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t _cleanup_strv_free_ char **domains = NULL; int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -280,8 +272,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); for (size_t offset = 0; offset < optlen;) { const uint8_t *subval; @@ -326,8 +318,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt } int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -367,11 +359,14 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { } int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - int r; char *fqdn; + int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); + + if (optlen == 0) + return 0; if (optlen < 2) return -ENODATA; @@ -419,6 +414,8 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; + assert(ret); + lease = new0(sd_dhcp6_lease, 1); if (!lease) return -ENOMEM; From 93bd7c4192c9afc41be1b317278a7ed3e829b971 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 4 Feb 2022 10:45:40 +0900 Subject: [PATCH 07/66] sd-dhcp6-lease: reset client or server ID when length is zero Addresses https://github.com/systemd/systemd/pull/22406#issuecomment-1029391091. --- src/libsystemd-network/sd-dhcp6-lease.c | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 3cb30348a9..7d6a6f7f9c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -72,15 +72,16 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { } int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *clientid; + uint8_t *clientid = NULL; assert(lease); - assert(id); - assert(len > 0); + assert(id || len == 0); - clientid = memdup(id, len); - if (!clientid) - return -ENOMEM; + if (len > 0) { + clientid = memdup(id, len); + if (!clientid) + return -ENOMEM; + } free_and_replace(lease->clientid, clientid); lease->clientid_len = len; @@ -103,15 +104,16 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re } int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *serverid; + uint8_t *serverid = NULL; assert(lease); - assert(id); - assert(len > 0); + assert(id || len == 0); - serverid = memdup(id, len); - if (!serverid) - return -ENOMEM; + if (len > 0) { + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + } free_and_replace(lease->serverid, serverid); lease->serverid_len = len; From 4b0f27173c25ec21f1181ee3d438d413d4e4dd6c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 13:38:08 +0900 Subject: [PATCH 08/66] sd-dhcp6-client: unify IA option header This also fixes possible unaligned read of message. --- src/libsystemd-network/dhcp6-internal.h | 21 +- src/libsystemd-network/dhcp6-option.c | 246 +++++++++------------ src/libsystemd-network/sd-dhcp6-client.c | 38 ++-- src/libsystemd-network/sd-dhcp6-lease.c | 2 +- src/libsystemd-network/test-dhcp6-client.c | 6 +- 5 files changed, 125 insertions(+), 188 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index f943409856..7ceb3a8b28 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -62,32 +62,15 @@ struct DHCP6Address { }; }; -/* Non-temporary Address option */ -struct ia_na { +struct ia_header { be32_t id; be32_t lifetime_t1; be32_t lifetime_t2; } _packed_; -/* Prefix Delegation option */ -struct ia_pd { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Temporary Address option */ -struct ia_ta { - be32_t id; -} _packed_; - typedef struct DHCP6IA { uint16_t type; - union { - struct ia_na ia_na; - struct ia_pd ia_pd; - struct ia_ta ia_ta; - }; + struct ia_header header; LIST_HEAD(DHCP6Address, addresses); } DHCP6IA; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 2ba6bd2aaa..e7a55c1b02 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -284,12 +284,10 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { size_t ia_buflen, ia_addrlen = 0; - struct ia_na ia_na; - struct ia_ta ia_ta; + struct ia_header header; DHCP6Address *addr; uint8_t *ia_hdr; uint16_t len; - void *p; int r; assert_return(buf, -EINVAL); @@ -301,19 +299,17 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: - len = DHCP6_OPTION_IA_NA_LEN; - ia_na = (struct ia_na) { - .id = ia->ia_na.id, + len = sizeof(struct ia_header); + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_na; break; case SD_DHCP6_OPTION_IA_TA: - len = DHCP6_OPTION_IA_TA_LEN; - ia_ta = (struct ia_ta) { - .id = ia->ia_ta.id, + len = sizeof(be32_t); /* IA_TA does not have lifetime. */ + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_ta; break; default: @@ -329,7 +325,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, p, len); + memcpy(*buf, &header, len); *buf += len; *buflen -= len; @@ -386,7 +382,7 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Add } int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { - struct ia_pd ia_pd; + struct ia_header header; size_t len, pd_buflen; uint8_t *pd_hdr; int r; @@ -398,10 +394,10 @@ int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, con assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); /* Do not set T1 and T2. */ - ia_pd = (struct ia_pd) { - .id = pd->ia_pd.id, + len = sizeof(struct ia_header); + header = (struct ia_header) { + .id = pd->header.id, }; - len = sizeof(struct ia_pd); if (*buflen < offsetof(DHCP6Option, data) + len) return -ENOBUFS; @@ -413,10 +409,9 @@ int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, con *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, &ia_pd, len); - - *buf += sizeof(struct ia_pd); - *buflen -= sizeof(struct ia_pd); + memcpy(*buf, &header, len); + *buf += len; + *buflen -= len; DHCP6Address *prefix; LIST_FOREACH(addresses, prefix, pd->addresses) { @@ -601,7 +596,7 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { int r; - assert(buf); + assert(buf || buflen == 0); for(size_t offset = 0; offset < buflen;) { const uint8_t *data; @@ -619,15 +614,15 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t r = dhcp6_option_parse_status(data, data_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - /* Let's log but ignore the invalid status option. */ - log_dhcp6_client_errno(client, r, - "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA address or PD prefix option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + /* Let's log but ignore the invalid status option. */ + log_dhcp6_client_errno(client, r, + "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); break; } default: @@ -638,19 +633,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return 0; } -static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; uint32_t lt_valid, lt_pref; - DHCP6Address *a; int r; - assert(data); - assert(ret); + assert(ia); + assert(data || len == 0); + + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address sub-option in an invalid option, ignoring."); if (len < sizeof(struct iaaddr)) return -EBADMSG; - lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred); + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + + lt_valid = be32toh(a->iaaddr.lifetime_valid); + lt_pref = be32toh(a->iaaddr.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -667,27 +672,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t return r; } + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); + return 0; +} + +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; + uint32_t lt_valid, lt_pref; + int r; + + assert(ia); + assert(data || len == 0); + + if (ia->type != SD_DHCP6_OPTION_IA_PD) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an PD prefix sub-option in an invalid option, ignoring"); + + if (len < sizeof(struct iapdprefix)) + return -EBADMSG; + a = new(DHCP6Address, 1); if (!a) return -ENOMEM; - LIST_INIT(addresses, a); - memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - *ret = a; - return 0; -} - -static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { - uint32_t lt_valid, lt_pref; - DHCP6Address *a; - int r; - - if (len < sizeof(struct iapdprefix)) - return -ENOMSG; - - lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); + lt_valid = be32toh(a->iapdprefix.lifetime_valid); + lt_pref = be32toh(a->iapdprefix.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -704,14 +715,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t return r; } - a = new(DHCP6Address, 1); - if (!a) - return -ENOMEM; - - LIST_INIT(addresses, a); - memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - - *ret = a; + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); return 0; } @@ -725,12 +729,11 @@ int dhcp6_option_parse_ia( _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; - be32_t received_iaid; - size_t offset; + size_t header_len; int r; assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); - assert(option_data); + assert(option_data || option_data_len == 0); assert(ret); /* This will return the following: @@ -743,56 +746,45 @@ int dhcp6_option_parse_ia( switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - - if (option_data_len < DHCP6_OPTION_IA_NA_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_NA_LEN; - - received_iaid = ((const struct ia_na*) option_data)->id; - lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2); - break; - case SD_DHCP6_OPTION_IA_PD: - - if (option_data_len < DHCP6_OPTION_IA_PD_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_PD_LEN; - - received_iaid = ((const struct ia_pd*) option_data)->id; - lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2); + header_len = sizeof(struct ia_header); break; case SD_DHCP6_OPTION_IA_TA: - if (option_data_len < DHCP6_OPTION_IA_TA_LEN) - return -ENOMSG; - - offset = DHCP6_OPTION_IA_TA_LEN; - - received_iaid = ((const struct ia_ta*) option_data)->id; - lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */ + header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */ break; default: assert_not_reached(); } + if (option_data_len < header_len) + return -EBADMSG; + + ia.type = option_code; + memcpy(&ia.header, option_data, header_len); + /* According to RFC8415, IAs which do not match the client's IAID should be ignored, * but not necessary to ignore or refuse the whole message. */ - if (received_iaid != iaid) + if (ia.header.id != iaid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), "Received an IA option with a different IAID " "from the one chosen by the client, ignoring."); + /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ + lt_t1 = be32toh(ia.header.lifetime_t1); + lt_t2 = be32toh(ia.header.lifetime_t2); + if (lt_t1 > lt_t2) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", lt_t1, lt_t2); + if (lt_t1 == 0 && lt_t2 > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.", + lt_t2); - for (; offset < option_data_len;) { + for (size_t offset = header_len; offset < option_data_len;) { const uint8_t *subdata; size_t subdata_len; uint16_t subopt; @@ -803,41 +795,25 @@ int dhcp6_option_parse_ia( switch (subopt) { case SD_DHCP6_OPTION_IAADDR: { - DHCP6Address *a; - - if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { - log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring."); - continue; - } - - r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_address(client, &ia, subdata, subdata_len); if (r == -ENOMEM) return r; if (r < 0) - /* Ignore the sub-option on non-critical errors. */ + /* Ignore non-critical errors in the sub-option. */ continue; - lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + lt_min = MIN(lt_min, be32toh(ia.addresses->iaaddr.lifetime_valid)); break; } case SD_DHCP6_OPTION_IA_PD_PREFIX: { - DHCP6Address *a; - - if (option_code != SD_DHCP6_OPTION_IA_PD) { - log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring"); - continue; - } - - r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_pdprefix(client, &ia, subdata, subdata_len); if (r == -ENOMEM) return r; if (r < 0) - /* Ignore the sub-option on non-critical errors. */ + /* Ignore non-critical errors in the sub-option. */ continue; - lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + lt_min = MIN(lt_min, be32toh(ia.addresses->iapdprefix.lifetime_valid)); break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -846,14 +822,14 @@ int dhcp6_option_parse_ia( r = dhcp6_option_parse_status(subdata, subdata_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - log_dhcp6_client_errno(client, r, - "Received an IA option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); break; } default: @@ -865,46 +841,24 @@ int dhcp6_option_parse_ia( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), "Received an IA option without valid IA addresses or PD prefixes, ignoring."); - if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) && - lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) { + if (lt_t2 == 0 && IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD)) { + + /* addresses or prefixes with zero valid lifetime are already ignored. */ + assert(lt_min != UINT32_MAX); + lt_t1 = lt_min / 2; lt_t2 = lt_min / 10 * 8; + ia.header.lifetime_t1 = htobe32(lt_t1); + ia.header.lifetime_t1 = htobe32(lt_t2); + log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); } - switch(option_code) { - case SD_DHCP6_OPTION_IA_NA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_na.id = iaid, - .ia_na.lifetime_t1 = htobe32(lt_t1), - .ia_na.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_TA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_ta.id = iaid, - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_PD: - *ret = (DHCP6IA) { - .type = option_code, - .ia_pd.id = iaid, - .ia_pd.lifetime_t1 = htobe32(lt_t1), - .ia_pd.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - default: - assert_not_reached(); - } - + *ret = ia; + ret->addresses = TAKE_PTR(ia.addresses); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index eb3b003722..65b8cce165 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -421,8 +421,8 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - client->ia_na.ia_na.id = htobe32(iaid); - client->ia_pd.ia_pd.id = htobe32(iaid); + client->ia_na.header.id = htobe32(iaid); + client->ia_pd.header.id = htobe32(iaid); client->iaid_set = true; return 0; @@ -441,7 +441,7 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { if (!client->iaid_set) return -ENODATA; - *iaid = be32toh(client->ia_na.ia_na.id); + *iaid = be32toh(client->ia_na.header.id); return 0; } @@ -1065,8 +1065,8 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { if (r < 0) return r; - client->ia_na.ia_na.id = iaid; - client->ia_pd.ia_pd.id = iaid; + client->ia_na.header.id = iaid; + client->ia_pd.header.id = iaid; client->iaid_set = true; return 0; @@ -1153,7 +1153,7 @@ int client_parse_message( break; } - r = dhcp6_option_parse_ia(client, client->ia_na.ia_na.id, optcode, optlen, optval, &ia); + r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); if (r == -ENOMEM) return r; if (r < 0) @@ -1167,8 +1167,8 @@ int client_parse_message( lease->ia_na = ia; ia = (DHCP6IA) {}; - lt_t1 = MIN(lt_t1, be32toh(lease->ia_na.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_na.ia_na.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_na.header.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_na.header.lifetime_t2)); break; } @@ -1180,7 +1180,7 @@ int client_parse_message( break; } - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia); + r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); if (r == -ENOMEM) return r; if (r < 0) @@ -1194,8 +1194,8 @@ int client_parse_message( lease->ia_pd = ia; ia = (DHCP6IA) {}; - lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd.ia_pd.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd.header.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd.header.lifetime_t2)); break; } @@ -1271,13 +1271,13 @@ int client_parse_message( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); if (lease->ia_na.addresses) { - lease->ia_na.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia_na.ia_na.lifetime_t2 = htobe32(lt_t2); + lease->ia_na.header.lifetime_t1 = htobe32(lt_t1); + lease->ia_na.header.lifetime_t2 = htobe32(lt_t2); } if (lease->ia_pd.addresses) { - lease->ia_pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->ia_pd.ia_pd.lifetime_t2 = htobe32(lt_t2); + lease->ia_pd.header.lifetime_t1 = htobe32(lt_t1); + lease->ia_pd.header.lifetime_t2 = htobe32(lt_t2); } } @@ -1545,15 +1545,15 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, assert_return(client->lease, -EINVAL); if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia_na.addresses) { - *lifetime_t1 = be32toh(client->lease->ia_na.ia_na.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_na.ia_na.lifetime_t2); + *lifetime_t1 = be32toh(client->lease->ia_na.header.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_na.header.lifetime_t2); return 0; } if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->ia_pd.addresses) { - *lifetime_t1 = be32toh(client->lease->ia_pd.ia_pd.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_pd.ia_pd.lifetime_t2); + *lifetime_t1 = be32toh(client->lease->ia_pd.header.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_pd.header.lifetime_t2); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 7d6a6f7f9c..96c32cf8f9 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -45,7 +45,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { valid = t; } - t = be32toh(ia->ia_na.lifetime_t2); + t = be32toh(ia->header.lifetime_t2); if (t > valid) return -EINVAL; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 274109d13a..91eec94058 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -351,9 +351,9 @@ static void test_option_status(void) { r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); assert_se(r >= 0); assert_se(pd.addresses); - assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); - assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); - assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0); + assert_se(memcmp(&pd.header.id, &option4[4], 4) == 0); + assert_se(memcmp(&pd.header.lifetime_t1, &option4[8], 4) == 0); + assert_se(memcmp(&pd.header.lifetime_t2, &option4[12], 4) == 0); dhcp6_lease_free_ia(&pd); zero(pd); From e5b0b87f516007405ed23849d74348ecca024dd3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 14:18:44 +0900 Subject: [PATCH 09/66] sd-dhcp6-client: introduce dhcp6_ia_free() --- src/libsystemd-network/dhcp6-internal.h | 2 +- src/libsystemd-network/dhcp6-lease-internal.h | 10 +++- src/libsystemd-network/dhcp6-option.c | 37 ++++++------ src/libsystemd-network/sd-dhcp6-client.c | 56 +++++++++---------- src/libsystemd-network/sd-dhcp6-lease.c | 33 +++++------ src/libsystemd-network/test-dhcp6-client.c | 42 ++++++-------- 6 files changed, 89 insertions(+), 91 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 7ceb3a8b28..131922b658 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -101,7 +101,7 @@ int dhcp6_option_parse_ia( uint16_t option_code, size_t option_data_len, const uint8_t *option_data, - DHCP6IA *ret); + DHCP6IA **ret); int dhcp6_option_parse_addresses( const uint8_t *optval, size_t optlen, diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index ffe88f31c7..5d009781b2 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -10,6 +10,7 @@ #include "sd-dhcp6-lease.h" #include "dhcp6-internal.h" +#include "macro.h" struct sd_dhcp6_lease { unsigned n_ref; @@ -23,8 +24,8 @@ struct sd_dhcp6_lease { triple_timestamp timestamp; struct in6_addr server_address; - DHCP6IA ia_na; - DHCP6IA ia_pd; + DHCP6IA *ia_na; + DHCP6IA *ia_pd; DHCP6Address *addr_iter; DHCP6Address *prefix_iter; @@ -41,7 +42,10 @@ struct sd_dhcp6_lease { }; int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); + +void dhcp6_ia_clear_addresses(DHCP6IA *ia); +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index e7a55c1b02..049380cdd9 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -725,9 +725,9 @@ int dhcp6_option_parse_ia( uint16_t option_code, size_t option_data_len, const uint8_t *option_data, - DHCP6IA *ret) { + DHCP6IA **ret) { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; size_t header_len; int r; @@ -761,19 +761,25 @@ int dhcp6_option_parse_ia( if (option_data_len < header_len) return -EBADMSG; - ia.type = option_code; - memcpy(&ia.header, option_data, header_len); + ia = new(DHCP6IA, 1); + if (!ia) + return -ENOMEM; + + *ia = (DHCP6IA) { + .type = option_code, + }; + memcpy(&ia->header, option_data, header_len); /* According to RFC8415, IAs which do not match the client's IAID should be ignored, * but not necessary to ignore or refuse the whole message. */ - if (ia.header.id != iaid) + if (ia->header.id != iaid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), "Received an IA option with a different IAID " "from the one chosen by the client, ignoring."); /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ - lt_t1 = be32toh(ia.header.lifetime_t1); - lt_t2 = be32toh(ia.header.lifetime_t2); + lt_t1 = be32toh(ia->header.lifetime_t1); + lt_t2 = be32toh(ia->header.lifetime_t2); if (lt_t1 > lt_t2) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -795,25 +801,25 @@ int dhcp6_option_parse_ia( switch (subopt) { case SD_DHCP6_OPTION_IAADDR: { - r = dhcp6_option_parse_ia_address(client, &ia, subdata, subdata_len); + r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; if (r < 0) /* Ignore non-critical errors in the sub-option. */ continue; - lt_min = MIN(lt_min, be32toh(ia.addresses->iaaddr.lifetime_valid)); + lt_min = MIN(lt_min, be32toh(ia->addresses->iaaddr.lifetime_valid)); break; } case SD_DHCP6_OPTION_IA_PD_PREFIX: { - r = dhcp6_option_parse_ia_pdprefix(client, &ia, subdata, subdata_len); + r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; if (r < 0) /* Ignore non-critical errors in the sub-option. */ continue; - lt_min = MIN(lt_min, be32toh(ia.addresses->iapdprefix.lifetime_valid)); + lt_min = MIN(lt_min, be32toh(ia->addresses->iapdprefix.lifetime_valid)); break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -837,7 +843,7 @@ int dhcp6_option_parse_ia( } } - if (!ia.addresses) + if (!ia->addresses) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), "Received an IA option without valid IA addresses or PD prefixes, ignoring."); @@ -849,16 +855,15 @@ int dhcp6_option_parse_ia( lt_t1 = lt_min / 2; lt_t2 = lt_min / 10 * 8; - ia.header.lifetime_t1 = htobe32(lt_t1); - ia.header.lifetime_t1 = htobe32(lt_t2); + ia->header.lifetime_t1 = htobe32(lt_t1); + ia->header.lifetime_t1 = htobe32(lt_t2); log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); } - *ret = ia; - ret->addresses = TAKE_PTR(ia.addresses); + *ret = TAKE_PTR(ia); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 65b8cce165..30795082bd 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -799,9 +799,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { assert(client->lease); r = client_append_common_options_in_managed_mode(client, &opt, &optlen, - client->lease->ia_na.addresses ? &client->lease->ia_na : NULL, - client->lease->ia_pd.addresses ? &client->lease->ia_pd : NULL, - NULL); + client->lease->ia_na, client->lease->ia_pd, NULL); if (r < 0) return r; break; @@ -971,7 +969,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { uint32_t expire = 0; - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia_na, &expire); + r = dhcp6_lease_ia_rebind_expire(client->lease->ia_na, &expire); if (r < 0) { client_stop(client, r); return 0; @@ -1146,7 +1144,7 @@ int client_parse_message( break; } case SD_DHCP6_OPTION_IA_NA: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); @@ -1159,21 +1157,21 @@ int client_parse_message( if (r < 0) continue; - if (lease->ia_na.addresses) { + if (lease->ia_na) { log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); continue; } - lease->ia_na = ia; - ia = (DHCP6IA) {}; + dhcp6_ia_free(lease->ia_na); + lease->ia_na = TAKE_PTR(ia); - lt_t1 = MIN(lt_t1, be32toh(lease->ia_na.header.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_na.header.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_na->header.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_na->header.lifetime_t2)); break; } case SD_DHCP6_OPTION_IA_PD: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); @@ -1186,16 +1184,16 @@ int client_parse_message( if (r < 0) continue; - if (lease->ia_pd.addresses) { + if (lease->ia_pd) { log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); continue; } - lease->ia_pd = ia; - ia = (DHCP6IA) {}; + dhcp6_ia_free(lease->ia_pd); + lease->ia_pd = TAKE_PTR(ia); - lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd.header.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd.header.lifetime_t2)); + lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd->header.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd->header.lifetime_t2)); break; } @@ -1267,17 +1265,17 @@ int client_parse_message( return log_dhcp6_client_errno(client, r, "%s has no server id", dhcp6_message_type_to_string(message->type)); - if (!lease->ia_na.addresses && !lease->ia_pd.addresses) + if (!lease->ia_na && !lease->ia_pd) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - if (lease->ia_na.addresses) { - lease->ia_na.header.lifetime_t1 = htobe32(lt_t1); - lease->ia_na.header.lifetime_t2 = htobe32(lt_t2); + if (lease->ia_na) { + lease->ia_na->header.lifetime_t1 = htobe32(lt_t1); + lease->ia_na->header.lifetime_t2 = htobe32(lt_t2); } - if (lease->ia_pd.addresses) { - lease->ia_pd.header.lifetime_t1 = htobe32(lt_t1); - lease->ia_pd.header.lifetime_t2 = htobe32(lt_t2); + if (lease->ia_pd) { + lease->ia_pd->header.lifetime_t1 = htobe32(lt_t1); + lease->ia_pd->header.lifetime_t2 = htobe32(lt_t2); } } @@ -1544,16 +1542,16 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, assert_return(client, -EINVAL); assert_return(client->lease, -EINVAL); - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia_na.addresses) { - *lifetime_t1 = be32toh(client->lease->ia_na.header.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_na.header.lifetime_t2); + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia_na) { + *lifetime_t1 = be32toh(client->lease->ia_na->header.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_na->header.lifetime_t2); return 0; } - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->ia_pd.addresses) { - *lifetime_t1 = be32toh(client->lease->ia_pd.header.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_pd.header.lifetime_t2); + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->ia_pd) { + *lifetime_t1 = be32toh(client->lease->ia_pd->header.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia_pd->header.lifetime_t2); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 96c32cf8f9..87c3a89ace 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -54,21 +54,24 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { return 0; } -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { - DHCP6Address *address; +void dhcp6_ia_clear_addresses(DHCP6IA *ia) { + DHCP6Address *a, *n; + assert(ia); + + LIST_FOREACH_SAFE(addresses, a, n, ia->addresses) + free(a); + + ia->addresses = NULL; +} + +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) { if (!ia) return NULL; - while (ia->addresses) { - address = ia->addresses; + dhcp6_ia_clear_addresses(ia); - LIST_REMOVE(addresses, ia->addresses, address); - - free(address); - } - - return NULL; + return mfree(ia); } int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { @@ -188,7 +191,7 @@ int sd_dhcp6_lease_get_address( void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { if (lease) - lease->addr_iter = lease->ia_na.addresses; + lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; } int sd_dhcp6_lease_get_pd( @@ -218,7 +221,7 @@ int sd_dhcp6_lease_get_pd( void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { if (lease) - lease->prefix_iter = lease->ia_pd.addresses; + lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -399,8 +402,8 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { free(lease->clientid); free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia_na); - dhcp6_lease_free_ia(&lease->ia_pd); + dhcp6_ia_free(lease->ia_na); + dhcp6_ia_free(lease->ia_pd); free(lease->dns); free(lease->fqdn); strv_free(lease->domains); @@ -424,8 +427,6 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) { lease->n_ref = 1; - LIST_HEAD_INIT(lease->ia_na.addresses); - *ret = lease; return 0; } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 91eec94058..53def1b07f 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -297,16 +297,15 @@ static void test_option_status(void) { /* PD prefix status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, }; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; DHCP6Option *option; - DHCP6IA ia, pd; be32_t iaid; - int r = 0; + int r; log_debug("/* %s */", __func__); memcpy(&iaid, option1 + 4, sizeof(iaid)); - zero(ia); option = (DHCP6Option*) option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); @@ -315,55 +314,46 @@ static void test_option_status(void) { r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EINVAL); - assert_se(!ia.addresses); option->len = htobe16(17); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EBADMSG); - assert_se(!ia.addresses); option->len = htobe16(sizeof(DHCP6Option)); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EBADMSG); - assert_se(!ia.addresses); - zero(ia); option = (DHCP6Option*) option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -ENODATA); - assert_se(!ia.addresses); - zero(ia); option = (DHCP6Option*) option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(ia.addresses); - dhcp6_lease_free_ia(&ia); + assert_se(ia); + assert_se(ia->addresses); + ia = dhcp6_ia_free(ia); - zero(pd); option = (DHCP6Option*) option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(pd.addresses); - assert_se(memcmp(&pd.header.id, &option4[4], 4) == 0); - assert_se(memcmp(&pd.header.lifetime_t1, &option4[8], 4) == 0); - assert_se(memcmp(&pd.header.lifetime_t2, &option4[12], 4) == 0); - dhcp6_lease_free_ia(&pd); + assert_se(ia); + assert_se(ia->addresses); + assert_se(memcmp(&ia->header.id, &option4[4], 4) == 0); + assert_se(memcmp(&ia->header.lifetime_t1, &option4[8], 4) == 0); + assert_se(memcmp(&ia->header.lifetime_t2, &option4[12], 4) == 0); + ia = dhcp6_ia_free(ia); - zero(pd); option = (DHCP6Option*) option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(pd.addresses); - dhcp6_lease_free_ia(&pd); + assert_se(ia); + assert_se(ia->addresses); + ia = dhcp6_ia_free(ia); } static void test_client_parse_message_issue_22099(void) { From 877bfc78fdf0e106164593641eb40cd1ac22bbbc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 14:32:36 +0900 Subject: [PATCH 10/66] sd-dhcp6-client: store PD prefix hint in ia_pd And allows to specify multiple hints. --- src/libsystemd-network/dhcp6-internal.h | 2 +- src/libsystemd-network/dhcp6-option.c | 10 +----- src/libsystemd-network/sd-dhcp6-client.c | 42 +++++++++++++++--------- src/systemd/sd-dhcp6-client.h | 2 +- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 131922b658..ad77a9c44c 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -81,7 +81,7 @@ bool dhcp6_option_can_request(uint16_t option); int dhcp6_option_append(uint8_t **buf, size_t *buflen, 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_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix); +int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd); 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 *user_class); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 049380cdd9..f570e78875 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -381,7 +381,7 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Add return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); } -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { +int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd) { struct ia_header header; size_t len, pd_buflen; uint8_t *pd_hdr; @@ -422,14 +422,6 @@ int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, con len += r; } - if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { - r = option_append_pd_prefix(buf, buflen, hint_pd_prefix); - if (r < 0) - return r; - - len += r; - } - return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 30795082bd..b60950740a 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -50,7 +50,6 @@ struct sd_dhcp6_client { int event_priority; int ifindex; char *ifname; - DHCP6Address hint_pd_prefix; struct in6_addr local_address; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; @@ -261,16 +260,32 @@ int sd_dhcp6_client_set_mac( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address) { + const struct in6_addr *pd_prefix) { + + _cleanup_free_ DHCP6Address *prefix = NULL; assert_return(client, -EINVAL); - assert_return(pd_address, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - client->hint_pd_prefix.iapdprefix.address = *pd_address; - client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen; + if (!pd_prefix) { + /* clear previous assignments. */ + dhcp6_ia_clear_addresses(&client->ia_pd); + return 0; + } - return 0; + assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL); + + prefix = new(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + *prefix = (DHCP6Address) { + .iapdprefix.address = *pd_prefix, + .iapdprefix.prefixlen = prefixlen, + }; + + LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix)); + return 1; } int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { @@ -667,8 +682,7 @@ static int client_append_common_options_in_managed_mode( uint8_t **opt, size_t *optlen, const DHCP6IA *ia_na, - const DHCP6IA *ia_pd, - const DHCP6Address *hint_pd_prefix) { + const DHCP6IA *ia_pd) { int r; @@ -688,7 +702,7 @@ static int client_append_common_options_in_managed_mode( } if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { - r = dhcp6_option_append_pd(opt, optlen, ia_pd, hint_pd_prefix); + r = dhcp6_option_append_pd(opt, optlen, ia_pd); if (r < 0) return r; } @@ -778,8 +792,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - r = client_append_common_options_in_managed_mode(client, &opt, &optlen, &client->ia_na, - &client->ia_pd, &client->hint_pd_prefix); + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + &client->ia_na, &client->ia_pd); if (r < 0) return r; break; @@ -799,7 +813,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { assert(client->lease); r = client_append_common_options_in_managed_mode(client, &opt, &optlen, - client->lease->ia_na, client->lease->ia_pd, NULL); + client->lease->ia_na, client->lease->ia_pd); if (r < 0) return r; break; @@ -1804,7 +1818,7 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { free(client->req_opts); free(client->fqdn); free(client->mudurl); - + dhcp6_ia_clear_addresses(&client->ia_pd); ordered_hashmap_free(client->extra_options); strv_free(client->user_class); strv_free(client->vendor_class); @@ -1841,8 +1855,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, .fd = -1, .req_opts_len = ELEMENTSOF(default_req_opts), - .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1, - .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1, .req_opts = TAKE_PTR(req_opts), }; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 0e23c84e64..d857af5acf 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address); + const struct in6_addr *pd_prefix); int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, From 29858a0f9e42552c948493af4c47e5158fa40e57 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 14:56:59 +0900 Subject: [PATCH 11/66] sd-dhcp6-client: unify dhcp6_option_append_{ia,pd}() --- src/libsystemd-network/dhcp6-internal.h | 1 - src/libsystemd-network/dhcp6-option.c | 154 ++++++++++------------- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 3 files changed, 66 insertions(+), 91 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index ad77a9c44c..23738d3197 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -81,7 +81,6 @@ bool dhcp6_option_can_request(uint16_t option); int dhcp6_option_append(uint8_t **buf, size_t *buflen, 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_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd); 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 *user_class); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f570e78875..5099fa152c 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -282,10 +282,66 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash return 0; } +static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { + struct iaaddr a; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(address); + + /* Do not append T1 and T2. */ + a = (struct iaaddr) { + .address = address->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); + if (r < 0) + return r; + + memcpy(*buf, &a, sizeof(struct iaaddr)); + + *buf += sizeof(struct iaaddr); + *buflen -= sizeof(struct iaaddr); + + return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); +} + +static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { + struct iapdprefix p; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(prefix); + + if (prefix->prefixlen == 0) + return -EINVAL; + + /* Do not append T1 and T2. */ + p = (struct iapdprefix) { + .prefixlen = prefix->prefixlen, + .address = prefix->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); + if (r < 0) + return r; + + memcpy(*buf, &p, sizeof(struct iapdprefix)); + + *buf += sizeof(struct iapdprefix); + *buflen -= sizeof(struct iapdprefix); + + return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { - size_t ia_buflen, ia_addrlen = 0; struct ia_header header; - DHCP6Address *addr; + const DHCP6Address *addr; + size_t ia_buflen; uint8_t *ia_hdr; uint16_t len; int r; @@ -299,6 +355,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_PD: len = sizeof(struct ia_header); header = (struct ia_header) { .id = ia->header.id, @@ -322,89 +379,6 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); - - memcpy(*buf, &header, len); - - *buf += len; - *buflen -= len; - - LIST_FOREACH(addresses, addr, ia->addresses) { - struct iaaddr a = { - .address = addr->iaaddr.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; - - memcpy(*buf, &a, sizeof(struct iaaddr)); - - *buf += sizeof(struct iaaddr); - *buflen -= sizeof(struct iaaddr); - - ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr); - } - - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); -} - -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) { - struct iapdprefix p; - int r; - - assert(buf); - assert(*buf); - assert(buflen); - assert(prefix); - - if (prefix->iapdprefix.prefixlen == 0) - return -EINVAL; - - /* Do not append T1 and T2. */ - - p = (struct iapdprefix) { - .prefixlen = prefix->iapdprefix.prefixlen, - .address = prefix->iapdprefix.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); - if (r < 0) - return r; - - memcpy(*buf, &p, sizeof(struct iapdprefix)); - - *buf += sizeof(struct iapdprefix); - *buflen -= sizeof(struct iapdprefix); - - return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); -} - -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd) { - struct ia_header header; - size_t len, pd_buflen; - uint8_t *pd_hdr; - int r; - - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(pd, -EINVAL); - assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); - - /* Do not set T1 and T2. */ - len = sizeof(struct ia_header); - header = (struct ia_header) { - .id = pd->header.id, - }; - - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - pd_hdr = *buf; - pd_buflen = *buflen; - /* The header will be written at the end of this function. */ *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); @@ -413,16 +387,18 @@ int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd) { *buf += len; *buflen -= len; - DHCP6Address *prefix; - LIST_FOREACH(addresses, prefix, pd->addresses) { - r = option_append_pd_prefix(buf, buflen, prefix); + LIST_FOREACH(addresses, addr, ia->addresses) { + if (ia->type == SD_DHCP6_OPTION_IA_PD) + r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + else + r = option_append_ia_address(buf, buflen, &addr->iaaddr); if (r < 0) return r; len += r; } - return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); + return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index b60950740a..89d91f9289 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -702,7 +702,7 @@ static int client_append_common_options_in_managed_mode( } if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { - r = dhcp6_option_append_pd(opt, optlen, ia_pd); + r = dhcp6_option_append_ia(opt, optlen, ia_pd); if (r < 0) return r; } From 126277aceb389137406eeac7703db1c0a0e355b6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 15:01:47 +0900 Subject: [PATCH 12/66] sd-dhcp6-client: always use ENODATA when a lease does not have requested data --- src/libsystemd-network/sd-dhcp6-lease.c | 14 +++++++------- src/libsystemd-network/test-dhcp6-client.c | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 87c3a89ace..4cd6d3e352 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -176,7 +176,7 @@ int sd_dhcp6_lease_get_address( assert_return(lease, -EINVAL); if (!lease->addr_iter) - return -ENOMSG; + return -ENODATA; if (ret_addr) *ret_addr = lease->addr_iter->iaaddr.address; @@ -204,7 +204,7 @@ int sd_dhcp6_lease_get_pd( assert_return(lease, -EINVAL); if (!lease->prefix_iter) - return -ENOMSG; + return -ENODATA; if (ret_prefix) *ret_prefix = lease->prefix_iter->iapdprefix.address; @@ -238,7 +238,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); if (!lease->dns) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->dns; @@ -268,7 +268,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(ret, -EINVAL); if (!lease->domains) - return -ENOENT; + return -ENODATA; *ret = lease->domains; return strv_length(lease->domains); @@ -349,14 +349,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr ** return lease->sntp_count; } - return -ENOENT; + return -ENODATA; } int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); if (!lease->ntp_fqdn) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->ntp_fqdn; @@ -390,7 +390,7 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(ret, -EINVAL); if (!lease->fqdn) - return -ENOENT; + return -ENODATA; *ret = lease->fqdn; return 0; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 53def1b07f..6152a30863 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -571,16 +571,16 @@ static void test_advertise_option(sd_event *e) { assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); assert_se(lt_pref == 150); assert_se(lt_valid == 180); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); sd_dhcp6_lease_reset_address_iter(lease); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); sd_dhcp6_lease_reset_address_iter(lease); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); assert_se(len == 14); @@ -735,7 +735,7 @@ static void test_client_verify_request(DHCP6Message *request, size_t len) { assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); } static void test_client_send_advertise(DHCP6Message *solicit) { @@ -910,7 +910,7 @@ static void test_client_verify_information_request(DHCP6Message *information_req sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); } int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, From f4fbea7a0f509c4010605cf12fe30887aea23fe4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 16:12:10 +0900 Subject: [PATCH 13/66] sd-dhcp6-lease: unify lease lifetime calculation --- src/libsystemd-network/dhcp6-lease-internal.h | 10 ++- src/libsystemd-network/dhcp6-option.c | 28 +------ src/libsystemd-network/sd-dhcp6-client.c | 63 +++----------- src/libsystemd-network/sd-dhcp6-lease.c | 83 ++++++++++++++----- 4 files changed, 82 insertions(+), 102 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 5d009781b2..7a5d14cb4f 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -11,6 +11,7 @@ #include "dhcp6-internal.h" #include "macro.h" +#include "time-util.h" struct sd_dhcp6_lease { unsigned n_ref; @@ -22,6 +23,9 @@ struct sd_dhcp6_lease { uint8_t preference; bool rapid_commit; triple_timestamp timestamp; + usec_t lifetime_t1; + usec_t lifetime_t2; + usec_t max_retransmit_duration; struct in6_addr server_address; DHCP6IA *ia_na; @@ -41,12 +45,14 @@ struct sd_dhcp6_lease { char *fqdn; }; -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); - void dhcp6_ia_clear_addresses(DHCP6IA *ia); DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); +void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease); +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2); +int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret); + int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 5099fa152c..cd67ab2c37 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -696,7 +696,7 @@ int dhcp6_option_parse_ia( DHCP6IA **ret) { _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; - uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; + uint32_t lt_t1, lt_t2; size_t header_len; int r; @@ -772,22 +772,16 @@ int dhcp6_option_parse_ia( r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore non-critical errors in the sub-option. */ - continue; - lt_min = MIN(lt_min, be32toh(ia->addresses->iaaddr.lifetime_valid)); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_IA_PD_PREFIX: { r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore non-critical errors in the sub-option. */ - continue; - lt_min = MIN(lt_min, be32toh(ia->addresses->iapdprefix.lifetime_valid)); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -815,22 +809,6 @@ int dhcp6_option_parse_ia( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), "Received an IA option without valid IA addresses or PD prefixes, ignoring."); - if (lt_t2 == 0 && IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD)) { - - /* addresses or prefixes with zero valid lifetime are already ignored. */ - assert(lt_min != UINT32_MAX); - - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - - ia->header.lifetime_t1 = htobe32(lt_t1); - ia->header.lifetime_t1 = htobe32(lt_t2); - - log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " - "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " - "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); - } - *ret = TAKE_PTR(ia); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 89d91f9289..f19c697713 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -981,14 +981,11 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda max_retransmit_time = DHCP6_REB_MAX_RT; if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { - uint32_t expire = 0; - - r = dhcp6_lease_ia_rebind_expire(client->lease->ia_na, &expire); + r = dhcp6_lease_get_max_retransmit_duration(client->lease, &max_retransmit_duration); if (r < 0) { client_stop(client, r); return 0; } - max_retransmit_duration = expire * USEC_PER_SEC; } break; @@ -1090,7 +1087,6 @@ int client_parse_message( size_t len, sd_dhcp6_lease *lease) { - uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; usec_t irt = IRT_DEFAULT; int r; @@ -1178,10 +1174,6 @@ int client_parse_message( dhcp6_ia_free(lease->ia_na); lease->ia_na = TAKE_PTR(ia); - - lt_t1 = MIN(lt_t1, be32toh(lease->ia_na->header.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_na->header.lifetime_t2)); - break; } case SD_DHCP6_OPTION_IA_PD: { @@ -1205,10 +1197,6 @@ int client_parse_message( dhcp6_ia_free(lease->ia_pd); lease->ia_pd = TAKE_PTR(ia); - - lt_t1 = MIN(lt_t1, be32toh(lease->ia_pd->header.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia_pd->header.lifetime_t2)); - break; } case SD_DHCP6_OPTION_RAPID_COMMIT: @@ -1282,15 +1270,7 @@ int client_parse_message( if (!lease->ia_na && !lease->ia_pd) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - if (lease->ia_na) { - lease->ia_na->header.lifetime_t1 = htobe32(lt_t1); - lease->ia_na->header.lifetime_t2 = htobe32(lt_t2); - } - - if (lease->ia_pd) { - lease->ia_pd->header.lifetime_t1 = htobe32(lt_t1); - lease->ia_pd->header.lifetime_t2 = htobe32(lt_t2); - } + dhcp6_lease_set_lifetime(lease); } client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); @@ -1551,32 +1531,9 @@ static int client_receive_message( return 0; } -static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, - uint32_t *lifetime_t2) { - assert_return(client, -EINVAL); - assert_return(client->lease, -EINVAL); - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia_na) { - *lifetime_t1 = be32toh(client->lease->ia_na->header.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_na->header.lifetime_t2); - - return 0; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->ia_pd) { - *lifetime_t1 = be32toh(client->lease->ia_pd->header.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia_pd->header.lifetime_t2); - - return 0; - } - - return -ENOMSG; -} - static int client_start(sd_dhcp6_client *client, DHCP6State state) { + usec_t timeout, time_now, lifetime_t1, lifetime_t2; int r; - usec_t timeout, time_now; - uint32_t lifetime_t1, lifetime_t2; assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); @@ -1635,18 +1592,18 @@ static int client_start(sd_dhcp6_client *client, DHCP6State state) { case DHCP6_STATE_BOUND: - r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); + assert(client->lease); + + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2); if (r < 0) goto error; - if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { - log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - lifetime_t1, lifetime_t2); - + if (lifetime_t1 == USEC_INFINITY || lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1 or T2"); return 0; } - timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t1); log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); @@ -1658,7 +1615,7 @@ static int client_start(sd_dhcp6_client *client, DHCP6State state) { if (r < 0) goto error; - timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t2); log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 4cd6d3e352..c2d076d3b9 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -24,6 +24,67 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } +void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { + uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; + DHCP6Address *a; + + assert(lease); + assert(lease->ia_na || lease->ia_pd); + + if (lease->ia_na) { + t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_na->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid)); + } + + if (lease->ia_pd) { + t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_pd->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid)); + } + + if (t2 == 0 || t2 > min_valid_lt) { + /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes, + * then adjust lifetime with it. */ + t1 = min_valid_lt / 2; + t2 = min_valid_lt / 10 * 8; + } + + assert(t2 <= min_valid_lt); + lease->max_retransmit_duration = (min_valid_lt - t2) * USEC_PER_SEC; + + lease->lifetime_t1 = t1 == UINT32_MAX ? USEC_INFINITY : t1 * USEC_PER_SEC; + lease->lifetime_t2 = t2 == UINT32_MAX ? USEC_INFINITY : t2 * USEC_PER_SEC; +} + +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2) { + assert(lease); + + if (!lease->ia_na && !lease->ia_pd) + return -ENODATA; + + if (ret_t1) + *ret_t1 = lease->lifetime_t1; + if (ret_t2) + *ret_t2 = lease->lifetime_t2; + return 0; +} + +int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret) { + assert(lease); + + if (!lease->ia_na && !lease->ia_pd) + return -ENODATA; + + if (ret) + *ret = lease->max_retransmit_duration; + return 0; +} + int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { assert_return(lease, -EINVAL); assert_return(ret, -EINVAL); @@ -32,28 +93,6 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re return 0; } -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { - DHCP6Address *addr; - uint32_t valid = 0, t; - - assert(ia); - assert(expire); - - LIST_FOREACH(addresses, addr, ia->addresses) { - t = be32toh(addr->iaaddr.lifetime_valid); - if (valid < t) - valid = t; - } - - t = be32toh(ia->header.lifetime_t2); - if (t > valid) - return -EINVAL; - - *expire = valid - t; - - return 0; -} - void dhcp6_ia_clear_addresses(DHCP6IA *ia) { DHCP6Address *a, *n; From c930925841a54df24f962b8230b8dc9ec700a410 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 18:24:13 +0900 Subject: [PATCH 14/66] sd-dhcp6-client: use structured initializer --- src/libsystemd-network/sd-dhcp6-lease.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index c2d076d3b9..e323fd3923 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -460,11 +460,13 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) { assert(ret); - lease = new0(sd_dhcp6_lease, 1); + lease = new(sd_dhcp6_lease, 1); if (!lease) return -ENOMEM; - lease->n_ref = 1; + *lease = (sd_dhcp6_lease) { + .n_ref = 1, + }; *ret = lease; return 0; From 65ece4c85b149f4b7ae1c97793e00147da547a0b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 19:36:30 +0900 Subject: [PATCH 15/66] sd-dhcp6-client: introduce dhcp6_lease_new_from_message() --- src/libsystemd-network/dhcp6-internal.h | 62 +++- src/libsystemd-network/dhcp6-lease-internal.h | 8 +- src/libsystemd-network/sd-dhcp6-client.c | 299 +----------------- src/libsystemd-network/sd-dhcp6-lease.c | 251 ++++++++++++++- src/libsystemd-network/test-dhcp6-client.c | 4 +- 5 files changed, 324 insertions(+), 300 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 23738d3197..7f03ce1e07 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -11,7 +11,9 @@ #include "sd-event.h" #include "sd-dhcp6-client.h" +#include "dhcp-identifier.h" #include "dhcp6-protocol.h" +#include "ether-addr-util.h" #include "hashmap.h" #include "list.h" #include "macro.h" @@ -75,7 +77,59 @@ typedef struct DHCP6IA { LIST_HEAD(DHCP6Address, addresses); } DHCP6IA; -typedef struct sd_dhcp6_client sd_dhcp6_client; +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +typedef enum DHCP6RequestIA { + DHCP6_REQUEST_IA_NA = 1 << 0, + DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ + DHCP6_REQUEST_IA_PD = 1 << 2, +} DHCP6RequestIA; + +typedef struct sd_dhcp6_client { + unsigned n_ref; + + DHCP6State state; + sd_event *event; + int event_priority; + int ifindex; + char *ifname; + struct in6_addr local_address; + uint8_t mac_addr[HW_ADDR_MAX_SIZE]; + size_t mac_addr_len; + uint16_t arp_type; + DHCP6IA ia_na; + DHCP6IA ia_pd; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + DHCP6RequestIA request_ia; + be32_t transaction_id; + usec_t transaction_start; + struct sd_dhcp6_lease *lease; + int fd; + bool information_request; + bool iaid_set; + be16_t *req_opts; + size_t req_opts_len; + char *fqdn; + char *mudurl; + char **user_class; + char **vendor_class; + sd_event_source *receive_message; + usec_t retransmit_time; + uint8_t retransmit_count; + sd_event_source *timeout_resend; + sd_event_source *timeout_resend_expire; + sd_dhcp6_client_callback_t callback; + void *userdata; + struct duid duid; + size_t duid_len; + usec_t information_request_time_usec; + usec_t information_refresh_time_usec; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; + + /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + bool test_mode; +} sd_dhcp6_client; bool dhcp6_option_can_request(uint16_t option); int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, @@ -113,12 +167,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); -int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease); - const char *dhcp6_message_type_to_string(int s) _const_; int dhcp6_message_type_from_string(const char *s) _pure_; const char *dhcp6_message_status_to_string(int s) _const_; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 7a5d14cb4f..589274c23b 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -49,7 +49,6 @@ void dhcp6_ia_clear_addresses(DHCP6IA *ia); DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); -void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease); int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2); int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret); @@ -69,3 +68,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index f19c697713..9bd3865314 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -30,65 +30,6 @@ #include "util.h" #include "web-util.h" -#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN - -#define IRT_DEFAULT (1 * USEC_PER_DAY) -#define IRT_MINIMUM (600 * USEC_PER_SEC) - -/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ -typedef enum DHCP6RequestIA { - DHCP6_REQUEST_IA_NA = 1 << 0, - DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ - DHCP6_REQUEST_IA_PD = 1 << 2, -} DHCP6RequestIA; - -struct sd_dhcp6_client { - unsigned n_ref; - - DHCP6State state; - sd_event *event; - int event_priority; - int ifindex; - char *ifname; - struct in6_addr local_address; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - DHCP6IA ia_na; - DHCP6IA ia_pd; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - DHCP6RequestIA request_ia; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - bool iaid_set; - be16_t *req_opts; - size_t req_opts_len; - char *fqdn; - char *mudurl; - char **user_class; - char **vendor_class; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; - usec_t information_request_time_usec; - usec_t information_refresh_time_usec; - OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; - - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ - bool test_mode; -}; - static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_DNS_SERVERS, SD_DHCP6_OPTION_DOMAIN_LIST, @@ -237,7 +178,7 @@ int sd_dhcp6_client_set_mac( assert_return(client, -EINVAL); assert_return(addr, -EINVAL); - assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(addr_len <= HW_ADDR_MAX_SIZE, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); if (arp_type == ARPHRD_ETHER) @@ -1081,208 +1022,11 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { return 0; } -int client_parse_message( +static int client_receive_reply( sd_dhcp6_client *client, DHCP6Message *message, size_t len, - sd_dhcp6_lease *lease) { - - usec_t irt = IRT_DEFAULT; - int r; - - assert(client); - assert(message); - assert(len >= sizeof(DHCP6Message)); - assert(lease); - - len -= sizeof(DHCP6Message); - for (size_t offset = 0; offset < len;) { - uint16_t optcode; - size_t optlen; - const uint8_t *optval; - - r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); - if (r < 0) - return r; - - switch (optcode) { - case SD_DHCP6_OPTION_CLIENTID: - if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", - dhcp6_message_type_to_string(message->type)); - - r = dhcp6_lease_set_clientid(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_SERVERID: - if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", - dhcp6_message_type_to_string(message->type)); - - r = dhcp6_lease_set_serverid(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_PREFERENCE: - if (optlen != 1) - return -EINVAL; - - r = dhcp6_lease_set_preference(lease, optval[0]); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: { - _cleanup_free_ char *msg = NULL; - - r = dhcp6_option_parse_status(optval, optlen, &msg); - if (r < 0) - return r; - - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received %s message with non-zero status: %s%s%s", - dhcp6_message_type_to_string(message->type), - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); - break; - } - case SD_DHCP6_OPTION_IA_NA: { - _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; - - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); - break; - } - - r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; - - if (lease->ia_na) { - log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); - continue; - } - - dhcp6_ia_free(lease->ia_na); - lease->ia_na = TAKE_PTR(ia); - break; - } - case SD_DHCP6_OPTION_IA_PD: { - _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; - - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); - break; - } - - r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; - - if (lease->ia_pd) { - log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); - continue; - } - - dhcp6_ia_free(lease->ia_pd); - lease->ia_pd = TAKE_PTR(ia); - break; - } - case SD_DHCP6_OPTION_RAPID_COMMIT: - r = dhcp6_lease_set_rapid_commit(lease); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_add_dns(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_add_domains(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_add_ntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_add_sntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_CLIENT_FQDN: - r = dhcp6_lease_set_fqdn(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: - if (optlen != 4) - return -EINVAL; - - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; - break; - } - } - - uint8_t *clientid; - size_t clientid_len; - if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", - dhcp6_message_type_to_string(message->type)); - - if (clientid_len != client->duid_len || - memcmp(clientid, &client->duid, clientid_len) != 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", - dhcp6_message_type_to_string(message->type)); - - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - r = dhcp6_lease_get_serverid(lease, NULL, NULL); - if (r < 0) - return log_dhcp6_client_errno(client, r, "%s has no server id", - dhcp6_message_type_to_string(message->type)); - - if (!lease->ia_na && !lease->ia_pd) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - - dhcp6_lease_set_lifetime(lease); - } - - client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); - - return 0; -} - -static int client_receive_reply( - sd_dhcp6_client *client, - DHCP6Message *reply, - size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; @@ -1290,21 +1034,12 @@ static int client_receive_reply( int r; assert(client); - assert(reply); - assert(t); + assert(message); - if (reply->type != DHCP6_MESSAGE_REPLY) + if (message->type != DHCP6_MESSAGE_REPLY) return 0; - r = dhcp6_lease_new(&lease); - if (r < 0) - return -ENOMEM; - - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; - - r = client_parse_message(client, reply, len, lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) return r; @@ -1325,9 +1060,9 @@ static int client_receive_reply( static int client_receive_advertise( sd_dhcp6_client *client, - DHCP6Message *advertise, + DHCP6Message *message, size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; @@ -1335,21 +1070,12 @@ static int client_receive_advertise( int r; assert(client); - assert(advertise); - assert(t); + assert(message); - if (advertise->type != DHCP6_MESSAGE_ADVERTISE) + if (message->type != DHCP6_MESSAGE_ADVERTISE) return 0; - r = dhcp6_lease_new(&lease); - if (r < 0) - return r; - - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; - - r = client_parse_message(client, advertise, len, lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) return r; @@ -1451,9 +1177,6 @@ static int client_receive_message( triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); } - if (!triple_timestamp_is_set(&t)) - triple_timestamp_get(&t); - if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { const char *type_str = dhcp6_message_type_to_string(message->type); if (type_str) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index e323fd3923..9cc7e2b642 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -11,6 +11,18 @@ #include "strv.h" #include "util.h" +#define IRT_DEFAULT (1 * USEC_PER_DAY) +#define IRT_MINIMUM (600 * USEC_PER_SEC) + +static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) { + assert(lease); + + if (timestamp && triple_timestamp_is_set(timestamp)) + lease->timestamp = *timestamp; + else + triple_timestamp_get(&lease->timestamp); +} + int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { assert_return(lease, -EINVAL); assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); @@ -24,7 +36,7 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } -void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { +static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; DHCP6Address *a; @@ -85,6 +97,15 @@ int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret) return 0; } +static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) { + assert(lease); + + if (server_address) + lease->server_address = *server_address; + else + lease->server_address = (struct in6_addr) {}; +} + int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { assert_return(lease, -EINVAL); assert_return(ret, -EINVAL); @@ -435,6 +456,203 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { return 0; } +static int dhcp6_lease_parse_message( + sd_dhcp6_client *client, + sd_dhcp6_lease *lease, + const DHCP6Message *message, + size_t len) { + + usec_t irt = IRT_DEFAULT; + int r; + + assert(client); + assert(lease); + assert(message); + assert(len >= sizeof(DHCP6Message)); + + len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; + + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; + + switch (optcode) { + case SD_DHCP6_OPTION_CLIENTID: + if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_clientid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_SERVERID: + if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_serverid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_PREFERENCE: + if (optlen != 1) + return -EINVAL; + + r = dhcp6_lease_set_preference(lease, optval[0]); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(optval, optlen, &msg); + if (r < 0) + return r; + + if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received %s message with non-zero status: %s%s%s", + dhcp6_message_type_to_string(message->type), + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + case SD_DHCP6_OPTION_IA_NA: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_na) { + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_na); + lease->ia_na = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_pd) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_pd); + lease->ia_pd = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_add_dns(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_add_domains(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_add_ntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_add_sntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_CLIENT_FQDN: + r = dhcp6_lease_set_fqdn(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: + if (optlen != 4) + return -EINVAL; + + irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + break; + } + } + + uint8_t *clientid; + size_t clientid_len; + if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (clientid_len != client->duid_len || + memcmp(clientid, &client->duid, clientid_len) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + if (r < 0) + return log_dhcp6_client_errno(client, r, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + + if (!lease->ia_na && !lease->ia_pd) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); + + dhcp6_lease_set_lifetime(lease); + } + + client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + + return 0; +} + static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { if (!lease) return NULL; @@ -471,3 +689,34 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) { *ret = lease; return 0; } + +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; + + assert(client); + assert(message); + assert(len >= sizeof(DHCP6Message)); + assert(ret); + + r = dhcp6_lease_new(&lease); + if (r < 0) + return r; + + dhcp6_lease_set_timestamp(lease, timestamp); + dhcp6_lease_set_server_address(lease, server_address); + + r = dhcp6_lease_parse_message(client, lease, message, len); + if (r < 0) + return r; + + *ret = TAKE_PTR(lease); + return 0; +} diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 6152a30863..03862bd5ac 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -401,9 +401,7 @@ static void test_client_parse_message_issue_22099(void) { assert_se(sd_dhcp6_client_set_iaid(client, 0xcc59117b) >= 0); assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0); - assert_se(dhcp6_lease_new(&lease) >= 0); - - assert_se(client_parse_message(client, (DHCP6Message*) msg, sizeof(msg), lease) >= 0); + assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0); } static uint8_t msg_advertise[198] = { From 16347c15d87ec4618f66b62b953c5097e2d7aff1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 19:42:58 +0900 Subject: [PATCH 16/66] sd-dhcp6-client: voidify client_reset() --- src/libsystemd-network/sd-dhcp6-client.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 9bd3865314..457e8ca915 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -584,7 +584,7 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static int client_reset(sd_dhcp6_client *client) { +static void client_reset(sd_dhcp6_client *client) { assert(client); client->lease = sd_dhcp6_lease_unref(client->lease); @@ -604,8 +604,6 @@ static int client_reset(sd_dhcp6_client *client) { (void) event_source_disable(client->timeout_t2); client->state = DHCP6_STATE_STOPPED; - - return 0; } static void client_stop(sd_dhcp6_client *client, int error) { @@ -1407,9 +1405,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (!client->information_request && client->request_ia == 0) return -EINVAL; - r = client_reset(client); - if (r < 0) - return r; + client_reset(client); r = client_ensure_iaid(client); if (r < 0) From 0123b2dbacade0e05b708231b307ad510bd9fc44 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 20:53:34 +0900 Subject: [PATCH 17/66] sd-dhcp6-client: fix possible unaligned read or write --- src/libsystemd-network/dhcp6-option.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index cd67ab2c37..ac032f07fd 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -212,19 +212,15 @@ 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) { - DHCP6Option *option; - assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - option = (DHCP6Option*) *buf; - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; - option->code = htobe16(optcode); - option->len = htobe16(optlen); + unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); @@ -511,7 +507,6 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data) { - const DHCP6Option *option; size_t len; assert(buf); @@ -526,16 +521,15 @@ int dhcp6_option_parse( if (*offset >= buflen - offsetof(DHCP6Option, data)) return -EBADMSG; - option = (const DHCP6Option*) (buf + *offset); - len = be16toh(option->len); + len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len)); if (len > buflen - offsetof(DHCP6Option, data) - *offset) return -EBADMSG; - *offset += offsetof(DHCP6Option, data) + len; - *ret_option_code = be16toh(option->code); + *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code)); *ret_option_data_len = len; - *ret_option_data = option->data; + *ret_option_data = buf + *offset + offsetof(DHCP6Option, data); + *offset += offsetof(DHCP6Option, data) + len; return 0; } From 07a3b3408b31a6194756ad5dcd9998a9ede86b7a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 21:59:07 +0900 Subject: [PATCH 18/66] sd-dhcp6-client: introduce client_process_*() This is mostly for shortening `client_receive_message()`. This also fixes the following: - do not trigger SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST notification when non-reply message, - add several missing error handlings caused in client_start(). --- .../fuzz-dhcp6-client-send.c | 8 +- src/libsystemd-network/sd-dhcp6-client.c | 171 ++++++++++-------- 2 files changed, 103 insertions(+), 76 deletions(-) diff --git a/src/libsystemd-network/fuzz-dhcp6-client-send.c b/src/libsystemd-network/fuzz-dhcp6-client-send.c index 39a5f4fd4d..d05bb55c43 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client-send.c +++ b/src/libsystemd-network/fuzz-dhcp6-client-send.c @@ -22,7 +22,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - triple_timestamp t = {}; usec_t time_now; int r; @@ -43,8 +42,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(sd_dhcp6_client_start(client) >= 0); assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0); - triple_timestamp_get(&t); - r = client_receive_advertise(client, (DHCP6Message *) data, size, &t, NULL); + r = client_process_advertise_or_rapid_commit_reply(client, (DHCP6Message *) data, size, NULL, NULL); if (r < 0) goto cleanup; @@ -52,8 +50,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (r < 0) goto cleanup; - if (r == DHCP6_STATE_REQUEST) - client->state = DHCP6_STATE_REQUEST; + if (client->state != DHCP6_STATE_REQUEST) + client->state = DHCP6_STATE_SOLICITATION; (void) client_send_message(client, time_now); cleanup: assert_se(sd_dhcp6_client_stop(client) >= 0); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 457e8ca915..2784a4ee24 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1020,7 +1020,52 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { return 0; } -static int client_receive_reply( +static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { + const char *type_str; + + assert(client); + assert(message); + + type_str = dhcp6_message_type_to_string(message->type); + if (type_str) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unexpected %s message, ignoring.", type_str); + else + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unsupported message type %u, ignoring.", message->type); +} + +static int client_process_information( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; + + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); + + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); + r = client_start(client, DHCP6_STATE_STOPPED); + if (r < 0) { + client_stop(client, r); + return r; + } + + return 0; +} + +static int client_process_reply( sd_dhcp6_client *client, DHCP6Message *message, size_t len, @@ -1028,35 +1073,32 @@ static int client_receive_reply( const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool rapid_commit; int r; assert(client); assert(message); if (message->type != DHCP6_MESSAGE_REPLY) - return 0; + return log_invalid_message_type(client, message); r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return r; - - if (client->state == DHCP6_STATE_SOLICITATION) { - r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); - if (r < 0) - return r; - - if (!rapid_commit) - return 0; - } + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - return DHCP6_STATE_BOUND; + r = client_start(client, DHCP6_STATE_BOUND); + if (r < 0) { + client_stop(client, r); + return r; + } + + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + return 0; } -static int client_receive_advertise( +static int client_process_advertise_or_rapid_commit_reply( sd_dhcp6_client *client, DHCP6Message *message, size_t len, @@ -1064,18 +1106,43 @@ static int client_receive_advertise( const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t pref_advertise = 0, pref_lease = 0; + uint8_t pref_advertise, pref_lease = 0; int r; assert(client); assert(message); - if (message->type != DHCP6_MESSAGE_ADVERTISE) - return 0; + if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY)) + return log_invalid_message_type(client, message); r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m", + dhcp6_message_type_to_string(message->type)); + + if (message->type == DHCP6_MESSAGE_REPLY) { + bool rapid_commit; + + r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); + if (r < 0) + return r; + + if (!rapid_commit) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received reply message without rapid commit flag, ignoring."); + + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + r = client_start(client, DHCP6_STATE_BOUND); + if (r < 0) { + client_stop(client, r); + return r; + } + + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + return 0; + } r = dhcp6_lease_get_preference(lease, &pref_advertise); if (r < 0) @@ -1088,15 +1155,20 @@ static int client_receive_advertise( } if (!client->lease || pref_advertise > pref_lease) { + /* If this is the first advertise message or has higher preference, then save the lease. */ sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = 0; } - if (pref_advertise == 255 || client->retransmit_count > 1) - r = DHCP6_STATE_REQUEST; + if (pref_advertise == 255 || client->retransmit_count > 1) { + r = client_start(client, DHCP6_STATE_REQUEST); + if (r < 0) { + client_stop(client, r); + return r; + } + } - return r; + return 0; } static int client_receive_message( @@ -1124,7 +1196,6 @@ static int client_receive_message( _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; ssize_t buflen, len; - int r = 0; assert(s); assert(client); @@ -1175,73 +1246,31 @@ static int client_receive_message( triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); } - if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { - const char *type_str = dhcp6_message_type_to_string(message->type); - if (type_str) - log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str); - else - log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type); - return 0; - } - if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_information(client, message, len, &t, server_address) < 0) return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - client_start(client, DHCP6_STATE_STOPPED); - break; case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); + if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0) return 0; - } + break; - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - break; - } - - _fallthrough_; /* for Solicitation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: - - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_reply(client, message, len, &t, server_address) < 0) return 0; - } - - if (r == DHCP6_STATE_BOUND) { - r = client_start(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - } - break; case DHCP6_STATE_BOUND: - - break; - case DHCP6_STATE_STOPPED: return 0; + default: assert_not_reached(); } From 06d5e856f8775bc7305e4965361a8927449fe162 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 22:16:17 +0900 Subject: [PATCH 19/66] sd-dhcp6-client: rename client_start() -> client_set_state() --- src/libsystemd-network/sd-dhcp6-client.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 2784a4ee24..e597b3bcaf 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -108,7 +108,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) -static int client_start(sd_dhcp6_client *client, DHCP6State state); +static int client_set_state(sd_dhcp6_client *client, DHCP6State state); int sd_dhcp6_client_set_callback( sd_dhcp6_client *client, @@ -820,7 +820,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T2"); - client_start(client, DHCP6_STATE_REBIND); + client_set_state(client, DHCP6_STATE_REBIND); return 0; } @@ -836,7 +836,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T1"); - client_start(client, DHCP6_STATE_RENEW); + client_set_state(client, DHCP6_STATE_RENEW); return 0; } @@ -857,7 +857,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void /* RFC 3315, section 18.1.4., says that "...the client may choose to use a Solicit message to locate a new DHCP server..." */ if (state == DHCP6_STATE_REBIND) - client_start(client, DHCP6_STATE_SOLICITATION); + client_set_state(client, DHCP6_STATE_SOLICITATION); return 0; } @@ -889,7 +889,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - client_start(client, DHCP6_STATE_REQUEST); + client_set_state(client, DHCP6_STATE_REQUEST); return 0; } @@ -1056,7 +1056,7 @@ static int client_process_information( client->lease = TAKE_PTR(lease); client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - r = client_start(client, DHCP6_STATE_STOPPED); + r = client_set_state(client, DHCP6_STATE_STOPPED); if (r < 0) { client_stop(client, r); return r; @@ -1088,7 +1088,7 @@ static int client_process_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_start(client, DHCP6_STATE_BOUND); + r = client_set_state(client, DHCP6_STATE_BOUND); if (r < 0) { client_stop(client, r); return r; @@ -1134,7 +1134,7 @@ static int client_process_advertise_or_rapid_commit_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_start(client, DHCP6_STATE_BOUND); + r = client_set_state(client, DHCP6_STATE_BOUND); if (r < 0) { client_stop(client, r); return r; @@ -1161,7 +1161,7 @@ static int client_process_advertise_or_rapid_commit_reply( } if (pref_advertise == 255 || client->retransmit_count > 1) { - r = client_start(client, DHCP6_STATE_REQUEST); + r = client_set_state(client, DHCP6_STATE_REQUEST); if (r < 0) { client_stop(client, r); return r; @@ -1281,7 +1281,7 @@ static int client_receive_message( return 0; } -static int client_start(sd_dhcp6_client *client, DHCP6State state) { +static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { usec_t timeout, time_now, lifetime_t1, lifetime_t2; int r; @@ -1470,7 +1470,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { log_dhcp6_client(client, "Started in %s mode", client->information_request ? "Information request" : "Managed"); - return client_start(client, state); + return client_set_state(client, state); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { From bfa1139af4404420f432c19795b10f37985d16f4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 22:17:43 +0900 Subject: [PATCH 20/66] sd-dhcp6-client: add missing one more error handling --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e597b3bcaf..3cdaf49156 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1297,7 +1297,7 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) - return r; + goto error; if (!client->receive_message) { r = sd_event_add_io(client->event, &client->receive_message, From 4db6334c9a6174bbc1ba4060c8c46d6c918812cb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 22:22:38 +0900 Subject: [PATCH 21/66] sd-dhcp6-client: stop client on error in client state transition --- src/libsystemd-network/sd-dhcp6-client.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 3cdaf49156..dfb0b2c65f 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1056,13 +1056,7 @@ static int client_process_information( client->lease = TAKE_PTR(lease); client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - r = client_set_state(client, DHCP6_STATE_STOPPED); - if (r < 0) { - client_stop(client, r); - return r; - } - - return 0; + return client_set_state(client, DHCP6_STATE_STOPPED); } static int client_process_reply( @@ -1089,10 +1083,8 @@ static int client_process_reply( client->lease = TAKE_PTR(lease); r = client_set_state(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); + if (r < 0) return r; - } client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); return 0; @@ -1135,10 +1127,8 @@ static int client_process_advertise_or_rapid_commit_reply( client->lease = TAKE_PTR(lease); r = client_set_state(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); + if (r < 0) return r; - } client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); return 0; @@ -1162,10 +1152,8 @@ static int client_process_advertise_or_rapid_commit_reply( if (pref_advertise == 255 || client->retransmit_count > 1) { r = client_set_state(client, DHCP6_STATE_REQUEST); - if (r < 0) { - client_stop(client, r); + if (r < 0) return r; - } } return 0; @@ -1397,8 +1385,8 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { return 0; - error: - client_reset(client); +error: + client_stop(client, r); return r; } From 7771233127cada23622b574d2d032d31dfc229c2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 22:34:04 +0900 Subject: [PATCH 22/66] sd-dhcp6-client: use in6_addr_to_string() --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index dfb0b2c65f..03b1c918df 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1437,7 +1437,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) { _cleanup_free_ char *p = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + (void) in6_addr_to_string(&client->local_address, &p); return log_dhcp6_client_errno(client, r, "Failed to bind to UDP socket at address %s: %m", strna(p)); } From d0875a07dd8d2f0238ed33ce77c04a34b891780f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 22:35:17 +0900 Subject: [PATCH 23/66] sd-dhcp6-client: disable event source when client is stopped or freed --- src/libsystemd-network/sd-dhcp6-client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 03b1c918df..ff7e04bbdc 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -589,8 +589,7 @@ static void client_reset(sd_dhcp6_client *client) { client->lease = sd_dhcp6_lease_unref(client->lease); - client->receive_message = - sd_event_source_unref(client->receive_message); + client->receive_message = sd_event_source_disable_unref(client->receive_message); client->transaction_id = 0; client->transaction_start = 0; @@ -1497,12 +1496,13 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { assert(client); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + sd_dhcp6_lease_unref(client->lease); - client_reset(client); + sd_event_source_disable_unref(client->receive_message); + sd_event_source_disable_unref(client->timeout_resend); + sd_event_source_disable_unref(client->timeout_resend_expire); + sd_event_source_disable_unref(client->timeout_t1); + sd_event_source_disable_unref(client->timeout_t2); client->fd = safe_close(client->fd); From cc5184823fde84768d10ae42f20f843b65d52adc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 23:25:07 +0900 Subject: [PATCH 24/66] sd-dhcp6-client: fix overflow in calculating timeout value --- src/libsystemd-network/sd-dhcp6-client.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ff7e04bbdc..a830b44f76 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -808,6 +808,10 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return 0; } +static usec_t client_timeout_compute_random(usec_t val) { + return usec_sub_unsigned(val, random_u64_range(val / 10)); +} + static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; @@ -861,10 +865,6 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void return 0; } -static usec_t client_timeout_compute_random(usec_t val) { - return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC; -} - static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { int r = 0; sd_dhcp6_client *client = userdata; From 02354ee738cfa84ddc8400f00f06025dcdf18228 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 23:33:55 +0900 Subject: [PATCH 25/66] sd-dhcp6-client: introduce client_enter_bound_state() To make client_set_state() smaller. This should not change any behavior. --- src/libsystemd-network/sd-dhcp6-client.c | 105 ++++++++++++++--------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a830b44f76..b2d447a0b6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -844,6 +844,66 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) return 0; } +static int client_enter_bound_state(sd_dhcp6_client *client) { + usec_t timeout, time_now, lifetime_t1, lifetime_t2; + int r; + + assert(client); + assert(client->lease); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + + (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_resend); + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + goto error; + + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2); + if (r < 0) + goto error; + + if (lifetime_t1 == USEC_INFINITY || lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1 or T2"); + return 0; + } + + timeout = client_timeout_compute_random(lifetime_t1); + + log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); + + r = event_reset_time(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); + if (r < 0) + goto error; + + timeout = client_timeout_compute_random(lifetime_t2); + + log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); + + r = event_reset_time(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); + if (r < 0) + goto error; + + client->state = DHCP6_STATE_BOUND; + return 0; + +error: + client_stop(client, r); + return r; +} + static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); @@ -1081,7 +1141,7 @@ static int client_process_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_set_state(client, DHCP6_STATE_BOUND); + r = client_enter_bound_state(client); if (r < 0) return r; @@ -1125,7 +1185,7 @@ static int client_process_advertise_or_rapid_commit_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_set_state(client, DHCP6_STATE_BOUND); + r = client_enter_bound_state(client); if (r < 0) return r; @@ -1269,7 +1329,7 @@ static int client_receive_message( } static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { - usec_t timeout, time_now, lifetime_t1, lifetime_t2; + usec_t time_now; int r; assert_return(client, -EINVAL); @@ -1328,45 +1388,6 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { break; case DHCP6_STATE_BOUND: - - assert(client->lease); - - r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2); - if (r < 0) - goto error; - - if (lifetime_t1 == USEC_INFINITY || lifetime_t2 == USEC_INFINITY) { - log_dhcp6_client(client, "Infinite T1 or T2"); - return 0; - } - - timeout = client_timeout_compute_random(lifetime_t1); - - log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(lifetime_t2); - - log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); - if (r < 0) - goto error; - - client->state = state; - - return 0; default: assert_not_reached(); } From 1d6cb4ce2a4fcec440f79df158f2a4dad0558e30 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 23:43:52 +0900 Subject: [PATCH 26/66] sd-dhcp6-client: use event_reset_time_relative() --- src/libsystemd-network/sd-dhcp6-client.c | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index b2d447a0b6..c4bf9652d8 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -845,7 +845,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) } static int client_enter_bound_state(sd_dhcp6_client *client) { - usec_t timeout, time_now, lifetime_t1, lifetime_t2; + usec_t timeout, lifetime_t1, lifetime_t2; int r; assert(client); @@ -859,10 +859,6 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { (void) event_source_disable(client->timeout_resend_expire); (void) event_source_disable(client->timeout_resend); - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2); if (r < 0) goto error; @@ -876,11 +872,11 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); + r = event_reset_time_relative(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + timeout, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); if (r < 0) goto error; @@ -888,11 +884,11 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); + r = event_reset_time_relative(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + timeout, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); if (r < 0) goto error; From cdf3d8c5845f9b70a7634d019d9d540e4db9a365 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 23:51:43 +0900 Subject: [PATCH 27/66] sd-dhcp6-client: fix lifetime handling This fixes the following issues: - if T1 is finite and T2 is infinite, then the timer for T1 was not enabled, - after T1 and T2 are randomized, T1 may be longer than T2. --- src/libsystemd-network/sd-dhcp6-client.c | 56 ++++++++++++------------ 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index c4bf9652d8..1408878da3 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -845,7 +845,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) } static int client_enter_bound_state(sd_dhcp6_client *client) { - usec_t timeout, lifetime_t1, lifetime_t2; + usec_t lifetime_t1, lifetime_t2; int r; assert(client); @@ -863,34 +863,36 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { if (r < 0) goto error; - if (lifetime_t1 == USEC_INFINITY || lifetime_t2 == USEC_INFINITY) { - log_dhcp6_client(client, "Infinite T1 or T2"); - return 0; + lifetime_t2 = client_timeout_compute_random(lifetime_t2); + lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2)); + + if (lifetime_t1 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1"); + event_source_disable(client->timeout_t1); + } else { + log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + lifetime_t1, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); + if (r < 0) + goto error; } - timeout = client_timeout_compute_random(lifetime_t1); - - log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time_relative(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(lifetime_t2); - - log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time_relative(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); - if (r < 0) - goto error; + if (lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T2"); + event_source_disable(client->timeout_t2); + } else { + log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + lifetime_t2, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); + if (r < 0) + goto error; + } client->state = DHCP6_STATE_BOUND; return 0; From c41bdb17fc12275b9f0008da82f7d0b4eb62c2c0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 6 Feb 2022 23:58:36 +0900 Subject: [PATCH 28/66] sd-dhcp6-client: call client_notify() in client_enter_bound_state() --- src/libsystemd-network/sd-dhcp6-client.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1408878da3..ff2025efe7 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -895,6 +895,7 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { } client->state = DHCP6_STATE_BOUND; + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); return 0; error: @@ -1139,12 +1140,7 @@ static int client_process_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_enter_bound_state(client); - if (r < 0) - return r; - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - return 0; + return client_enter_bound_state(client); } static int client_process_advertise_or_rapid_commit_reply( @@ -1183,12 +1179,7 @@ static int client_process_advertise_or_rapid_commit_reply( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = client_enter_bound_state(client); - if (r < 0) - return r; - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - return 0; + return client_enter_bound_state(client); } r = dhcp6_lease_get_preference(lease, &pref_advertise); From 220a88ca757f753fc48b30f45c8c39fb7560f63d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 00:23:19 +0900 Subject: [PATCH 29/66] sd-dhcp6-client: disable T1 timer on T2 Otherwise, the client may be enter RENEW state after REBIND state. --- src/libsystemd-network/sd-dhcp6-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ff2025efe7..d88b7ec236 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -820,6 +820,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) assert(client->lease); (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); log_dhcp6_client(client, "Timeout T2"); From c2c878d806c83b30646ea0ad78486cc1c1c0e6e7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 01:09:09 +0900 Subject: [PATCH 30/66] sd-dhcp6-client: prohibit spurious state transition Previously, `client_set_state(client, DHCP6_STATE_STOPPED)` might make the client enter the SOLICIT state. --- src/libsystemd-network/sd-dhcp6-client.c | 14 +++++--------- src/libsystemd-network/test-dhcp6-client.c | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index d88b7ec236..0ca1be86b6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1114,8 +1114,11 @@ static int client_process_information( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); + (void) event_source_disable(client->timeout_resend); + client->state = DHCP6_STATE_STOPPED; + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - return client_set_state(client, DHCP6_STATE_STOPPED); + return 0; } static int client_process_reply( @@ -1355,14 +1358,6 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { } switch (state) { - case DHCP6_STATE_STOPPED: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - client->state = DHCP6_STATE_STOPPED; - - return 0; - } - - _fallthrough_; case DHCP6_STATE_SOLICITATION: client->state = DHCP6_STATE_SOLICITATION; @@ -1377,6 +1372,7 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { break; + case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: default: assert_not_reached(); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 03862bd5ac..60bb7f0ca8 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -845,7 +845,6 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, void assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); assert_se(sd_dhcp6_client_stop(client) >= 0); assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); From f814cd80dec288954dea5593715b51daed8f1034 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 01:24:01 +0900 Subject: [PATCH 31/66] sd-dhcp6-client: make state transition stricter --- src/libsystemd-network/sd-dhcp6-client.c | 43 ++++++++++++------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 0ca1be86b6..25091e1b60 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1328,7 +1328,26 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->state != state, -EINVAL); + + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_SOLICITATION: + assert(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_REQUEST: + assert(client->state == DHCP6_STATE_SOLICITATION); + break; + case DHCP6_STATE_RENEW: + assert(client->state == DHCP6_STATE_BOUND); + break; + case DHCP6_STATE_REBIND: + assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); + break; + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } (void) event_source_disable(client->timeout_resend_expire); (void) event_source_disable(client->timeout_resend); @@ -1357,27 +1376,7 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { goto error; } - switch (state) { - case DHCP6_STATE_SOLICITATION: - client->state = DHCP6_STATE_SOLICITATION; - - break; - - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - client->state = state; - - break; - - case DHCP6_STATE_STOPPED: - case DHCP6_STATE_BOUND: - default: - assert_not_reached(); - } - + client->state = state; client->transaction_id = random_u32() & htobe32(0x00ffffff); client->transaction_start = time_now; From 8ef959cd090459bf45d2dfe37e4e9ed23b09ffc2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 01:38:21 +0900 Subject: [PATCH 32/66] sd-dhcp6-client: initialize IO event source in sd_dhcp6_client_start() --- src/libsystemd-network/sd-dhcp6-client.c | 44 +++++++++++++----------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 25091e1b60..2c02c0d951 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -589,14 +589,13 @@ static void client_reset(sd_dhcp6_client *client) { client->lease = sd_dhcp6_lease_unref(client->lease); - client->receive_message = sd_event_source_disable_unref(client->receive_message); - client->transaction_id = 0; client->transaction_start = 0; client->retransmit_time = 0; client->retransmit_count = 0; + (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); (void) event_source_disable(client->timeout_resend_expire); (void) event_source_disable(client->timeout_t1); @@ -1358,24 +1357,6 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { if (r < 0) goto error; - if (!client->receive_message) { - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; - } - client->state = state; client->transaction_id = random_u32() & htobe32(0x00ffffff); client->transaction_start = time_now; @@ -1388,6 +1369,10 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { if (r < 0) goto error; + r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); + if (r < 0) + goto error; + return 0; error: @@ -1401,6 +1386,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) { client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->receive_message = sd_event_source_unref(client->receive_message); client->fd = safe_close(client->fd); return 0; @@ -1450,6 +1436,24 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { client->fd = r; } + if (!client->receive_message) { + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL; + + r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s, client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "dhcp6-receive-message"); + if (r < 0) + return r; + + client->receive_message = TAKE_PTR(s); + } + if (client->information_request) { usec_t t = now(CLOCK_MONOTONIC); From 30b31359b04f29af9d490b0f4cb0ee9b5aa3a8e4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 01:59:27 +0900 Subject: [PATCH 33/66] sd-dhcp6-client: drop unnecessary assignment --- src/libsystemd-network/sd-dhcp6-client.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 2c02c0d951..81b78a016f 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1321,7 +1321,6 @@ static int client_receive_message( } static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { - usec_t time_now; int r; assert_return(client, -EINVAL); @@ -1353,13 +1352,12 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { client->retransmit_time = 0; client->retransmit_count = 0; - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - client->state = state; client->transaction_id = random_u32() & htobe32(0x00ffffff); - client->transaction_start = time_now; + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &client->transaction_start); + if (r < 0) + goto error; r = event_reset_time(client->event, &client->timeout_resend, clock_boottime_or_monotonic(), From 3bb18e707c5209d7f051fa22682c10136d22a33e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 02:36:55 +0900 Subject: [PATCH 34/66] sd-dhcp6-client: set lease expiration timer in client_enter_bound_state() The timer must be active until the client get re-enter bound state, and the timeout must be determined by the lease acquired when entering the bound state. --- src/libsystemd-network/dhcp6-internal.h | 6 +- src/libsystemd-network/dhcp6-lease-internal.h | 5 +- src/libsystemd-network/sd-dhcp6-client.c | 104 +++++++++--------- src/libsystemd-network/sd-dhcp6-lease.c | 27 ++--- 4 files changed, 64 insertions(+), 78 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 7f03ce1e07..c2655827b8 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -98,8 +98,6 @@ typedef struct sd_dhcp6_client { uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; DHCP6RequestIA request_ia; be32_t transaction_id; usec_t transaction_start; @@ -117,7 +115,9 @@ typedef struct sd_dhcp6_client { usec_t retransmit_time; uint8_t retransmit_count; sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; + sd_event_source *timeout_expire; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; sd_dhcp6_client_callback_t callback; void *userdata; struct duid duid; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 589274c23b..a82e9fd06b 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -25,7 +25,7 @@ struct sd_dhcp6_lease { triple_timestamp timestamp; usec_t lifetime_t1; usec_t lifetime_t2; - usec_t max_retransmit_duration; + usec_t lifetime_valid; struct in6_addr server_address; DHCP6IA *ia_na; @@ -49,8 +49,7 @@ void dhcp6_ia_clear_addresses(DHCP6IA *ia); DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); -int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2); -int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret); +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 81b78a016f..0ebe9f503b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -597,7 +597,7 @@ static void client_reset(sd_dhcp6_client *client) { (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); - (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_expire); (void) event_source_disable(client->timeout_t1); (void) event_source_disable(client->timeout_t2); @@ -811,6 +811,31 @@ static usec_t client_timeout_compute_random(usec_t val) { return usec_sub_unsigned(val, random_u64_range(val / 10)); } +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = userdata; + DHCP6_CLIENT_DONT_DESTROY(client); + DHCP6State state; + + assert(s); + assert(client); + assert(client->event); + + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); + + state = client->state; + + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); + + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + client_set_state(client, DHCP6_STATE_SOLICITATION); + + return 0; +} + static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; @@ -845,7 +870,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) } static int client_enter_bound_state(sd_dhcp6_client *client) { - usec_t lifetime_t1, lifetime_t2; + usec_t lifetime_t1, lifetime_t2, lifetime_valid; int r; assert(client); @@ -856,10 +881,9 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { DHCP6_STATE_RENEW, DHCP6_STATE_REBIND)); - (void) event_source_disable(client->timeout_resend_expire); (void) event_source_disable(client->timeout_resend); - r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2); + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); if (r < 0) goto error; @@ -894,6 +918,21 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { goto error; } + if (lifetime_valid == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite valid lifetime"); + event_source_disable(client->timeout_expire); + } else { + log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC)); + + r = event_reset_time_relative(client->event, &client->timeout_expire, + clock_boottime_or_monotonic(), + lifetime_valid, USEC_PER_SEC, + client_timeout_expire, client, + client->event_priority, "dhcp6-lease-expire", true); + if (r < 0) + goto error; + } + client->state = DHCP6_STATE_BOUND; client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); return 0; @@ -903,36 +942,13 @@ error: return r; } -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - DHCP6_CLIENT_DONT_DESTROY(client); - DHCP6State state; - - assert(s); - assert(client); - assert(client->event); - - state = client->state; - - client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - - /* RFC 3315, section 18.1.4., says that "...the client may choose to - use a Solicit message to locate a new DHCP server..." */ - if (state == DHCP6_STATE_REBIND) - client_set_state(client, DHCP6_STATE_SOLICITATION); - - return 0; -} - static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - int r = 0; - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - usec_t max_retransmit_duration = 0; uint8_t max_retransmit_count = 0; + int r; assert(s); - assert(client); assert(client->event); (void) event_source_disable(client->timeout_resend); @@ -977,14 +993,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { - r = dhcp6_lease_get_max_retransmit_duration(client->lease, &max_retransmit_duration); - if (r < 0) { - client_stop(client, r); - return 0; - } - } - + /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ break; case DHCP6_STATE_STOPPED: @@ -1034,24 +1043,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda if (r < 0) goto error; - if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) { - - log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", - max_retransmit_duration / USEC_PER_SEC); - - r = event_reset_time(client->event, &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, USEC_PER_SEC, - client_timeout_resend_expire, client, - client->event_priority, "dhcp6-resend-expire-timer", true); - if (r < 0) - goto error; - } + return 0; error: - if (r < 0) - client_stop(client, r); - + client_stop(client, r); return 0; } @@ -1347,7 +1342,6 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { assert_not_reached(); } - (void) event_source_disable(client->timeout_resend_expire); (void) event_source_disable(client->timeout_resend); client->retransmit_time = 0; client->retransmit_count = 0; @@ -1508,7 +1502,7 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { sd_event_source_disable_unref(client->receive_message); sd_event_source_disable_unref(client->timeout_resend); - sd_event_source_disable_unref(client->timeout_resend_expire); + sd_event_source_disable_unref(client->timeout_expire); sd_event_source_disable_unref(client->timeout_t1); sd_event_source_disable_unref(client->timeout_t2); diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 9cc7e2b642..90c3373bd5 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -36,6 +36,10 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } +static usec_t sec2usec(uint32_t sec) { + return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC; +} + static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; DHCP6Address *a; @@ -66,14 +70,12 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { t2 = min_valid_lt / 10 * 8; } - assert(t2 <= min_valid_lt); - lease->max_retransmit_duration = (min_valid_lt - t2) * USEC_PER_SEC; - - lease->lifetime_t1 = t1 == UINT32_MAX ? USEC_INFINITY : t1 * USEC_PER_SEC; - lease->lifetime_t2 = t2 == UINT32_MAX ? USEC_INFINITY : t2 * USEC_PER_SEC; + lease->lifetime_valid = sec2usec(min_valid_lt); + lease->lifetime_t1 = sec2usec(t1); + lease->lifetime_t2 = sec2usec(t2); } -int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2) { +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) { assert(lease); if (!lease->ia_na && !lease->ia_pd) @@ -83,17 +85,8 @@ int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_ *ret_t1 = lease->lifetime_t1; if (ret_t2) *ret_t2 = lease->lifetime_t2; - return 0; -} - -int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret) { - assert(lease); - - if (!lease->ia_na && !lease->ia_pd) - return -ENODATA; - - if (ret) - *ret = lease->max_retransmit_duration; + if (ret_valid) + *ret_valid = lease->lifetime_valid; return 0; } From 0f3b8b809ce83c09eb6c3915c98df4afa2146cfa Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 02:48:03 +0900 Subject: [PATCH 35/66] sd-dhcp6-client: drop unnecessary event_source_disable() The event source will be soon re-enabled in the same function. The function client_timeout_resend() may return earlier without re-enabling the timer souce. However, - the timer event source is one shot by default. Hence, it is not necessary to disable in the callback function, - when it returns early, then client_set_state() or client_stop() is called before return, and they re-ernable or disable the timer. --- src/libsystemd-network/sd-dhcp6-client.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 0ebe9f503b..95ec78d828 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -951,8 +951,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda assert(s); assert(client->event); - (void) event_source_disable(client->timeout_resend); - switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: init_retransmit_time = DHCP6_INF_TIMEOUT; @@ -1342,7 +1340,6 @@ static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { assert_not_reached(); } - (void) event_source_disable(client->timeout_resend); client->retransmit_time = 0; client->retransmit_count = 0; From 22ad018b5e75ad3114bb4849428301ca37c78f31 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 02:59:31 +0900 Subject: [PATCH 36/66] sd-dhcp6-client: max_retransmit_count is only used when client is in DHCP6_STATE_REQUEST --- src/libsystemd-network/sd-dhcp6-client.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 95ec78d828..c7aec08c7b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -944,8 +944,7 @@ error: static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = ASSERT_PTR(userdata); - usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - uint8_t max_retransmit_count = 0; + usec_t time_now, init_retransmit_time, max_retransmit_time; int r; assert(s); @@ -955,7 +954,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda case DHCP6_STATE_INFORMATION_REQUEST: init_retransmit_time = DHCP6_INF_TIMEOUT; max_retransmit_time = DHCP6_INF_MAX_RT; - break; case DHCP6_STATE_SOLICITATION: @@ -967,14 +965,17 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda init_retransmit_time = DHCP6_SOL_TIMEOUT; max_retransmit_time = DHCP6_SOL_MAX_RT; - break; case DHCP6_STATE_REQUEST: + + if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + init_retransmit_time = DHCP6_REQ_TIMEOUT; max_retransmit_time = DHCP6_REQ_MAX_RT; - max_retransmit_count = DHCP6_REQ_MAX_RC; - break; case DHCP6_STATE_RENEW: @@ -984,7 +985,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.3. says max retransmit duration will be the remaining time until T2. Instead of setting MRD, wait for T2 to trigger with the same end result */ - break; case DHCP6_STATE_REBIND: @@ -996,17 +996,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return 0; default: assert_not_reached(); } - if (max_retransmit_count > 0 && - client->retransmit_count >= max_retransmit_count) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) goto error; From ec7baf998dbb404443a65d0d8dcfd1d7f1e9e245 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:03:24 +0900 Subject: [PATCH 37/66] sd-dhcp6-client: use event_reset_time_relative() at one more place --- .../fuzz-dhcp6-client-send.c | 7 +-- src/libsystemd-network/sd-dhcp6-client.c | 47 ++++++++----------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/libsystemd-network/fuzz-dhcp6-client-send.c b/src/libsystemd-network/fuzz-dhcp6-client-send.c index d05bb55c43..906c835b03 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client-send.c +++ b/src/libsystemd-network/fuzz-dhcp6-client-send.c @@ -22,7 +22,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - usec_t time_now; int r; if (size < sizeof(DHCP6Message)) @@ -46,13 +45,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (r < 0) goto cleanup; - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto cleanup; - if (client->state != DHCP6_STATE_REQUEST) client->state = DHCP6_STATE_SOLICITATION; - (void) client_send_message(client, time_now); + (void) client_send_message(client); cleanup: assert_se(sd_dhcp6_client_stop(client) >= 0); return 0; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index c7aec08c7b..70eeb72232 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -690,7 +690,7 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) } } -static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { +static int client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; @@ -698,11 +698,16 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; - usec_t elapsed_usec; + usec_t elapsed_usec, time_now; be16_t elapsed_time; int r; assert(client); + assert(client->event); + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; len = sizeof(DHCP6Message) + optlen; @@ -944,7 +949,7 @@ error: static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = ASSERT_PTR(userdata); - usec_t time_now, init_retransmit_time, max_retransmit_time; + usec_t init_retransmit_time, max_retransmit_time; int r; assert(s); @@ -1000,45 +1005,33 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda assert_not_reached(); } - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - r = client_send_message(client, time_now); + r = client_send_message(client); if (r >= 0) client->retransmit_count++; if (client->retransmit_time == 0) { - client->retransmit_time = - client_timeout_compute_random(init_retransmit_time); + client->retransmit_time = client_timeout_compute_random(init_retransmit_time); if (client->state == DHCP6_STATE_SOLICITATION) client->retransmit_time += init_retransmit_time / 10; - } else { - assert(max_retransmit_time > 0); - if (client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - } + } else if (client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); log_dhcp6_client(client, "Next retransmission in %s", FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timer", true); + r = event_reset_time_relative(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) - goto error; + client_stop(client, r); return 0; - -error: - client_stop(client, r); - return 0; } static int client_ensure_iaid(sd_dhcp6_client *client) { From af2b484141029f6ca261575648aaa8d314973a9f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:19:40 +0900 Subject: [PATCH 38/66] sd-dhcp6-client: merge client_start() and client_reset() --- src/libsystemd-network/sd-dhcp6-client.c | 26 ++++++++---------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 70eeb72232..5a046414d7 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -584,17 +584,15 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static void client_reset(sd_dhcp6_client *client) { +static void client_stop(sd_dhcp6_client *client, int error) { + DHCP6_CLIENT_DONT_DESTROY(client); + assert(client); + client_notify(client, error); + client->lease = sd_dhcp6_lease_unref(client->lease); - client->transaction_id = 0; - client->transaction_start = 0; - - client->retransmit_time = 0; - client->retransmit_count = 0; - (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); (void) event_source_disable(client->timeout_expire); @@ -604,16 +602,6 @@ static void client_reset(sd_dhcp6_client *client) { client->state = DHCP6_STATE_STOPPED; } -static void client_stop(sd_dhcp6_client *client, int error) { - DHCP6_CLIENT_DONT_DESTROY(client); - - assert(client); - - client_notify(client, error); - - client_reset(client); -} - static int client_append_common_options_in_managed_mode( sd_dhcp6_client *client, uint8_t **opt, @@ -1388,7 +1376,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (!client->information_request && client->request_ia == 0) return -EINVAL; - client_reset(client); + /* Even if the client is in the STOPPED state, the lease acquired in the previous information + * request may be stored. */ + client->lease = sd_dhcp6_lease_unref(client->lease); r = client_ensure_iaid(client); if (r < 0) From 0eca25ba0098803b0b14c680aafd77c42bf12b17 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:23:34 +0900 Subject: [PATCH 39/66] sd-dhcp6-client: move client_ensure_iaid() No functional change. --- src/libsystemd-network/sd-dhcp6-client.c | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5a046414d7..044cf31733 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -384,10 +384,27 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { return 0; } -void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { +static int client_ensure_iaid(sd_dhcp6_client *client) { + int r; + uint32_t iaid; + assert(client); - client->test_mode = test_mode; + if (client->iaid_set) + return 0; + + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + /* legacy_unstable_byteorder = */ true, + /* use_mac = */ client->test_mode, + &iaid); + if (r < 0) + return r; + + client->ia_na.header.id = iaid; + client->ia_pd.header.id = iaid; + client->iaid_set = true; + + return 0; } int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { @@ -402,6 +419,12 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { return 0; } +void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { + assert(client); + + client->test_mode = test_mode; +} + int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { @@ -1022,29 +1045,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda return 0; } -static int client_ensure_iaid(sd_dhcp6_client *client) { - int r; - uint32_t iaid; - - assert(client); - - if (client->iaid_set) - return 0; - - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, - /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, - &iaid); - if (r < 0) - return r; - - client->ia_na.header.id = iaid; - client->ia_pd.header.id = iaid; - client->iaid_set = true; - - return 0; -} - static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { const char *type_str; From c50c9e509f5cda6dc872f910c2a96a28934c9103 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:34:45 +0900 Subject: [PATCH 40/66] sd-dhcp6-client: move client_timeout_resend() and client_set_state() These are deeply related to client_message_send(). Hence, move them near the function. --- src/libsystemd-network/sd-dhcp6-client.c | 285 +++++++++++------------ 1 file changed, 142 insertions(+), 143 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 044cf31733..2cfa7623e9 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -827,6 +827,148 @@ static usec_t client_timeout_compute_random(usec_t val) { return usec_sub_unsigned(val, random_u64_range(val / 10)); } +static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + usec_t init_retransmit_time, max_retransmit_time; + int r; + + assert(s); + assert(client->event); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + init_retransmit_time = DHCP6_INF_TIMEOUT; + max_retransmit_time = DHCP6_INF_MAX_RT; + break; + + case DHCP6_STATE_SOLICITATION: + + if (client->retransmit_count > 0 && client->lease) { + client_set_state(client, DHCP6_STATE_REQUEST); + return 0; + } + + init_retransmit_time = DHCP6_SOL_TIMEOUT; + max_retransmit_time = DHCP6_SOL_MAX_RT; + break; + + case DHCP6_STATE_REQUEST: + + if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + + init_retransmit_time = DHCP6_REQ_TIMEOUT; + max_retransmit_time = DHCP6_REQ_MAX_RT; + break; + + case DHCP6_STATE_RENEW: + init_retransmit_time = DHCP6_REN_TIMEOUT; + max_retransmit_time = DHCP6_REN_MAX_RT; + + /* RFC 3315, section 18.1.3. says max retransmit duration will + be the remaining time until T2. Instead of setting MRD, + wait for T2 to trigger with the same end result */ + break; + + case DHCP6_STATE_REBIND: + init_retransmit_time = DHCP6_REB_TIMEOUT; + max_retransmit_time = DHCP6_REB_MAX_RT; + + /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ + break; + + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } + + r = client_send_message(client); + if (r >= 0) + client->retransmit_count++; + + if (client->retransmit_time == 0) { + client->retransmit_time = client_timeout_compute_random(init_retransmit_time); + + if (client->state == DHCP6_STATE_SOLICITATION) + client->retransmit_time += init_retransmit_time / 10; + + } else if (client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); + + log_dhcp6_client(client, "Next retransmission in %s", + FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); + + r = event_reset_time_relative(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); + if (r < 0) + client_stop(client, r); + + return 0; +} + +static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { + int r; + + assert_return(client, -EINVAL); + assert_return(client->event, -EINVAL); + assert_return(client->ifindex > 0, -EINVAL); + + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_SOLICITATION: + assert(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_REQUEST: + assert(client->state == DHCP6_STATE_SOLICITATION); + break; + case DHCP6_STATE_RENEW: + assert(client->state == DHCP6_STATE_BOUND); + break; + case DHCP6_STATE_REBIND: + assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); + break; + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } + + client->state = state; + client->retransmit_time = 0; + client->retransmit_count = 0; + client->transaction_id = random_u32() & htobe32(0x00ffffff); + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &client->transaction_start); + if (r < 0) + goto error; + + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); + if (r < 0) + goto error; + + r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); + if (r < 0) + goto error; + + return 0; + +error: + client_stop(client, r); + return r; +} + static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); @@ -958,93 +1100,6 @@ error: return r; } -static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = ASSERT_PTR(userdata); - usec_t init_retransmit_time, max_retransmit_time; - int r; - - assert(s); - assert(client->event); - - switch (client->state) { - case DHCP6_STATE_INFORMATION_REQUEST: - init_retransmit_time = DHCP6_INF_TIMEOUT; - max_retransmit_time = DHCP6_INF_MAX_RT; - break; - - case DHCP6_STATE_SOLICITATION: - - if (client->retransmit_count > 0 && client->lease) { - client_set_state(client, DHCP6_STATE_REQUEST); - return 0; - } - - init_retransmit_time = DHCP6_SOL_TIMEOUT; - max_retransmit_time = DHCP6_SOL_MAX_RT; - break; - - case DHCP6_STATE_REQUEST: - - if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - - init_retransmit_time = DHCP6_REQ_TIMEOUT; - max_retransmit_time = DHCP6_REQ_MAX_RT; - break; - - case DHCP6_STATE_RENEW: - init_retransmit_time = DHCP6_REN_TIMEOUT; - max_retransmit_time = DHCP6_REN_MAX_RT; - - /* RFC 3315, section 18.1.3. says max retransmit duration will - be the remaining time until T2. Instead of setting MRD, - wait for T2 to trigger with the same end result */ - break; - - case DHCP6_STATE_REBIND: - init_retransmit_time = DHCP6_REB_TIMEOUT; - max_retransmit_time = DHCP6_REB_MAX_RT; - - /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ - break; - - case DHCP6_STATE_STOPPED: - case DHCP6_STATE_BOUND: - default: - assert_not_reached(); - } - - r = client_send_message(client); - if (r >= 0) - client->retransmit_count++; - - if (client->retransmit_time == 0) { - client->retransmit_time = client_timeout_compute_random(init_retransmit_time); - - if (client->state == DHCP6_STATE_SOLICITATION) - client->retransmit_time += init_retransmit_time / 10; - - } else if (client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - - log_dhcp6_client(client, "Next retransmission in %s", - FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); - - r = event_reset_time_relative(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - client->retransmit_time, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timer", true); - if (r < 0) - client_stop(client, r); - - return 0; -} - static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { const char *type_str; @@ -1287,62 +1342,6 @@ static int client_receive_message( return 0; } -static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { - int r; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - - switch (state) { - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_SOLICITATION: - assert(client->state == DHCP6_STATE_STOPPED); - break; - case DHCP6_STATE_REQUEST: - assert(client->state == DHCP6_STATE_SOLICITATION); - break; - case DHCP6_STATE_RENEW: - assert(client->state == DHCP6_STATE_BOUND); - break; - case DHCP6_STATE_REBIND: - assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); - break; - case DHCP6_STATE_STOPPED: - case DHCP6_STATE_BOUND: - default: - assert_not_reached(); - } - - client->retransmit_time = 0; - client->retransmit_count = 0; - - client->state = state; - client->transaction_id = random_u32() & htobe32(0x00ffffff); - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &client->transaction_start); - if (r < 0) - goto error; - - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timeout", true); - if (r < 0) - goto error; - - r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); - if (r < 0) - goto error; - - return 0; - -error: - client_stop(client, r); - return r; -} - int sd_dhcp6_client_stop(sd_dhcp6_client *client) { if (!client) return 0; From cfcc85bbca12dd7e5c07b2ceea15355d48aac629 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:42:07 +0900 Subject: [PATCH 41/66] sd-dhcp6-client: log message is processed before state is changed --- src/libsystemd-network/sd-dhcp6-client.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 2cfa7623e9..0057491162 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1132,6 +1132,8 @@ static int client_process_information( if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); @@ -1162,6 +1164,8 @@ static int client_process_reply( if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); @@ -1201,6 +1205,8 @@ static int client_process_advertise_or_rapid_commit_reply( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received reply message without rapid commit flag, ignoring."); + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); @@ -1217,17 +1223,16 @@ static int client_process_advertise_or_rapid_commit_reply( return r; } + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + if (!client->lease || pref_advertise > pref_lease) { /* If this is the first advertise message or has higher preference, then save the lease. */ sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); } - if (pref_advertise == 255 || client->retransmit_count > 1) { - r = client_set_state(client, DHCP6_STATE_REQUEST); - if (r < 0) - return r; - } + if (pref_advertise == 255 || client->retransmit_count > 1) + (void) client_set_state(client, DHCP6_STATE_REQUEST); return 0; } @@ -1336,9 +1341,6 @@ static int client_receive_message( assert_not_reached(); } - log_dhcp6_client(client, "Recv %s", - dhcp6_message_type_to_string(message->type)); - return 0; } From a8d1012f30be985a0bc9289ba6efcbb879484591 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:43:25 +0900 Subject: [PATCH 42/66] sd-dhcp6-client: voidify client_set_state() --- src/libsystemd-network/sd-dhcp6-client.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 0057491162..4eacb7ebb6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -844,7 +844,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - client_set_state(client, DHCP6_STATE_REQUEST); + (void) client_set_state(client, DHCP6_STATE_REQUEST); return 0; } @@ -989,7 +989,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.4., says that "...the client may choose to use a Solicit message to locate a new DHCP server..." */ if (state == DHCP6_STATE_REBIND) - client_set_state(client, DHCP6_STATE_SOLICITATION); + (void) client_set_state(client, DHCP6_STATE_SOLICITATION); return 0; } @@ -1006,7 +1006,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T2"); - client_set_state(client, DHCP6_STATE_REBIND); + (void) client_set_state(client, DHCP6_STATE_REBIND); return 0; } @@ -1022,7 +1022,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T1"); - client_set_state(client, DHCP6_STATE_RENEW); + (void) client_set_state(client, DHCP6_STATE_RENEW); return 0; } From fa78d165bb478404887e33fad7ca60a5da489e54 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 03:53:08 +0900 Subject: [PATCH 43/66] sd-dhcp6-client: use proper type for string table lookup funcs --- src/libsystemd-network/dhcp6-internal.h | 8 ++++---- src/libsystemd-network/sd-dhcp6-client.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index c2655827b8..9961a25faa 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -167,10 +167,10 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); -const char *dhcp6_message_type_to_string(int s) _const_; -int dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(int s) _const_; -int dhcp6_message_status_from_string(const char *s) _pure_; +const char *dhcp6_message_type_to_string(DHCP6MessageType t) _const_; +DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; +DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 4eacb7ebb6..1632181fef 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -37,7 +37,7 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; -const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { +static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { [DHCP6_MESSAGE_SOLICIT] = "Solicit", [DHCP6_MESSAGE_ADVERTISE] = "Advertise", [DHCP6_MESSAGE_REQUEST] = "Request", @@ -75,9 +75,9 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { [DHCP6_MESSAGE_CONTACT] = "Contact", }; -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); -const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { +static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { [DHCP6_STATUS_SUCCESS] = "Success", [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", @@ -103,7 +103,7 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", }; -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) From e5d69be216aab7649f43a89c0fa39855603b0215 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 04:04:34 +0900 Subject: [PATCH 44/66] sd-dhcp6-client: rename client_set_state() -> client_start_transaction() Previously, it obeys state transition, but now it is only used when (re-)starting transaction. Let's rename again in more explanative name. --- src/libsystemd-network/sd-dhcp6-client.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1632181fef..c079705845 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -108,7 +108,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) -static int client_set_state(sd_dhcp6_client *client, DHCP6State state); +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state); int sd_dhcp6_client_set_callback( sd_dhcp6_client *client, @@ -844,7 +844,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - (void) client_set_state(client, DHCP6_STATE_REQUEST); + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); return 0; } @@ -914,7 +914,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda return 0; } -static int client_set_state(sd_dhcp6_client *client, DHCP6State state) { +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { int r; assert_return(client, -EINVAL); @@ -989,7 +989,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.4., says that "...the client may choose to use a Solicit message to locate a new DHCP server..." */ if (state == DHCP6_STATE_REBIND) - (void) client_set_state(client, DHCP6_STATE_SOLICITATION); + (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION); return 0; } @@ -1006,7 +1006,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T2"); - (void) client_set_state(client, DHCP6_STATE_REBIND); + (void) client_start_transaction(client, DHCP6_STATE_REBIND); return 0; } @@ -1022,7 +1022,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) log_dhcp6_client(client, "Timeout T1"); - (void) client_set_state(client, DHCP6_STATE_RENEW); + (void) client_start_transaction(client, DHCP6_STATE_RENEW); return 0; } @@ -1232,7 +1232,7 @@ static int client_process_advertise_or_rapid_commit_reply( } if (pref_advertise == 255 || client->retransmit_count > 1) - (void) client_set_state(client, DHCP6_STATE_REQUEST); + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); return 0; } @@ -1433,7 +1433,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { log_dhcp6_client(client, "Started in %s mode", client->information_request ? "Information request" : "Managed"); - return client_set_state(client, state); + return client_start_transaction(client, state); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { From 65b85f23406514588176265e623fddfaf7bdc7de Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 04:15:22 +0900 Subject: [PATCH 45/66] sd-dhcp6-client: log state transition --- src/libsystemd-network/sd-dhcp6-client.c | 35 ++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index c079705845..0a849821ad 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -37,6 +37,18 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; +static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { + [DHCP6_STATE_STOPPED] = "stopped", + [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", + [DHCP6_STATE_SOLICITATION] = "solicitation", + [DHCP6_STATE_REQUEST] = "request", + [DHCP6_STATE_BOUND] = "bound", + [DHCP6_STATE_RENEW] = "renew", + [DHCP6_STATE_REBIND] = "rebind", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); + static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { [DHCP6_MESSAGE_SOLICIT] = "Solicit", [DHCP6_MESSAGE_ADVERTISE] = "Advertise", @@ -600,6 +612,18 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { return 0; } +static void client_set_state(sd_dhcp6_client *client, DHCP6State state) { + assert(client); + + if (client->state == state) + return; + + log_dhcp6_client(client, "State changed: %s -> %s", + dhcp6_state_to_string(client->state), dhcp6_state_to_string(state)); + + client->state = state; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -622,7 +646,7 @@ static void client_stop(sd_dhcp6_client *client, int error) { (void) event_source_disable(client->timeout_t1); (void) event_source_disable(client->timeout_t2); - client->state = DHCP6_STATE_STOPPED; + client_set_state(client, DHCP6_STATE_STOPPED); } static int client_append_common_options_in_managed_mode( @@ -941,7 +965,8 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { assert_not_reached(); } - client->state = state; + client_set_state(client, state); + client->retransmit_time = 0; client->retransmit_count = 0; client->transaction_id = random_u32() & htobe32(0x00ffffff); @@ -1091,7 +1116,7 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { goto error; } - client->state = DHCP6_STATE_BOUND; + client_set_state(client, DHCP6_STATE_BOUND); client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); return 0; @@ -1138,7 +1163,7 @@ static int client_process_information( client->lease = TAKE_PTR(lease); (void) event_source_disable(client->timeout_resend); - client->state = DHCP6_STATE_STOPPED; + client_set_state(client, DHCP6_STATE_STOPPED); client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); return 0; @@ -1430,7 +1455,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { state = DHCP6_STATE_INFORMATION_REQUEST; } - log_dhcp6_client(client, "Started in %s mode", + log_dhcp6_client(client, "Starting in %s mode", client->information_request ? "Information request" : "Managed"); return client_start_transaction(client, state); From 6f4490bb0c0bb878dfdbf38c607464a8943556a1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 04:34:05 +0900 Subject: [PATCH 46/66] sd-dhcp6-client: adjust assertions --- src/libsystemd-network/sd-dhcp6-client.c | 92 ++++++++++-------------- 1 file changed, 38 insertions(+), 54 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 0a849821ad..f90e81f85a 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -137,8 +137,8 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(ifindex > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ifindex = ifindex; return 0; @@ -174,9 +174,9 @@ int sd_dhcp6_client_set_local_address( const struct in6_addr *local_address) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(local_address, -EINVAL); assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->local_address = *local_address; @@ -191,7 +191,9 @@ int sd_dhcp6_client_set_mac( assert_return(client, -EINVAL); assert_return(addr, -EINVAL); assert_return(addr_len <= HW_ADDR_MAX_SIZE, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + /* Unlike the other setters, it is OK to set a new MAC address while the client is running, + * as the MAC address is used only when setting DUID or IAID. */ if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); @@ -218,7 +220,7 @@ int sd_dhcp6_client_set_prefix_delegation_hint( _cleanup_free_ DHCP6Address *prefix = NULL; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (!pd_prefix) { /* clear previous assignments. */ @@ -245,6 +247,7 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * int r; assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(v, -EINVAL); r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); @@ -257,6 +260,8 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * } static int client_ensure_duid(sd_dhcp6_client *client) { + assert(client); + if (client->duid_len != 0) return 0; @@ -277,8 +282,8 @@ static int dhcp6_client_set_duid_internal( int r; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(duid_len == 0 || duid, -EINVAL); if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); @@ -387,7 +392,7 @@ int sd_dhcp6_client_duid_as_string( int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->ia_na.header.id = htobe32(iaid); client->ia_pd.header.id = htobe32(iaid); @@ -442,6 +447,7 @@ int sd_dhcp6_client_set_fqdn( const char *fqdn) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); /* Make sure FQDN qualifies as DNS and as Linux hostname */ if (fqdn && @@ -453,7 +459,7 @@ int sd_dhcp6_client_set_fqdn( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->information_request = enabled; @@ -473,7 +479,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) size_t t; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (!dhcp6_option_can_request(option)) return -EINVAL; @@ -492,7 +498,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(mudurl, -EINVAL); assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); assert_return(http_url_is_valid(mudurl), -EINVAL); @@ -505,7 +511,7 @@ int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(user_class), -EINVAL); STRV_FOREACH(p, user_class) { @@ -527,7 +533,7 @@ int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * con char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(vendor_class), -EINVAL); STRV_FOREACH(p, vendor_class) { @@ -555,6 +561,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation); @@ -572,6 +579,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request); @@ -721,7 +729,7 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) case DHCP6_STATE_REBIND: return DHCP6_MESSAGE_REBIND; default: - return -EINVAL; + assert_not_reached(); } } @@ -729,7 +737,6 @@ static int client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; - DHCP6MessageType message_type; struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; @@ -753,12 +760,7 @@ static int client_send_message(sd_dhcp6_client *client) { opt = (uint8_t *)(message + 1); message->transaction_id = client->transaction_id; - - message_type = client_message_type_from_state(client); - if (message_type < 0) - return message_type; - - message->type = message_type; + message->type = client_message_type_from_state(client); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -797,7 +799,6 @@ static int client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return -EINVAL; default: assert_not_reached(); } @@ -856,7 +857,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda usec_t init_retransmit_time, max_retransmit_time; int r; - assert(s); assert(client->event); switch (client->state) { @@ -941,9 +941,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { int r; - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); + assert(client); + assert(client->event); switch (state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -995,14 +994,10 @@ error: } static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); DHCP6_CLIENT_DONT_DESTROY(client); DHCP6State state; - assert(s); - assert(client); - assert(client->event); - (void) event_source_disable(client->timeout_expire); (void) event_source_disable(client->timeout_t2); (void) event_source_disable(client->timeout_t1); @@ -1020,11 +1015,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userda } static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); + sd_dhcp6_client *client = ASSERT_PTR(userdata); (void) event_source_disable(client->timeout_t2); (void) event_source_disable(client->timeout_t1); @@ -1037,11 +1028,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) } static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); + sd_dhcp6_client *client = ASSERT_PTR(userdata); (void) event_source_disable(client->timeout_t1); @@ -1150,6 +1137,9 @@ static int client_process_information( _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; int r; + assert(client); + assert(message); + if (message->type != DHCP6_MESSAGE_REPLY) return log_invalid_message_type(client, message); @@ -1268,7 +1258,7 @@ static int client_receive_message( revents, void *userdata) { - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); DHCP6_CLIENT_DONT_DESTROY(client); /* This needs to be initialized with zero. See #20741. */ CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {}; @@ -1288,10 +1278,6 @@ static int client_receive_message( struct in6_addr *server_address = NULL; ssize_t buflen, len; - assert(s); - assert(client); - assert(client->event); - buflen = next_datagram_size_fd(fd); if (buflen < 0) { if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen)) @@ -1395,12 +1381,8 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); - - if (client->state != DHCP6_STATE_STOPPED) - return -EBUSY; - - if (!client->information_request && client->request_ia == 0) - return -EINVAL; + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(client->information_request || client->request_ia != 0, -EINVAL); /* Even if the client is in the STOPPED state, the lease acquired in the previous information * request may be stored. */ @@ -1466,6 +1448,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 assert_return(client, -EINVAL); assert_return(!client->event, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (event) client->event = sd_event_ref(event); @@ -1482,6 +1465,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->event = sd_event_unref(client->event); @@ -1495,7 +1479,8 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { } static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { - assert(client); + if (!client) + return NULL; sd_dhcp6_lease_unref(client->lease); @@ -1504,11 +1489,10 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { sd_event_source_disable_unref(client->timeout_expire); sd_event_source_disable_unref(client->timeout_t1); sd_event_source_disable_unref(client->timeout_t2); + sd_event_unref(client->event); client->fd = safe_close(client->fd); - sd_dhcp6_client_detach_event(client); - free(client->req_opts); free(client->fqdn); free(client->mudurl); From 6f8ff34218be17a485534cce1712c80e2ce599d4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 05:21:31 +0900 Subject: [PATCH 47/66] sd-dhcp6-client: stop IO event source when client entered bound state Also when the client receives a reply when running in information requesting mode. --- src/libsystemd-network/sd-dhcp6-client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index f90e81f85a..e6eecbbe36 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1051,6 +1051,7 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { DHCP6_STATE_RENEW, DHCP6_STATE_REBIND)); + (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); @@ -1152,6 +1153,8 @@ static int client_process_information( sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); + /* Do not call client_stop() here, as it frees the acquired lease. */ + (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); client_set_state(client, DHCP6_STATE_STOPPED); @@ -1346,8 +1349,6 @@ static int client_receive_message( case DHCP6_STATE_BOUND: case DHCP6_STATE_STOPPED: - return 0; - default: assert_not_reached(); } From 1978efb9a565acc24164939b9f2def64e6f368e0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 05:21:52 +0900 Subject: [PATCH 48/66] sd-dhcp6-client: use struct hw_addr_data --- src/libsystemd-network/dhcp6-internal.h | 3 +-- src/libsystemd-network/sd-dhcp6-client.c | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 9961a25faa..40578220d3 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -93,8 +93,7 @@ typedef struct sd_dhcp6_client { int ifindex; char *ifname; struct in6_addr local_address; - uint8_t mac_addr[HW_ADDR_MAX_SIZE]; - size_t mac_addr_len; + struct hw_addr_data hw_addr; uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e6eecbbe36..87f297e2d6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -185,12 +185,13 @@ int sd_dhcp6_client_set_local_address( int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, - const uint8_t *addr, size_t addr_len, + const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { assert_return(client, -EINVAL); assert_return(addr, -EINVAL); - assert_return(addr_len <= HW_ADDR_MAX_SIZE, -EINVAL); + assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL); /* Unlike the other setters, it is OK to set a new MAC address while the client is running, * as the MAC address is used only when setting DUID or IAID. */ @@ -201,12 +202,12 @@ int sd_dhcp6_client_set_mac( assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else { client->arp_type = ARPHRD_NONE; - client->mac_addr_len = 0; + client->hw_addr.length = 0; return 0; } - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; + memcpy(client->hw_addr.bytes, addr, addr_len); + client->hw_addr.length = addr_len; client->arp_type = arp_type; return 0; @@ -301,10 +302,10 @@ static int dhcp6_client_set_duid_internal( } else switch (duid_type) { case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) + if (client->hw_addr.length == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, &client->duid_len); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); break; @@ -314,10 +315,10 @@ static int dhcp6_client_set_duid_internal( return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); break; case DUID_TYPE_LL: - if (client->mac_addr_len == 0) + if (client->hw_addr.length == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + r = dhcp_identifier_set_duid_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, &client->duid_len); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); break; @@ -410,7 +411,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { if (client->iaid_set) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length, /* legacy_unstable_byteorder = */ true, /* use_mac = */ client->test_mode, &iaid); From 885ea2ab1cd7dc2d73314a5db4f3ac0d886ebf36 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 05:34:37 +0900 Subject: [PATCH 49/66] ordered-set: introduce ordered_set_clear() --- src/basic/ordered-set.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 3ee47350b3..c0650e0158 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -18,6 +18,14 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); #define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) +static inline void ordered_set_clear(OrderedSet *s) { + return ordered_hashmap_clear((OrderedHashmap*) s); +} + +static inline void ordered_set_clear_free(OrderedSet *s) { + return ordered_hashmap_clear_free((OrderedHashmap*) s); +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); } From 9e4dee4cec64df8db016227d25c2285827ab2665 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 05:37:02 +0900 Subject: [PATCH 50/66] sd-dhcp6-client: use OrderedSet for vendor option This also fixes memleak. --- src/libsystemd-network/dhcp6-internal.h | 5 +++-- src/libsystemd-network/dhcp6-option.c | 4 ++-- src/libsystemd-network/sd-dhcp6-client.c | 12 +++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 40578220d3..75bf879540 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -18,6 +18,7 @@ #include "list.h" #include "macro.h" #include "network-common.h" +#include "ordered-set.h" #include "sparse-endian.h" typedef struct sd_dhcp6_option { @@ -124,7 +125,7 @@ typedef struct sd_dhcp6_client { usec_t information_request_time_usec; usec_t information_refresh_time_usec; OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; + OrderedSet *vendor_options; /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ bool test_mode; @@ -137,7 +138,7 @@ 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 *user_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); int dhcp6_option_parse( const uint8_t *buf, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index ac032f07fd..544887b9bc 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -246,7 +246,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; @@ -255,7 +255,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash assert(buflen); assert(vendor_options); - ORDERED_HASHMAP_FOREACH(options, vendor_options) { + ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; size_t total; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 87f297e2d6..b59dce74a9 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -249,9 +249,14 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); + if (!v) { + /* Clear the previous assignments. */ + ordered_set_clear(client->vendor_options); + return 0; + } + + r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v); if (r < 0) return r; @@ -706,7 +711,7 @@ static int client_append_common_options_in_managed_mode( return r; } - if (!ordered_hashmap_isempty(client->vendor_options)) { + if (!ordered_set_isempty(client->vendor_options)) { r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); if (r < 0) return r; @@ -1500,6 +1505,7 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { free(client->mudurl); dhcp6_ia_clear_addresses(&client->ia_pd); ordered_hashmap_free(client->extra_options); + ordered_set_free(client->vendor_options); strv_free(client->user_class); strv_free(client->vendor_class); free(client->ifname); From 6f3fc8613133436f948ca38b125bd14650d6afce Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 13:56:36 +0900 Subject: [PATCH 51/66] sd-dhcp6-client: do not expose set_transaction_id() This is mostly for tests or fuzzers. Hence, this makes the function requires that the client is running in the test mode. Also, now the function mask the value for message type. --- src/libsystemd-network/dhcp6-internal.h | 1 + src/libsystemd-network/fuzz-dhcp6-client-send.c | 2 +- src/libsystemd-network/fuzz-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 9 ++++++--- src/systemd/sd-dhcp6-client.h | 2 -- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 75bf879540..aefcdc2d36 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -173,6 +173,7 @@ const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); #define log_dhcp6_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ diff --git a/src/libsystemd-network/fuzz-dhcp6-client-send.c b/src/libsystemd-network/fuzz-dhcp6-client-send.c index 906c835b03..16a71e72d3 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client-send.c +++ b/src/libsystemd-network/fuzz-dhcp6-client-send.c @@ -39,7 +39,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(sd_dhcp6_client_set_information_request(client, false) == 0); dhcp6_client_set_test_mode(client, true); assert_se(sd_dhcp6_client_start(client) >= 0); - assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); r = client_process_advertise_or_rapid_commit_reply(client, (DHCP6Message *) data, size, NULL, NULL); if (r < 0) diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c index f62ab468df..10dbead21c 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client.c +++ b/src/libsystemd-network/fuzz-dhcp6-client.c @@ -38,7 +38,7 @@ static void fuzz_client(const uint8_t *data, size_t size, bool is_information_re assert_se(sd_dhcp6_client_start(client) >= 0); if (size >= sizeof(DHCP6Message)) - assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index b59dce74a9..1d1e263306 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -592,10 +592,13 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { return 0; } -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { - assert_return(client, -EINVAL); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert(client); + assert(client->test_mode); - client->transaction_id = transaction_id; + /* This is for tests or fuzzers. */ + + client->transaction_id = transaction_id & htobe32(0x00ffffff); return 0; } diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index d857af5acf..1bb21e0255 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, - uint32_t transaction_id); int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); From f9971018ebe3d5c2bdaf85b752cfb06348f18c61 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 15:08:27 +0900 Subject: [PATCH 52/66] dhcp-identifier: introduce duid_type_to_string() --- src/libsystemd-network/dhcp-identifier.c | 13 ++++++++++--- src/libsystemd-network/dhcp-identifier.h | 2 ++ src/libsystemd-network/sd-dhcp6-client.c | 10 +--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 4f02022cd3..313d0a43f1 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -8,19 +8,26 @@ #include "sd-id128.h" #include "dhcp-identifier.h" -#include "dhcp6-protocol.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "stat-util.h" -#include "stdio-util.h" +#include "string-table.h" #include "udev-util.h" -#include "virt.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ +static const char * const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "DUID-LLT", + [DUID_TYPE_EN] = "DUID-EN/Vendor", + [DUID_TYPE_LL] = "DUID-LL", + [DUID_TYPE_UUID] = "UUID", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); + int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { struct duid d; diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index 6c24af0326..31a96b20b3 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -60,3 +60,5 @@ int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t a int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id); + +const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1d1e263306..1ae7a03728 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -353,14 +353,6 @@ int sd_dhcp6_client_set_duid_llt( return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); } -static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "DUID-LLT", - [DUID_TYPE_EN] = "DUID-EN/Vendor", - [DUID_TYPE_LL] = "DUID-LL", - [DUID_TYPE_UUID] = "UUID", -}; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType); - int sd_dhcp6_client_duid_as_string( sd_dhcp6_client *client, char **duid) { @@ -372,7 +364,7 @@ int sd_dhcp6_client_duid_as_string( assert_return(client->duid_len > 0, -ENODATA); assert_return(duid, -EINVAL); - v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); + v = duid_type_to_string(be16toh(client->duid.type)); if (v) { s = strdup(v); if (!s) From 5e1618fafa5fbaca85a5a0a0ccf58458cafeb022 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 15:10:25 +0900 Subject: [PATCH 53/66] dhcp-identifier: introduce dhcp_identifier_set_duid() --- src/libsystemd-network/dhcp-identifier.c | 97 +++++++++++++++--------- src/libsystemd-network/dhcp-identifier.h | 23 ++++-- src/libsystemd-network/sd-dhcp-client.c | 44 ++++------- src/libsystemd-network/sd-dhcp6-client.c | 44 ++++------- 4 files changed, 106 insertions(+), 102 deletions(-) diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 313d0a43f1..f9769300d5 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -28,7 +28,7 @@ static const char * const duid_type_table[_DUID_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); @@ -64,62 +64,68 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { return 0; } -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { +static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) { uint16_t time_from_2000y; - assert(duid); - assert(len); assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; if (t < USEC_2000) time_from_2000y = 0; else time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); - unaligned_write_be16(&duid->type, DUID_TYPE_LLT); - unaligned_write_be16(&duid->llt.htype, arp_type); - unaligned_write_be32(&duid->llt.time, time_from_2000y); - memcpy(duid->llt.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&ret_duid->llt.htype, arp_type); + unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); + memcpy(ret_duid->llt.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + *ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->llt.htype) + sizeof(ret_duid->llt.time) + addr_len; return 0; } -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { - assert(duid); - assert(len); +static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) { assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; - unaligned_write_be16(&duid->type, DUID_TYPE_LL); - unaligned_write_be16(&duid->ll.htype, arp_type); - memcpy(duid->ll.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); + unaligned_write_be16(&ret_duid->ll.htype, arp_type); + memcpy(ret_duid->ll.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + *ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->ll.htype) + addr_len; return 0; } -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { +int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; uint64_t hash; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION int r = sd_id128_get_machine(&machine_id); @@ -129,45 +135,68 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); #endif - unaligned_write_be16(&duid->type, DUID_TYPE_EN); - unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); - - *len = sizeof(duid->type) + sizeof(duid->en); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); + unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); /* a bit of snake-oil perhaps, but no need to expose the machine-id * directly; duid->en.id might not be aligned, so we need to copy */ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); - memcpy(duid->en.id, &hash, sizeof(duid->en.id)); + memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id)); + + *ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->en); return 0; } -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { +static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); if (r < 0) return r; - unaligned_write_be16(&duid->type, DUID_TYPE_UUID); - memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); + memcpy(ret_duid->raw.data, &machine_id, sizeof(machine_id)); - *len = sizeof(duid->type) + sizeof(machine_id); + *ret_len = sizeof(ret_duid->type) + sizeof(machine_id); return 0; } +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + struct duid *ret_duid, + size_t *ret_len) { + + switch (duid_type) { + case DUID_TYPE_LLT: + return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len); + case DUID_TYPE_EN: + return dhcp_identifier_set_duid_en(ret_duid, ret_len); + case DUID_TYPE_LL: + return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len); + case DUID_TYPE_UUID: + return dhcp_identifier_set_duid_uuid(ret_duid, ret_len); + default: + return -EINVAL; + } +} + int dhcp_identifier_set_iaid( int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, - void *_id) { + void *ret) { /* name is a pointer to memory in the sd_device struct, so must * have the same scope */ @@ -219,6 +248,6 @@ int dhcp_identifier_set_iaid( * behavior. */ id32 = be32toh(id32); - unaligned_write_ne32(_id, id32); + unaligned_write_ne32(ret, id32); return 0; } diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index 31a96b20b3..acf203c057 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -54,11 +54,22 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id); +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); +int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len); +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + struct duid *ret_duid, + size_t *ret_len); +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + bool use_mac, + void *ret); const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 7b296ae4a0..2106fa8a2c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -450,7 +450,7 @@ static int dhcp_client_set_iaid_duid_internal( bool iaid_append, bool iaid_set, uint32_t iaid, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { @@ -489,37 +489,19 @@ static int dhcp_client_set_iaid_duid_internal( client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len, + client->arp_type, llt_time, &client->client_id.ns.duid, &len); + if (r == -EOPNOTSUPP) + return log_dhcp_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } client->client_id_len = sizeof(client->client_id.type) + len + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1ae7a03728..ce543b7dfb 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -281,7 +281,7 @@ static int client_ensure_duid(sd_dhcp6_client *client) { */ static int dhcp6_client_set_duid_internal( sd_dhcp6_client *client, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { @@ -304,37 +304,19 @@ static int dhcp6_client_set_duid_internal( client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); client->duid_len = sizeof(client->duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->hw_addr.length == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->hw_addr.length == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length, + client->arp_type, llt_time, &client->duid, &client->duid_len); + if (r == -EOPNOTSUPP) + return log_dhcp6_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } return 0; } From ac680f766d03be2ba80e3b3c67e9c9506d2a8a8c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 15:31:33 +0900 Subject: [PATCH 54/66] dhcp-identifier: generate static and constant DUID-EN when the client is running in test mode Follow-up for 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. --- src/libsystemd-network/dhcp-identifier.c | 25 +++++++++++++++-------- src/libsystemd-network/dhcp-identifier.h | 3 ++- src/libsystemd-network/sd-dhcp-client.c | 5 +++-- src/libsystemd-network/sd-dhcp6-client.c | 4 ++-- src/libsystemd-network/test-dhcp-client.c | 2 +- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index f9769300d5..ce59216c1b 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -120,20 +120,23 @@ static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uin return 0; } -int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) { +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; uint64_t hash; + int r; assert(ret_duid); assert(ret_len); -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; -#else - machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); -#endif + if (!test_mode) { + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + } else + /* For tests, especially for fuzzers, reproducibility is important. + * Hence, use a static and constant machine ID. + * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ + machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); @@ -145,6 +148,9 @@ int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) { *ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->en); + if (test_mode) + assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); + return 0; } @@ -173,6 +179,7 @@ int dhcp_identifier_set_duid( size_t addr_len, uint16_t arp_type, usec_t llt_time, + bool test_mode, struct duid *ret_duid, size_t *ret_len) { @@ -180,7 +187,7 @@ int dhcp_identifier_set_duid( case DUID_TYPE_LLT: return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len); case DUID_TYPE_EN: - return dhcp_identifier_set_duid_en(ret_duid, ret_len); + return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len); case DUID_TYPE_LL: return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len); case DUID_TYPE_UUID: diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index acf203c057..697ba3bfbb 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -55,13 +55,14 @@ struct duid { } _packed_; int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len); +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len); int dhcp_identifier_set_duid( DUIDType duid_type, const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t llt_time, + bool test_mode, struct duid *ret_duid, size_t *ret_len); int dhcp_identifier_set_iaid( diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 2106fa8a2c..84ae5cddd6 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -492,7 +492,8 @@ static int dhcp_client_set_iaid_duid_internal( } else { r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len, - client->arp_type, llt_time, &client->client_id.ns.duid, &len); + client->arp_type, llt_time, client->test_mode, + &client->client_id.ns.duid, &len); if (r == -EOPNOTSUPP) return log_dhcp_client_errno(client, r, "Failed to set %s. MAC address is not set or " @@ -858,7 +859,7 @@ static int client_message_init( if (r < 0) return r; - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ce543b7dfb..ad6c2b550d 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -271,7 +271,7 @@ static int client_ensure_duid(sd_dhcp6_client *client) { if (client->duid_len != 0) return 0; - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len); } /** @@ -307,7 +307,7 @@ static int dhcp6_client_set_duid_internal( } else { r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length, - client->arp_type, llt_time, &client->duid, &client->duid_len); + client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len); if (r == -EOPNOTSUPP) return log_dhcp6_client_errno(client, r, "Failed to set %s. MAC address is not set or " diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 4b11cdbc16..476dcce85e 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -170,7 +170,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us struct duid duid; size_t duid_len; - assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); + assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 0); assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0); assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); From 9c9fee80027c509a53cdd6a932ededd08ad5a317 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 21:09:53 +0900 Subject: [PATCH 55/66] sd-dhcp6-client: use memcmp_nn() at one more place --- src/libsystemd-network/sd-dhcp6-lease.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 90c3373bd5..5617e85212 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -621,12 +621,13 @@ static int dhcp6_lease_parse_message( uint8_t *clientid; size_t clientid_len; if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "%s message does not contain client ID. Ignoring.", dhcp6_message_type_to_string(message->type)); - if (clientid_len != client->duid_len || - memcmp(clientid, &client->duid, clientid_len) != 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", + if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "The client ID in %s message does not match. Ignoring.", dhcp6_message_type_to_string(message->type)); if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { @@ -636,7 +637,8 @@ static int dhcp6_lease_parse_message( dhcp6_message_type_to_string(message->type)); if (!lease->ia_na && !lease->ia_pd) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "No IA_PD prefix or IA_NA address received. Ignoring."); dhcp6_lease_set_lifetime(lease); } From e1774086a7e535c52f64f2a4431943d9dd9d96a4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 21:04:50 +0900 Subject: [PATCH 56/66] sd-dhcp6-client: add log about Information Refresh Time And store IRT only when the client is running in information requesting mode. --- src/libsystemd-network/sd-dhcp6-lease.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 5617e85212..8b8c2312ea 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -630,7 +630,12 @@ static int dhcp6_lease_parse_message( "The client ID in %s message does not match. Ignoring.", dhcp6_message_type_to_string(message->type)); - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + log_dhcp6_client(client, "New information request will be refused in %s.", + FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC)); + + } else { r = dhcp6_lease_get_serverid(lease, NULL, NULL); if (r < 0) return log_dhcp6_client_errno(client, r, "%s has no server id", @@ -643,8 +648,6 @@ static int dhcp6_lease_parse_message( dhcp6_lease_set_lifetime(lease); } - client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); - return 0; } From dd73db78505dbd03c64dcafbbdae63f3bd6a58b0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 23:23:05 +0900 Subject: [PATCH 57/66] sd-dhcp6-client: reset Information Refresh Time on stop --- src/libsystemd-network/sd-dhcp6-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ad6c2b550d..4f92fd6b1c 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -631,6 +631,11 @@ static void client_stop(sd_dhcp6_client *client, int error) { client->lease = sd_dhcp6_lease_unref(client->lease); + /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode, + * even though the lease is freed below. */ + client->information_request_time_usec = 0; + client->information_refresh_time_usec = 0; + (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); (void) event_source_disable(client->timeout_expire); From 8b1cfab96258828ee76e730abc567c8d7e54b899 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Feb 2022 23:36:49 +0900 Subject: [PATCH 58/66] network: do not restart DHCPv6 client when it is already running in managed mode Otherwise, if a network has two router and one provides M flag and the other provides O flag, then the DHCPv6 client switches the running mode repeatedly. That makes the IPv6 network configuration of the host unstable. --- src/network/networkd-dhcp6.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index ab51b7377d..1293a4d913 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -432,8 +432,20 @@ int dhcp6_start_on_ra(Link *link, bool information_request) { return r; if (inf_req == information_request) + /* The client is already running in the requested mode. */ return 0; + if (!inf_req) { + log_link_debug(link, + "The DHCPv6 client is already running in the managed mode, " + "refusing to start the client in the information requesting mode."); + return 0; + } + + log_link_debug(link, + "The DHCPv6 client is running in the information requesting mode. " + "Restarting the client in the managed mode."); + r = sd_dhcp6_client_stop(link->dhcp6_client); if (r < 0) return r; From 3f09d563f49ecef0d22dab5b166f69ab7d175ba2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 04:05:00 +0900 Subject: [PATCH 59/66] sd-dhcp6-client: split dhcp6-internal.h into two Also, this moves string tables to dhcp6-protocol.c. --- src/libsystemd-network/dhcp6-internal.h | 151 ++++-------------- src/libsystemd-network/dhcp6-lease-internal.h | 9 +- src/libsystemd-network/dhcp6-option.c | 4 +- src/libsystemd-network/dhcp6-option.h | 104 ++++++++++++ src/libsystemd-network/dhcp6-protocol.c | 84 ++++++++++ src/libsystemd-network/dhcp6-protocol.h | 16 +- src/libsystemd-network/meson.build | 2 + src/libsystemd-network/sd-dhcp6-client.c | 84 ---------- src/libsystemd-network/sd-dhcp6-lease.c | 3 +- 9 files changed, 238 insertions(+), 219 deletions(-) create mode 100644 src/libsystemd-network/dhcp6-option.h create mode 100644 src/libsystemd-network/dhcp6-protocol.c diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index aefcdc2d36..81497fda14 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -12,71 +12,15 @@ #include "sd-dhcp6-client.h" #include "dhcp-identifier.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" #include "ether-addr-util.h" #include "hashmap.h" -#include "list.h" #include "macro.h" #include "network-common.h" #include "ordered-set.h" #include "sparse-endian.h" - -typedef struct sd_dhcp6_option { - unsigned n_ref; - - uint32_t enterprise_identifier; - uint16_t option; - void *data; - size_t length; -} sd_dhcp6_option; - -extern const struct hash_ops dhcp6_option_hash_ops; - -/* Common option header */ -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; - -/* Address option */ -struct iaaddr { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; -} _packed_; - -/* Prefix Delegation Prefix option */ -struct iapdprefix { - be32_t lifetime_preferred; - be32_t lifetime_valid; - uint8_t prefixlen; - struct in6_addr address; -} _packed_; - -typedef struct DHCP6Address DHCP6Address; - -struct DHCP6Address { - LIST_FIELDS(DHCP6Address, addresses); - - union { - struct iaaddr iaaddr; - struct iapdprefix iapdprefix; - }; -}; - -struct ia_header { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -typedef struct DHCP6IA { - uint16_t type; - struct ia_header header; - - LIST_HEAD(DHCP6Address, addresses); -} DHCP6IA; +#include "time-util.h" /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ typedef enum DHCP6RequestIA { @@ -85,93 +29,62 @@ typedef enum DHCP6RequestIA { DHCP6_REQUEST_IA_PD = 1 << 2, } DHCP6RequestIA; -typedef struct sd_dhcp6_client { +struct sd_dhcp6_client { unsigned n_ref; - DHCP6State state; - sd_event *event; - int event_priority; int ifindex; char *ifname; + struct in6_addr local_address; struct hw_addr_data hw_addr; uint16_t arp_type; + + sd_event *event; + sd_event_source *receive_message; + sd_event_source *timeout_resend; + sd_event_source *timeout_expire; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + int event_priority; + int fd; + + DHCP6State state; + bool information_request; + usec_t information_request_time_usec; + usec_t information_refresh_time_usec; + be32_t transaction_id; + usec_t transaction_start; + usec_t retransmit_time; + uint8_t retransmit_count; + + bool iaid_set; DHCP6IA ia_na; DHCP6IA ia_pd; DHCP6RequestIA request_ia; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - bool iaid_set; + struct duid duid; + size_t duid_len; be16_t *req_opts; size_t req_opts_len; char *fqdn; char *mudurl; char **user_class; char **vendor_class; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_expire; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; - usec_t information_request_time_usec; - usec_t information_refresh_time_usec; OrderedHashmap *extra_options; OrderedSet *vendor_options; + struct sd_dhcp6_lease *lease; + + sd_dhcp6_client_callback_t callback; + void *userdata; + /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ bool test_mode; -} sd_dhcp6_client; - -bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, 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 *user_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); - -int dhcp6_option_parse( - const uint8_t *buf, - size_t buflen, - size_t *offset, - uint16_t *ret_option_code, - size_t *ret_option_data_len, - const uint8_t **ret_option_data); -int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); -int dhcp6_option_parse_ia( - sd_dhcp6_client *client, - be32_t iaid, - uint16_t option_code, - size_t option_data_len, - const uint8_t *option_data, - DHCP6IA **ret); -int dhcp6_option_parse_addresses( - const uint8_t *optval, - size_t optlen, - struct in6_addr **addrs, - size_t *count); -int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); -int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); +}; int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); -const char *dhcp6_message_type_to_string(DHCP6MessageType t) _const_; -DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; -DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; - void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index a82e9fd06b..1fbaab96e9 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -5,11 +5,11 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include +#include #include "sd-dhcp6-lease.h" -#include "dhcp6-internal.h" +#include "dhcp6-option.h" #include "macro.h" #include "time-util.h" @@ -45,12 +45,7 @@ struct sd_dhcp6_lease { char *fqdn; }; -void dhcp6_ia_clear_addresses(DHCP6IA *ia); -DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); -DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); - int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); - int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 544887b9bc..27c907d9e9 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -9,14 +9,12 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" -#include "dhcp-identifier.h" #include "dhcp6-internal.h" -#include "dhcp6-lease-internal.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "escape.h" #include "memory-util.h" -#include "sparse-endian.h" #include "strv.h" #include "unaligned.h" diff --git a/src/libsystemd-network/dhcp6-option.h b/src/libsystemd-network/dhcp6-option.h new file mode 100644 index 0000000000..80aba7f37f --- /dev/null +++ b/src/libsystemd-network/dhcp6-option.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp6-client.h" + +#include "hash-funcs.h" +#include "list.h" +#include "macro.h" +#include "ordered-set.h" +#include "sparse-endian.h" + +typedef struct sd_dhcp6_option { + unsigned n_ref; + + uint32_t enterprise_identifier; + uint16_t option; + void *data; + size_t length; +} sd_dhcp6_option; + +extern const struct hash_ops dhcp6_option_hash_ops; + +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + +typedef struct DHCP6Address DHCP6Address; + +struct DHCP6Address { + LIST_FIELDS(DHCP6Address, addresses); + + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; +}; + +struct ia_header { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +typedef struct DHCP6IA { + uint16_t type; + struct ia_header header; + + LIST_HEAD(DHCP6Address, addresses); +} DHCP6IA; + +void dhcp6_ia_clear_addresses(DHCP6IA *ia); +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); +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, + 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 *user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); + +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data); +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA **ret); +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); diff --git a/src/libsystemd-network/dhcp6-protocol.c b/src/libsystemd-network/dhcp6-protocol.c new file mode 100644 index 0000000000..c399a7ac50 --- /dev/null +++ b/src/libsystemd-network/dhcp6-protocol.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dhcp6-protocol.h" +#include "string-table.h" + +static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { + [DHCP6_STATE_STOPPED] = "stopped", + [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", + [DHCP6_STATE_SOLICITATION] = "solicitation", + [DHCP6_STATE_REQUEST] = "request", + [DHCP6_STATE_BOUND] = "bound", + [DHCP6_STATE_RENEW] = "renew", + [DHCP6_STATE_REBIND] = "rebind", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); + +static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { + [DHCP6_MESSAGE_SOLICIT] = "Solicit", + [DHCP6_MESSAGE_ADVERTISE] = "Advertise", + [DHCP6_MESSAGE_REQUEST] = "Request", + [DHCP6_MESSAGE_CONFIRM] = "Confirm", + [DHCP6_MESSAGE_RENEW] = "Renew", + [DHCP6_MESSAGE_REBIND] = "Rebind", + [DHCP6_MESSAGE_REPLY] = "Reply", + [DHCP6_MESSAGE_RELEASE] = "Release", + [DHCP6_MESSAGE_DECLINE] = "Decline", + [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", + [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", + [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", + [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", + [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", + [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", + [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", + [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", + [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", + [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", + [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", + [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", + [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", + [DHCP6_MESSAGE_START_TLS] = "Start TLS", + [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", + [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", + [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", + [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", + [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", + [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", + [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", + [DHCP6_MESSAGE_CONNECT] = "Connect", + [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", + [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", + [DHCP6_MESSAGE_STATE] = "State", + [DHCP6_MESSAGE_CONTACT] = "Contact", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); + +static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", + [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", + [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", + [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", + [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", + [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", + [DHCP6_STATUS_DATA_MISSING] = "Data missing", + [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", + [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", + [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", + [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", + [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", + [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", + [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", + [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", + [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", + [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 5d2af439e2..f4e47857e3 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message; #define DHCP6_MIN_OPTIONS_SIZE \ 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) -#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ +#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } enum { @@ -100,13 +100,15 @@ typedef enum DHCP6MessageType { DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */ DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */ _DHCP6_MESSAGE_TYPE_MAX, - _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, + _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, } DHCP6MessageType; typedef enum DHCP6NTPSubOption { DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, + _DHCP6_NTP_SUBOPTION_MAX, + _DHCP6_NTP_SUBOPTION_INVALID = -EINVAL, } DHCP6NTPSubOption; /* @@ -138,7 +140,7 @@ typedef enum DHCP6Status { DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, _DHCP6_STATUS_MAX, - _DHCP6_STATUS_INVALID = -EINVAL, + _DHCP6_STATUS_INVALID = -EINVAL, } DHCP6Status; typedef enum DHCP6FQDNFlag { @@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag { DHCP6_FQDN_FLAG_O = 1 << 1, DHCP6_FQDN_FLAG_N = 1 << 2, } DHCP6FQDNFlag; + +const char *dhcp6_state_to_string(DHCP6State s) _const_; +const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_; +DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; +DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index a44e8c0824..f82e2b257d 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -17,6 +17,8 @@ sources = files(''' dhcp6-lease-internal.h dhcp6-network.c dhcp6-option.c + dhcp6-option.h + dhcp6-protocol.c dhcp6-protocol.h icmp6-util.c icmp6-util.h diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 4f92fd6b1c..bc76e026bd 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -14,7 +14,6 @@ #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "dns-domain.h" #include "event-util.h" #include "fd-util.h" @@ -22,12 +21,9 @@ #include "hostname-util.h" #include "in-addr-util.h" #include "io-util.h" -#include "network-common.h" #include "random-util.h" #include "socket-util.h" -#include "string-table.h" #include "strv.h" -#include "util.h" #include "web-util.h" static const uint16_t default_req_opts[] = { @@ -37,86 +33,6 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; -static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { - [DHCP6_STATE_STOPPED] = "stopped", - [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", - [DHCP6_STATE_SOLICITATION] = "solicitation", - [DHCP6_STATE_REQUEST] = "request", - [DHCP6_STATE_BOUND] = "bound", - [DHCP6_STATE_RENEW] = "renew", - [DHCP6_STATE_REBIND] = "rebind", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); - -static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { - [DHCP6_MESSAGE_SOLICIT] = "Solicit", - [DHCP6_MESSAGE_ADVERTISE] = "Advertise", - [DHCP6_MESSAGE_REQUEST] = "Request", - [DHCP6_MESSAGE_CONFIRM] = "Confirm", - [DHCP6_MESSAGE_RENEW] = "Renew", - [DHCP6_MESSAGE_REBIND] = "Rebind", - [DHCP6_MESSAGE_REPLY] = "Reply", - [DHCP6_MESSAGE_RELEASE] = "Release", - [DHCP6_MESSAGE_DECLINE] = "Decline", - [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", - [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", - [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", - [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", - [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", - [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", - [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", - [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", - [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", - [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", - [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", - [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", - [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", - [DHCP6_MESSAGE_START_TLS] = "Start TLS", - [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", - [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", - [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", - [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", - [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", - [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", - [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", - [DHCP6_MESSAGE_CONNECT] = "Connect", - [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", - [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", - [DHCP6_MESSAGE_STATE] = "State", - [DHCP6_MESSAGE_CONTACT] = "Contact", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); - -static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", - [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", - [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", - [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", - [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", - [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", - [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", - [DHCP6_STATUS_DATA_MISSING] = "Data missing", - [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", - [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", - [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", - [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", - [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", - [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", - [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", - [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", - [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", - [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); - #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 8b8c2312ea..90adad16a6 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -6,10 +6,9 @@ #include #include "alloc-util.h" +#include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "strv.h" -#include "util.h" #define IRT_DEFAULT (1 * USEC_PER_DAY) #define IRT_MINIMUM (600 * USEC_PER_SEC) From e261d31557828488a7020e1d0bde607b4e61c483 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 10 Feb 2022 21:11:32 +0900 Subject: [PATCH 60/66] sd-dhcp6-client: logs invalid NTP option Returning negative errno triggers to produce debugging log in dhcp6_lease_parse_message(). --- src/libsystemd-network/sd-dhcp6-lease.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 90adad16a6..941de2f68c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -345,7 +345,7 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt case DHCP6_NTP_SUBOPTION_SRV_ADDR: case DHCP6_NTP_SUBOPTION_MC_ADDR: if (sublen != 16) - return 0; + return -EINVAL; r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) From bbe3f62a163795153b085850023d60601199b74a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 10 Feb 2022 21:44:28 +0900 Subject: [PATCH 61/66] sd-dhcp6-client: append extra options before elapsed time option The value of elapsed time option is determined in runtime and not deterministic. It is hard to test the sent packets if it is located in the intermidiate of the packet. --- src/libsystemd-network/sd-dhcp6-client.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index bc76e026bd..45994c93ce 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -726,6 +726,12 @@ static int client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; + ORDERED_HASHMAP_FOREACH(j, client->extra_options) { + r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + if (r < 0) + return r; + } + /* RFC 8415 Section 21.9. * A client MUST include an Elapsed Time option in messages to indicate how long the client has * been trying to complete a DHCP message exchange. */ @@ -735,12 +741,6 @@ static int client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; - ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); - if (r < 0) - return r; - } - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, len - optlen); if (r < 0) From 013c6904fa5cf214c7cecf144d84b93514072ed0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 00:30:56 +0900 Subject: [PATCH 62/66] fuzz-dhcp6-client: test multiple states Then, now all three paths client_process_information(), client_process_reply(), and client_process_advertise_or_rapid_commit_reply() in client_receive_message() are fuzzed. --- src/libsystemd-network/fuzz-dhcp6-client.c | 65 ++++++++++++++-------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c index 10dbead21c..ad676e6744 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client.c +++ b/src/libsystemd-network/fuzz-dhcp6-client.c @@ -6,43 +6,49 @@ #include "sd-event.h" #include "dhcp6-internal.h" -#include "dhcp6-protocol.h" +#include "event-util.h" #include "fd-util.h" #include "fuzz.h" static int test_dhcp_fd[2] = { -1, -1 }; -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) { return len; } int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0); - return test_dhcp_fd[0]; + return TAKE_FD(test_dhcp_fd[0]); } -static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - - assert_se(sd_event_new(&e) >= 0); - assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0); - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, is_information_request_enabled) == 0); - dhcp6_client_set_test_mode(client, true); - +static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t size, DHCP6State state) { + assert_se(sd_dhcp6_client_set_information_request(client, state == DHCP6_STATE_INFORMATION_REQUEST) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); + client->state = state; + if (size >= sizeof(DHCP6Message)) assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size); - sd_event_run(e, UINT64_MAX); + assert_se(sd_event_run(sd_dhcp6_client_get_event(client), UINT64_MAX) > 0); + + /* Check the state transition. */ + if (client->state != state) + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + assert_se(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_SOLICITATION: + assert_se(IN_SET(client->state, DHCP6_STATE_REQUEST, DHCP6_STATE_BOUND)); + break; + case DHCP6_STATE_REQUEST: + assert_se(client->state == DHCP6_STATE_BOUND); + break; + default: + assert_not_reached(); + } assert_se(sd_dhcp6_client_stop(client) >= 0); @@ -50,14 +56,29 @@ static void fuzz_client(const uint8_t *data, size_t size, bool is_information_re } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; + if (size > 65536) return 0; - /* This triggers client_receive_advertise */ - fuzz_client(data, size, false); + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_dhcp6_client_new(&client) >= 0); + assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + dhcp6_client_set_test_mode(client, true); - /* This triggers client_receive_reply */ - fuzz_client(data, size, true); + fuzz_client(client, data, size, DHCP6_STATE_INFORMATION_REQUEST); + fuzz_client(client, data, size, DHCP6_STATE_SOLICITATION); + + /* If size is zero, then the resend timer will be triggered at first, + * but in the REQUEST state the client must have a lease. */ + if (size == 0) + return 0; + + fuzz_client(client, data, size, DHCP6_STATE_REQUEST); return 0; } From 7b53d3ead38d2c2256828b59bd5bdf25e9052e2d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 01:19:27 +0900 Subject: [PATCH 63/66] fuzz-dhcp6-client: merge with fuzz-dhcp6-client-send --- src/libsystemd-network/dhcp6-internal.h | 1 + .../fuzz-dhcp6-client-send.c | 54 ------------------ src/libsystemd-network/fuzz-dhcp6-client.c | 16 ++++++ src/libsystemd-network/meson.build | 4 -- src/libsystemd-network/sd-dhcp6-client.c | 4 +- .../12ad30d317800d7f731c1c8bc0854e531d5ef928 | Bin ...h-a93b8ba024ada36014c29c25cc90c668fd91ce7f | Bin .../f202c4dff34d15e41c032a66ed25d89154be1f6d | Bin 8 files changed, 19 insertions(+), 60 deletions(-) delete mode 100644 src/libsystemd-network/fuzz-dhcp6-client-send.c rename test/fuzz/{fuzz-dhcp6-client-send => fuzz-dhcp6-client}/12ad30d317800d7f731c1c8bc0854e531d5ef928 (100%) rename test/fuzz/{fuzz-dhcp6-client-send => fuzz-dhcp6-client}/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f (100%) rename test/fuzz/{fuzz-dhcp6-client-send => fuzz-dhcp6-client}/f202c4dff34d15e41c032a66ed25d89154be1f6d (100%) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 81497fda14..0d7813f613 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -85,6 +85,7 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); +int dhcp6_client_send_message(sd_dhcp6_client *client); void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); diff --git a/src/libsystemd-network/fuzz-dhcp6-client-send.c b/src/libsystemd-network/fuzz-dhcp6-client-send.c deleted file mode 100644 index 16a71e72d3..0000000000 --- a/src/libsystemd-network/fuzz-dhcp6-client-send.c +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "fuzz.h" - -#include "sd-dhcp6-client.c" - -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { - return len; -} - -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - int fd; - - fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - assert_se(fd >= 0); - - return fd; -} - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - int r; - - if (size < sizeof(DHCP6Message)) - return 0; - - assert_se(sd_event_new(&e) >= 0); - assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0); - assert_se(sd_dhcp6_client_set_fqdn(client, "example.com") == 1); - assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0); - assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0); - assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0); - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, false) == 0); - dhcp6_client_set_test_mode(client, true); - assert_se(sd_dhcp6_client_start(client) >= 0); - assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); - - r = client_process_advertise_or_rapid_commit_reply(client, (DHCP6Message *) data, size, NULL, NULL); - if (r < 0) - goto cleanup; - - if (client->state != DHCP6_STATE_REQUEST) - client->state = DHCP6_STATE_SOLICITATION; - (void) client_send_message(client); -cleanup: - assert_se(sd_dhcp6_client_stop(client) >= 0); - return 0; -} diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c index ad676e6744..32fa68d8a9 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client.c +++ b/src/libsystemd-network/fuzz-dhcp6-client.c @@ -30,6 +30,10 @@ static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t siz if (size >= sizeof(DHCP6Message)) assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); + /* These states does not require lease to send message. */ + if (IN_SET(client->state, DHCP6_STATE_INFORMATION_REQUEST, DHCP6_STATE_SOLICITATION)) + assert_se(dhcp6_client_send_message(client) >= 0); + assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size); assert_se(sd_event_run(sd_dhcp6_client_get_event(client), UINT64_MAX) > 0); @@ -50,6 +54,12 @@ static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t siz assert_not_reached(); } + /* Send message if the client has a lease. */ + if (state != DHCP6_STATE_INFORMATION_REQUEST && sd_dhcp6_client_get_lease(client, NULL) >= 0) { + client->state = DHCP6_STATE_REQUEST; + dhcp6_client_send_message(client); + } + assert_se(sd_dhcp6_client_stop(client) >= 0); test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); @@ -70,6 +80,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); dhcp6_client_set_test_mode(client, true); + /* Used when sending message. */ + assert_se(sd_dhcp6_client_set_fqdn(client, "example.com") == 1); + assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0); + assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0); + assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0); + fuzz_client(client, data, size, DHCP6_STATE_INFORMATION_REQUEST); fuzz_client(client, data, size, DHCP6_STATE_SOLICITATION); diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index f82e2b257d..63ac8165b7 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -115,10 +115,6 @@ fuzzers += [ [libshared, libsystemd_network]], - [files('fuzz-dhcp6-client-send.c'), - [libshared, - libsystemd_network]], - [files('fuzz-dhcp-server.c'), [libsystemd_network, libshared]], diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 45994c93ce..163a208a44 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -637,7 +637,7 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) } } -static int client_send_message(sd_dhcp6_client *client) { +int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; @@ -813,7 +813,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda assert_not_reached(); } - r = client_send_message(client); + r = dhcp6_client_send_message(client); if (r >= 0) client->retransmit_count++; diff --git a/test/fuzz/fuzz-dhcp6-client-send/12ad30d317800d7f731c1c8bc0854e531d5ef928 b/test/fuzz/fuzz-dhcp6-client/12ad30d317800d7f731c1c8bc0854e531d5ef928 similarity index 100% rename from test/fuzz/fuzz-dhcp6-client-send/12ad30d317800d7f731c1c8bc0854e531d5ef928 rename to test/fuzz/fuzz-dhcp6-client/12ad30d317800d7f731c1c8bc0854e531d5ef928 diff --git a/test/fuzz/fuzz-dhcp6-client-send/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f b/test/fuzz/fuzz-dhcp6-client/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f similarity index 100% rename from test/fuzz/fuzz-dhcp6-client-send/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f rename to test/fuzz/fuzz-dhcp6-client/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f diff --git a/test/fuzz/fuzz-dhcp6-client-send/f202c4dff34d15e41c032a66ed25d89154be1f6d b/test/fuzz/fuzz-dhcp6-client/f202c4dff34d15e41c032a66ed25d89154be1f6d similarity index 100% rename from test/fuzz/fuzz-dhcp6-client-send/f202c4dff34d15e41c032a66ed25d89154be1f6d rename to test/fuzz/fuzz-dhcp6-client/f202c4dff34d15e41c032a66ed25d89154be1f6d From bccb8fc61a8e1241887ab734334b961c087a6729 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 01:19:50 +0900 Subject: [PATCH 64/66] fuzz-dhcp6-client: add prefix hint and vendor option to sent message --- src/libsystemd-network/fuzz-dhcp6-client.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c index 32fa68d8a9..32e35510e5 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client.c +++ b/src/libsystemd-network/fuzz-dhcp6-client.c @@ -68,7 +68,10 @@ static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t siz int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *v1 = NULL, *v2 = NULL; struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; + struct in6_addr hint = { { { 0x3f, 0xfe, 0x05, 0x01, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; + static const char *v1_data = "hogehoge", *v2_data = "foobar"; if (size > 65536) return 0; @@ -85,6 +88,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0); assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0); assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0); + assert_se(sd_dhcp6_client_set_prefix_delegation_hint(client, 48, &hint) >= 0); + assert_se(sd_dhcp6_option_new(123, v1_data, strlen(v1_data), 12345, &v1) >= 0); + assert_se(sd_dhcp6_option_new(456, v2_data, strlen(v2_data), 45678, &v2) >= 0); + assert_se(sd_dhcp6_client_add_vendor_option(client, v1) >= 0); + assert_se(sd_dhcp6_client_add_vendor_option(client, v2) >= 0); fuzz_client(client, data, size, DHCP6_STATE_INFORMATION_REQUEST); fuzz_client(client, data, size, DHCP6_STATE_SOLICITATION); From 1f2801911367a6f7d6cbc576b963461a88fd5d6f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 10 Feb 2022 16:14:21 +0900 Subject: [PATCH 65/66] test-dhcp6-client: cleanups This does many cleanups. Most prominently, - check the sents packet more strictly, - make the packets used in the test more readable, - unify callbacks. --- src/libsystemd-network/test-dhcp6-client.c | 1163 ++++++++++---------- 1 file changed, 574 insertions(+), 589 deletions(-) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 60bb7f0ca8..5aa7ebb977 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -26,19 +26,62 @@ #include "tests.h" #include "time-util.h" -static struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +#define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77 +#define IA_ID_BYTES \ + 0x0e, 0xcf, 0xa3, 0x7d +#define IA_NA_ADDRESS1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xad +#define IA_NA_ADDRESS2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xae +#define IA_PD_PREFIX1_BYTES \ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define IA_PD_PREFIX2_BYTES \ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define DNS1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 +#define DNS2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +#define SNTP1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 +#define SNTP2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 +#define NTP1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 +#define NTP2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 +#define CLIENT_ID_BYTES \ + 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 +#define SERVER_ID_BYTES \ + 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 + +static const struct in6_addr local_address = + { { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } }; +static const struct in6_addr mcast_address = + IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; +static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } }; +static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } }; +static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } }; +static const struct in6_addr ia_pd_prefix2 = { { { IA_PD_PREFIX2_BYTES } } }; +static const struct in6_addr dns1 = { { { DNS1_BYTES } } }; +static const struct in6_addr dns2 = { { { DNS2_BYTES } } }; +static const struct in6_addr sntp1 = { { { SNTP1_BYTES } } }; +static const struct in6_addr sntp2 = { { { SNTP2_BYTES } } }; +static const struct in6_addr ntp1 = { { { NTP1_BYTES } } }; +static const struct in6_addr ntp2 = { { { NTP2_BYTES } } }; +static const uint8_t client_id[] = { CLIENT_ID_BYTES }; +static const uint8_t server_id[] = { SERVER_ID_BYTES }; +static const struct ether_addr mac = { + .ether_addr_octet = { 'A', 'B', 'C', '1', '2', '3' }, }; - -static sd_event_source *hangcheck; -static int test_dhcp_fd[2]; +static int test_fd[2] = { -1, -1, }; static int test_ifindex = 42; -static int test_client_message_num; -static be32_t test_iaid = 0; -static uint8_t test_duid[14] = { }; +static unsigned test_client_sent_message_count = 0; +static sd_dhcp6_client *client_ref = NULL; -static void test_client_basic(sd_event *e) { - sd_dhcp6_client *client; +STATIC_DESTRUCTOR_REGISTER(client_ref, sd_dhcp6_client_unrefp); + +static void test_client_basic(void) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; int v; log_debug("/* %s */", __func__); @@ -46,16 +89,10 @@ static void test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_new(&client) >= 0); assert_se(client); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0); - assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL); - assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL); assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); + assert_se(sd_dhcp6_client_set_mac(client, mac.ether_addr_octet, sizeof(mac), ARPHRD_ETHER) >= 0); assert_se(sd_dhcp6_client_set_fqdn(client, "host") == 1); assert_se(sd_dhcp6_client_set_fqdn(client, "host.domain") == 1); @@ -107,51 +144,44 @@ static void test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0); assert_se(sd_dhcp6_client_detach_event(client) >= 0); - assert_se(!sd_dhcp6_client_unref(client)); } static void test_parse_domain(void) { + _cleanup_free_ char *domain = NULL; + _cleanup_strv_free_ char **list = NULL; uint8_t *data; - char *domain; - char **list; - int r; log_debug("/* %s */", __func__); data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0 }; - r = dhcp6_option_parse_domainname(data, 13, &domain); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname(data, 13, &domain) >= 0); assert_se(domain); assert_se(streq(domain, "example.com")); - free(domain); + domain = mfree(domain); data = (uint8_t []) { 4, 't', 'e', 's', 't' }; - r = dhcp6_option_parse_domainname(data, 5, &domain); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname(data, 5, &domain) >= 0); assert_se(domain); assert_se(streq(domain, "test")); - free(domain); + domain = mfree(domain); data = (uint8_t []) { 0 }; - r = dhcp6_option_parse_domainname(data, 1, &domain); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0); data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 6, 'f', 'o', 'o', 'b', 'a', 'r', 0 }; - r = dhcp6_option_parse_domainname_list(data, 21, &list); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname_list(data, 21, &list) >= 0); assert_se(list); assert_se(streq(list[0], "example.com")); assert_se(streq(list[1], "foobar")); - strv_free(list); + assert_se(!list[2]); + list = strv_free(list); data = (uint8_t []) { 1, 'a', 0, 20, 'b', 'c' }; - r = dhcp6_option_parse_domainname_list(data, 6, &list); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname_list(data, 6, &list) < 0); data = (uint8_t []) { 0 , 0 }; - r = dhcp6_option_parse_domainname_list(data, 2, &list); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname_list(data, 2, &list) < 0); } static void test_option(void) { @@ -358,36 +388,57 @@ static void test_option_status(void) { static void test_client_parse_message_issue_22099(void) { static const uint8_t msg[] = { - /* xid */ - 0x07, 0x7c, 0x4c, 0x16, - /* status code (zero length) */ - 0x00, 0x0e, 0x00, 0x00, - /* NTP servers (broken sub option and sub option length) */ - 0x00, 0x38, 0x00, 0x14, 0x01, 0x00, 0x10, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, - /* client ID */ - 0x00, 0x01, 0x00, 0x0e, 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, - 0x15, 0x45, - /* server ID */ - 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, 0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e, + /* Message type */ + DHCP6_MESSAGE_REPLY, + /* Transaction ID */ + 0x7c, 0x4c, 0x16, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x14, + /* NTP server (broken sub option and sub option length) */ + 0x01, 0x00, 0x10, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + 0x00, 0x02, /* DUID-EN */ + 0x00, 0x00, 0xab, 0x11, /* pen */ + 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45, /* id */ + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0a, + 0x00, 0x03, /* DUID-LL */ + 0x00, 0x01, /* htype */ + 0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e, /* haddr */ /* preference */ - 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01, + 0x00, /* DNS servers */ - 0x00, 0x17, 0x00, 0x10, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, - 0xfe, 0xef, 0x1e, 0x4e, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x10, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, /* v6 pcp server */ - 0x00, 0x56, 0x00, 0x10, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff, - 0xfe, 0xef, 0x1e, 0x4e, + 0x00, SD_DHCP6_OPTION_V6_PCP_SERVER, 0x00, 0x10, + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, /* IA_NA */ - 0x00, 0x03, 0x00, 0x28, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40, - /* IA_NA (iaaddr) */ - 0x00, 0x05, 0x00, 0x18, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff, - 0xfe, 0xf1, 0x51, 0x53, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20, + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x28, + 0xcc, 0x59, 0x11, 0x7b, /* iaid */ + 0x00, 0x00, 0x07, 0x08, /* lifetime T1 */ + 0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */ + /* IA_NA (iaaddr suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff, 0xfe, 0xf1, 0x51, 0x53, /* address */ + 0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */ + 0x00, 0x00, 0x1c, 0x20, /* valid lifetime */ /* IA_PD */ - 0x00, 0x19, 0x00, 0x29, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40, - /* IA_PD (iaprefix) */ - 0x00, 0x1a, 0x00, 0x19, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20, 0x3a, 0x2a, 0x02, 0x81, - 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x29, + 0xcc, 0x59, 0x11, 0x7b, /* iaid */ + 0x00, 0x00, 0x07, 0x08, /* lifetime T1 */ + 0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */ + /* IA_PD (iaprefix suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */ + 0x00, 0x00, 0x1c, 0x20, /* valid lifetime */ + 0x3a, /* prefixlen */ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* prefix */ }; static const uint8_t duid[] = { 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45, @@ -404,614 +455,548 @@ static void test_client_parse_message_issue_22099(void) { assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0); } -static uint8_t msg_advertise[198] = { - 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, - 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03, - 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00, - 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, - 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, - 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, - 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, - 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, - 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, - 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, - 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, - 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, - 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19, - 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, - 0x53, 0x00, 0x07, 0x00, 0x01, 0x00 +static const uint8_t msg_information_request[] = { + /* Message type */ + DHCP6_MESSAGE_INFORMATION_REQUEST, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static uint8_t msg_reply[191] = { - 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, - 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01, - 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, - 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, - 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, - 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, - 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e, - 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, - 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17, - 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c, - 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, - 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d, - 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, - 0x0e, 0x01, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61 +static const uint8_t msg_solicit[] = { + /* Message type */ + DHCP6_MESSAGE_SOLICIT, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x0c, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x0c, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11, + DHCP6_FQDN_FLAG_S, + 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* User Class */ + /* Vendor Class */ + /* Vendor Options */ + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static uint8_t fqdn_wire[16] = { - 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', - 0x05, 'i', 'n', 't', 'r', 'a', 0x00 +static const uint8_t msg_request[] = { + /* Message type */ + DHCP6_MESSAGE_REQUEST, + /* Transaction ID */ + 0x00, 0x00, 0x00, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x44, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11, + DHCP6_FQDN_FLAG_S, + 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* User Class */ + /* Vendor Class */ + /* Vendor Options */ + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static void 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; - uint32_t lt_pref, lt_valid; - 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; +static const uint8_t msg_reply[] = { + /* Message type */ + DHCP6_MESSAGE_REPLY, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x01, + 0x00, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x66, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (status code suboption) */ + 0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x1e, + 0x00, 0x00, /* status code */ + 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x77, 0x65, + 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, /* status message */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* DNS servers */ + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20, + DNS1_BYTES, + DNS2_BYTES, + /* SNTP servers */ + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20, + SNTP1_BYTES, + SNTP2_BYTES, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP1_BYTES, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP2_BYTES, + /* NTP server (fqdn suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b, + 0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Domain list */ + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b, + 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12, + 0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', +}; +static const uint8_t msg_advertise[] = { + /* Message type */ + DHCP6_MESSAGE_ADVERTISE, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* Preference */ + 0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01, + 0xff, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x7a, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, /* address */ + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, /* address */ + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (status code suboption) */ + 0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x32, + 0x00, 0x00, /* status code */ + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, + 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, /* status message */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* DNS servers */ + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20, + DNS1_BYTES, + DNS2_BYTES, + /* SNTP servers */ + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20, + SNTP1_BYTES, + SNTP2_BYTES, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP1_BYTES, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP2_BYTES, + /* NTP server (fqdn suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b, + 0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Domain list */ + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b, + 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12, + 0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', +}; + +static void test_client_verify_information_request(const DHCP6Message *msg, size_t len) { log_debug("/* %s */", __func__); - assert_se(len >= sizeof(DHCP6Message)); + assert_se(len == sizeof(msg_information_request)); + /* The elapsed time value is not deterministic. Skip it. */ + assert_se(memcmp(msg, msg_information_request, len - sizeof(be16_t)) == 0); +} - assert_se(dhcp6_lease_new(&lease) >= 0); +static void test_client_verify_solicit(const DHCP6Message *msg, size_t len) { + log_debug("/* %s */", __func__); - assert_se(advertise->type == DHCP6_MESSAGE_ADVERTISE); - assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5); + assert_se(len == sizeof(msg_solicit)); + /* The elapsed time value is not deterministic. Skip it. */ + assert_se(memcmp(msg, msg_solicit, len - sizeof(be16_t)) == 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; +static void test_client_verify_request(const DHCP6Message *msg, size_t len) { + log_debug("/* %s */", __func__); - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(optlen == 14); + assert_se(len == sizeof(msg_request)); + assert_se(msg->type == DHCP6_MESSAGE_REQUEST); + /* The transaction ID and elapsed time value are not deterministic. Skip them. */ + assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options), len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0); +} - opt_clientid = true; - break; +static void test_lease_common(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + const struct in6_addr *addrs; + const char *str; + char **strv; + uint8_t *id; + size_t len; - case SD_DHCP6_OPTION_IA_NA: { - be32_t iaid = htobe32(0x0ecfa37d); + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - assert_se(optlen == 94); - assert_se(optval == &msg_advertise[26]); - assert_se(!memcmp(optval, &msg_advertise[26], optlen)); + assert_se(dhcp6_lease_get_clientid(lease, &id, &len) >= 0); + assert_se(memcmp_nn(id, len, client_id, sizeof(client_id)) == 0); - assert_se(!memcmp(optval, &iaid, sizeof(val))); + assert_se(sd_dhcp6_lease_get_domains(lease, &strv) == 1); + assert_se(streq(strv[0], "lab.intra")); + assert_se(!strv[1]); - val = htobe32(80); - assert_se(!memcmp(optval + 4, &val, sizeof(val))); + assert_se(sd_dhcp6_lease_get_fqdn(lease, &str) >= 0); + assert_se(streq(str, "client.lab.intra")); - val = htobe32(120); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 2); + assert_se(in6_addr_equal(&addrs[0], &dns1)); + assert_se(in6_addr_equal(&addrs[1], &dns2)); - assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia_na) >= 0); + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 2); + assert_se(in6_addr_equal(&addrs[0], &ntp1)); + assert_se(in6_addr_equal(&addrs[1], &ntp2)); - break; - } - case SD_DHCP6_OPTION_SERVERID: - assert_se(optlen == 14); - assert_se(optval == &msg_advertise[179]); - assert_se(!memcmp(optval, &msg_advertise[179], optlen)); + assert_se(sd_dhcp6_lease_get_ntp_fqdn(lease, &strv) == 1); + assert_se(streq(strv[0], "ntp.intra")); + assert_se(!strv[1]); - assert_se(dhcp6_lease_set_serverid(lease, optval, optlen) >= 0); - break; + assert_se(lease->sntp_count == 2); + assert_se(in6_addr_equal(&lease->sntp[0], &sntp1)); + assert_se(in6_addr_equal(&lease->sntp[1], &sntp2)); +} - case SD_DHCP6_OPTION_PREFERENCE: - assert_se(optlen == 1); - assert_se(!*optval); +static void test_lease_managed(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + struct in6_addr addr; + uint32_t lt_pref, lt_valid; + uint8_t *id, prefixlen; + size_t len; - assert_se(dhcp6_lease_set_preference(lease, *optval) >= 0); - break; + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(optlen == 2); - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0); - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - assert_se(optlen == 11); - assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0); - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0); - break; - - default: - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(pos == len); - assert_se(opt_clientid); + assert_se(dhcp6_lease_get_serverid(lease, &id, &len) >= 0); + assert_se(memcmp_nn(id, len, server_id, sizeof(server_id)) == 0); sd_dhcp6_lease_reset_address_iter(lease); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(in6_addr_equal(&addr, &ia_na_address1)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_na_address2)); assert_se(lt_pref == 150); assert_se(lt_valid == 180); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); sd_dhcp6_lease_reset_address_iter(lease); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); - sd_dhcp6_lease_reset_address_iter(lease); + assert_se(in6_addr_equal(&addr, &ia_na_address1)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(in6_addr_equal(&addr, &ia_na_address2)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); - assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); - assert_se(len == 14); - assert_se(!memcmp(opt, &msg_advertise[179], len)); - - assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); - assert_se(preference == 0); - - r = sd_dhcp6_lease_get_dns(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); - - r = sd_dhcp6_lease_get_domains(lease, &domains); - assert_se(r == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); -} - -static int test_check_completed_in_2_seconds(sd_event_source *s, uint64_t usec, void *userdata) { - assert_not_reached(); -} - -static void test_client_solicit_cb(sd_dhcp6_client *client, int event, - void *userdata) { - sd_event *e = userdata; - sd_dhcp6_lease *lease; - const struct in6_addr *addrs; - char **domains; - - log_debug("/* %s */", __func__); - - assert_se(e); - assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - - assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); - - sd_event_exit(e, 0); -} - -static void test_client_send_reply(DHCP6Message *request) { - DHCP6Message reply; - - log_debug("/* %s */", __func__); - - reply.transaction_id = request->transaction_id; - reply.type = DHCP6_MESSAGE_REPLY; - - memcpy(msg_reply, &reply.transaction_id, 4); - - memcpy(&msg_reply[26], test_duid, sizeof(test_duid)); - - memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); -} - -static void test_client_verify_request(DHCP6Message *request, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool found_clientid = false, found_iana = false, found_serverid = false, - found_elapsed_time = false, found_fqdn = false; - uint32_t lt_pref, lt_valid; - struct in6_addr addr; - size_t pos = 0; - be32_t val; - - log_debug("/* %s */", __func__); - - assert_se(request->type == DHCP6_MESSAGE_REQUEST); - assert_se(dhcp6_lease_new(&lease) >= 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); - found_clientid = true; - - assert_se(!memcmp(optval, &test_duid, - sizeof(test_duid))); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - assert_se(optlen == 40); - assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid))); - - /* T1 and T2 should not be set. */ - val = 0; - assert_se(!memcmp(optval + 4, &val, sizeof(val))); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); - - /* Then, this should refuse all addresses. */ - assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia_na) == -ENODATA); - - break; - - case SD_DHCP6_OPTION_SERVERID: - assert_se(!found_serverid); - found_serverid = true; - - assert_se(optlen == 14); - assert_se(!memcmp(&msg_advertise[179], optval, optlen)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - case SD_DHCP6_OPTION_CLIENT_FQDN: - assert_se(!found_fqdn); - found_fqdn = true; - - assert_se(optlen == 17); - - assert_se(optval[0] == 0x01); - assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix1)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix2)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); -} - -static void test_client_send_advertise(DHCP6Message *solicit) { - DHCP6Message advertise; - - log_debug("/* %s */", __func__); - - advertise.transaction_id = solicit->transaction_id; - advertise.type = DHCP6_MESSAGE_ADVERTISE; - - memcpy(msg_advertise, &advertise.transaction_id, 4); - - memcpy(&msg_advertise[8], test_duid, sizeof(test_duid)); - - memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise)); -} - -static void test_client_verify_solicit(DHCP6Message *solicit, size_t len) { - bool found_clientid = false, found_iana = false, - found_elapsed_time = false, found_fqdn = false; - size_t pos = 0; - - log_debug("/* %s */", __func__); - - assert_se(solicit->type == DHCP6_MESSAGE_SOLICIT); - - 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); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - assert_se(optlen == 12); - - memcpy(&test_iaid, optval, sizeof(test_iaid)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - - case SD_DHCP6_OPTION_CLIENT_FQDN: - assert_se(!found_fqdn); - found_fqdn = true; - - assert_se(optlen == 17); - - assert_se(optval[0] == 0x01); - assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); - - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(pos == len); - assert_se(found_clientid && found_iana && found_elapsed_time); -} - -static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { - sd_event *e = userdata; - sd_dhcp6_lease *lease; - const struct in6_addr *addrs; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - char **domains; - const char *fqdn; - - log_debug("/* %s */", __func__); - - assert_se(e); - assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_fqdn(lease, &fqdn) >= 0); - assert_se(streq(fqdn, "client.intra")); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - - assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); - assert_se(sd_dhcp6_client_stop(client) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); - - assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); - - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); - - assert_se(sd_dhcp6_client_start(client) >= 0); -} - -static void test_client_verify_information_request(DHCP6Message *information_request, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - size_t pos = 0; - bool found_clientid = false, found_elapsed_time = false; - struct in6_addr addr; - uint32_t lt_pref, lt_valid; - - log_debug("/* %s */", __func__); - - assert_se(information_request->type == DHCP6_MESSAGE_INFORMATION_REQUEST); - assert_se(dhcp6_lease_new(&lease) >= 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); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - case SD_DHCP6_OPTION_SERVERID: - assert_not_reached(); - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(pos == len); - assert_se(found_clientid && found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix1)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix2)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); + + test_lease_common(client); } -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { - struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; - DHCP6Message *message; +static void test_client_callback(sd_dhcp6_client *client, int event, void *userdata) { + switch (event) { + case SD_DHCP6_CLIENT_EVENT_STOP: + log_debug("/* %s (event=stop) */", __func__); + return; - log_debug("/* %s */", __func__); + case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: + log_debug("/* %s (event=information-request) */", __func__); - assert_se(s == test_dhcp_fd[0]); - assert_se(server_address); + assert_se(test_client_sent_message_count == 1); + + test_lease_common(client); + + assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_advertise)->transaction_id) >= 0); + break; + + case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: + log_debug("/* %s (event=ip-acquire) */", __func__); + + assert_se(IN_SET(test_client_sent_message_count, 3, 4)); + + test_lease_managed(client); + + assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0); + break; + + case DHCP6_CLIENT_EVENT_TEST_ADVERTISED: { + sd_dhcp6_lease *lease; + uint8_t preference; + + log_debug("/* %s (event=test-advertised) */", __func__); + + assert_se(test_client_sent_message_count == 2); + + test_lease_managed(client); + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); + assert_se(preference == 0xff); + + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); + break; + } + default: + assert_not_reached(); + } +} + +int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) { + log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count); + + assert_se(a); + assert_se(in6_addr_equal(a, &mcast_address)); assert_se(packet); - assert_se(len > sizeof(DHCP6Message) + 4); - assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); + assert_se(len >= sizeof(DHCP6Message)); - message = (DHCP6Message *)packet; + switch (test_client_sent_message_count) { + case 0: + test_client_verify_information_request(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; - assert_se(message->transaction_id & 0x00ffffff); + case 1: + test_client_verify_solicit(packet, len); + assert_se(write(test_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise)); + break; - if (test_client_message_num == 0) { - 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, len); - test_client_send_advertise(message); - test_client_message_num++; - } else if (test_client_message_num == 2) { - test_client_verify_request(message, len); - test_client_send_reply(message); - test_client_message_num++; + case 2: + test_client_callback(client_ref, DHCP6_CLIENT_EVENT_TEST_ADVERTISED, NULL); + test_client_verify_request(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; + + default: + assert_not_reached(); } + test_client_sent_message_count++; return len; } -int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { +int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) { assert_se(ifindex == test_ifindex); + assert_se(a); + assert_se(in6_addr_equal(a, &local_address)); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0) - return -errno; - - return test_dhcp_fd[0]; + assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0); + return TAKE_FD(test_fd[0]); } -static void test_client_solicit(sd_event *e) { - sd_dhcp6_client *client; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - int val; +static void test_dhcp6_client(void) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; log_debug("/* %s */", __func__); + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, clock_boottime_or_monotonic(), + 2 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(ETIMEDOUT)) >= 0); + assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(client); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0); - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); - assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1); + assert_se(sd_dhcp6_client_set_local_address(client, &local_address) >= 0); + assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") >= 0); + assert_se(sd_dhcp6_client_set_iaid(client, unaligned_read_be32((uint8_t[]) { IA_ID_BYTES })) >= 0); dhcp6_client_set_test_mode(client, true); - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val == 0); - assert_se(sd_dhcp6_client_set_information_request(client, 42) >= 0); - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val); - - assert_se(sd_dhcp6_client_set_callback(client, - test_client_information_cb, e) >= 0); - - assert_se(sd_event_add_time_relative(e, &hangcheck, clock_boottime_or_monotonic(), - 2 * USEC_PER_SEC, 0, - test_check_completed_in_2_seconds, NULL) >= 0); - - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); + assert_se(sd_dhcp6_client_set_callback(client, test_client_callback, NULL) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); - sd_event_loop(e); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); - hangcheck = sd_event_source_unref(hangcheck); + assert_se(client_ref = sd_dhcp6_client_ref(client)); - assert_se(!sd_dhcp6_client_unref(client)); + assert_se(sd_event_loop(e) >= 0); - test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); + assert_se(test_client_sent_message_count == 3); + + test_fd[1] = safe_close(test_fd[1]); } int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; - - assert_se(sd_event_new(&e) >= 0); - test_setup_logging(LOG_DEBUG); - test_client_basic(e); + test_client_basic(); test_parse_domain(); test_option(); test_option_status(); test_client_parse_message_issue_22099(); - test_advertise_option(e); - test_client_solicit(e); + test_dhcp6_client(); return 0; } From 119c00c1fa8abaa532c055b67493b6d8d7cefcde Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 14 Feb 2022 15:08:20 +0900 Subject: [PATCH 66/66] test-dhcp6-client: add test for rapid commit --- src/libsystemd-network/test-dhcp6-client.c | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 5aa7ebb977..3b25df7625 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -886,7 +886,21 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd test_lease_managed(client); - assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0); + switch (test_client_sent_message_count) { + case 3: + assert_se(sd_dhcp6_client_stop(client) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); + break; + + case 4: + assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0); + break; + + default: + assert_not_reached(); + } + break; case DHCP6_CLIENT_EVENT_TEST_ADVERTISED: { @@ -936,6 +950,11 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); break; + case 3: + test_client_verify_solicit(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; + default: assert_not_reached(); } @@ -983,7 +1002,7 @@ static void test_dhcp6_client(void) { assert_se(sd_event_loop(e) >= 0); - assert_se(test_client_sent_message_count == 3); + assert_se(test_client_sent_message_count == 4); test_fd[1] = safe_close(test_fd[1]); }