From: Yu Watanabe Date: Sun, 6 Feb 2022 12:59:07 +0000 (+0900) Subject: sd-dhcp6-client: introduce client_process_*() X-Git-Tag: v251-rc1~291^2~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07a3b3408b31a6194756ad5dcd9998a9ede86b7a;p=thirdparty%2Fsystemd.git 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(). --- diff --git a/src/libsystemd-network/fuzz-dhcp6-client-send.c b/src/libsystemd-network/fuzz-dhcp6-client-send.c index 39a5f4fd4dd..d05bb55c433 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 457e8ca915e..2784a4ee24f 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1020,7 +1020,22 @@ 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, @@ -1028,35 +1043,62 @@ 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; + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - if (client->state == DHCP6_STATE_SOLICITATION) { - r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); - if (r < 0) - return r; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); - if (!rapid_commit) - return 0; + 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, + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { + + _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); + + 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); - 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; - } - - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - break; - } + 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(); }