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().
This commit is contained in:
Yu Watanabe
2022-02-14 14:43:45 +09:00
parent 0123b2dbac
commit 07a3b3408b
2 changed files with 103 additions and 76 deletions
@@ -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);
+100 -71
View File
@@ -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();
}