diff options
author | chris <str77@pm.me> | 2023-01-07 21:11:28 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-01-17 21:26:18 +0900 |
commit | b895aa5ff5c56e88756dfa04efc76e30cb8b0841 (patch) | |
tree | 85888e5daee0730cf00ab1eb1a3c6f3363ad5d8d /src/libsystemd-network | |
parent | 1200777b21936bf5647a90504e0ea27e3ec3e42b (diff) | |
download | systemd-b895aa5ff5c56e88756dfa04efc76e30cb8b0841.tar.gz |
send dhcpv6 release when stopping
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r-- | src/libsystemd-network/dhcp6-internal.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.c | 1 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 83 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp6-client.c | 82 |
5 files changed, 152 insertions, 16 deletions
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 2afcda3eec..fa43f28eb5 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -79,6 +79,7 @@ struct sd_dhcp6_client { sd_dhcp6_client_callback_t callback; void *userdata; + bool send_release; /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */ bool test_mode; diff --git a/src/libsystemd-network/dhcp6-protocol.c b/src/libsystemd-network/dhcp6-protocol.c index f965ea40fe..be0f651f1a 100644 --- a/src/libsystemd-network/dhcp6-protocol.c +++ b/src/libsystemd-network/dhcp6-protocol.c @@ -11,6 +11,7 @@ static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { [DHCP6_STATE_BOUND] = "bound", [DHCP6_STATE_RENEW] = "renew", [DHCP6_STATE_REBIND] = "rebind", + [DHCP6_STATE_STOPPING] = "stopping", }; DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 18217691b7..c70f93203d 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -58,6 +58,7 @@ typedef enum DHCP6State { DHCP6_STATE_BOUND, DHCP6_STATE_RENEW, DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING, _DHCP6_STATE_MAX, _DHCP6_STATE_INVALID = -EINVAL, } DHCP6State; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 29cd003506..40edfd3908 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -498,6 +498,14 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) { return 0; } +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) { + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + client->send_release = enable; + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -586,7 +594,8 @@ static int client_append_common_options_in_managed_mode( DHCP6_STATE_SOLICITATION, DHCP6_STATE_REQUEST, DHCP6_STATE_RENEW, - DHCP6_STATE_REBIND)); + DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING)); assert(buf); assert(*buf); assert(offset); @@ -603,9 +612,11 @@ static int client_append_common_options_in_managed_mode( return r; } - r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); - if (r < 0) - return r; + if (client->state != DHCP6_STATE_STOPPING) { + r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); + if (r < 0) + return r; + } r = dhcp6_option_append_user_class(buf, offset, client->user_class); if (r < 0) @@ -636,6 +647,8 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) return DHCP6_MESSAGE_RENEW; case DHCP6_STATE_REBIND: return DHCP6_MESSAGE_REBIND; + case DHCP6_STATE_STOPPING: + return DHCP6_MESSAGE_RELEASE; default: assert_not_reached(); } @@ -679,6 +692,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off req_opts = p; break; + case DHCP6_STATE_STOPPING: + return 0; + default: n = client->n_req_opts; req_opts = client->req_opts; @@ -690,6 +706,22 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); } +static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) { + assert(client); + assert(buf); + assert(*buf); + assert(offset); + + if (!client->mudurl) + return 0; + + if (client->state == DHCP6_STATE_STOPPING) + return 0; + + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); +} + int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ uint8_t *buf = NULL; struct in6_addr all_servers = @@ -735,7 +767,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - + case DHCP6_STATE_STOPPING: r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); @@ -753,18 +785,15 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { return r; break; - case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: + case DHCP6_STATE_STOPPED: default: assert_not_reached(); } - if (client->mudurl) { - r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6, - strlen(client->mudurl), client->mudurl); - if (r < 0) - return r; - } + r = client_append_mudurl(client, &buf, &offset); + if (r < 0) + return r; r = client_append_oro(client, &buf, &offset); if (r < 0) @@ -856,6 +885,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -911,6 +941,7 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -1319,6 +1350,7 @@ static int client_receive_message( case DHCP6_STATE_BOUND: case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: default: assert_not_reached(); } @@ -1326,10 +1358,37 @@ static int client_receive_message( return 0; } +static int client_send_release(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + + assert(client); + + if (!client->send_release) + return 0; + + if (sd_dhcp6_client_get_lease(client, &lease) < 0) + return 0; + + if (!lease->ia_na && !lease->ia_pd) + return 0; + + client_set_state(client, DHCP6_STATE_STOPPING); + return dhcp6_client_send_message(client); +} + int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + int r; + if (!client) return 0; + /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client + engine is about to release its UDP socket inconditionally. */ + r = client_send_release(client); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Failed to send DHCP6 release message, ignoring: %m"); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); client->receive_message = sd_event_source_unref(client->receive_message); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 3e1c52a306..00c11909c9 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -602,6 +602,62 @@ static const uint8_t msg_request[] = { 0x00, 0x00, }; +/* RFC 3315 section 18.1.6. The DHCP6 Release message must include: + - transaction id + - server identifier + - client identifier + - all released IA with addresses included + - elapsed time (required for all messages). + All other options aren't required. */ +static const uint8_t msg_release[] = { + /* Message type */ + DHCP6_MESSAGE_RELEASE, + /* 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 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 const uint8_t msg_reply[] = { /* Message type */ DHCP6_MESSAGE_REPLY, @@ -775,13 +831,24 @@ static void test_client_verify_solicit(const DHCP6Message *msg, size_t len) { assert_se(memcmp(msg, msg_solicit, len - sizeof(be16_t)) == 0); } +static void test_client_verify_release(const DHCP6Message *msg, size_t len) { + log_debug("/* %s */", __func__); + + assert_se(len == sizeof(msg_release)); + assert_se(msg->type == DHCP6_MESSAGE_RELEASE); + /* The transaction ID and elapsed time value are not deterministic. Skip them. */ + assert_se(memcmp(msg->options, msg_release + offsetof(DHCP6Message, options), + len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0); +} + static void test_client_verify_request(const DHCP6Message *msg, size_t len) { log_debug("/* %s */", __func__); 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); + assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options), + len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0); } static void test_lease_common(sd_dhcp6_client *client) { @@ -905,7 +972,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd 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)); + assert_se(IN_SET(test_client_sent_message_count, 3, 5)); test_lease_managed(client); @@ -916,7 +983,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); break; - case 4: + case 5: assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0); break; @@ -974,6 +1041,12 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, break; case 3: + test_client_verify_release(packet, len); + /* when stopping, dhcp6 client doesn't wait for release server reply */ + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; + + case 4: test_client_verify_solicit(packet, len); assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); break; @@ -1010,6 +1083,7 @@ TEST(dhcp6_client) { 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); + assert_se(sd_dhcp6_client_set_send_release(client, true) >= 0); dhcp6_client_set_test_mode(client, true); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER) >= 0); @@ -1028,7 +1102,7 @@ TEST(dhcp6_client) { assert_se(sd_event_loop(e) >= 0); - assert_se(test_client_sent_message_count == 4); + assert_se(test_client_sent_message_count == 5); assert_se(!sd_dhcp6_client_unref(client_ref)); test_fd[1] = safe_close(test_fd[1]); |