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 | |
parent | 1200777b21936bf5647a90504e0ea27e3ec3e42b (diff) | |
download | systemd-b895aa5ff5c56e88756dfa04efc76e30cb8b0841.tar.gz |
send dhcpv6 release when stopping
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | man/systemd.network.xml | 1 | ||||
-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 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 6 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-network.c | 1 | ||||
-rw-r--r-- | src/network/networkd-network.h | 1 | ||||
-rw-r--r-- | src/systemd/sd-dhcp6-client.h | 1 |
12 files changed, 168 insertions, 16 deletions
@@ -131,6 +131,11 @@ CHANGES WITH 253 in spe: * systemd-networkd-wait-online now supports alternative interface names. + * The [DHCPv6] section in .network file gained new SendRelease= + setting which enables the DHCPv6 client to send release when + it stops. This is the analog of the [DHCPv4] SendRelease= setting. + It is enabled by default. + Changes in systemd-dissect: * systemd-dissect gained a new option --list, to print the paths fo the diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 9c0b9929dc..01d8897638 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2292,6 +2292,7 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting> <term><varname>UseHostname=</varname></term> <term><varname>UseDomains=</varname></term> <term><varname>NetLabel=</varname></term> + <term><varname>SendRelease=</varname></term> <listitem> <para>As in the [DHCPv4] section.</para> </listitem> 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]); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c44c37f3aa..43be988377 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -707,6 +707,12 @@ static int dhcp6_configure(Link *link) { "DHCPv6 CLIENT: Failed to %s rapid commit: %m", enable_disable(link->network->dhcp6_use_rapid_commit)); + r = sd_dhcp6_client_set_send_release(client, link->network->dhcp6_send_release); + if (r < 0) + return log_link_debug_errno(link, r, + "DHCPv6 CLIENT: Failed to %s sending release message on stop: %m", + enable_disable(link->network->dhcp6_send_release)); + link->dhcp6_client = TAKE_PTR(client); return 0; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 59d352b4d1..716904cc34 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -270,6 +270,7 @@ DHCPv6.DUIDType, config_parse_duid_type, DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid) DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit) DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel) +DHCPv6.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp6_send_release) IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway) IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix) IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index d881889316..7c5d691afa 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -416,6 +416,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_use_rapid_commit = true, .dhcp6_duid.type = _DUID_TYPE_INVALID, .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID, + .dhcp6_send_release = true, .dhcp_pd = -1, .dhcp_pd_announce = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 986e067902..7685c98f65 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -186,6 +186,7 @@ struct Network { OrderedHashmap *dhcp6_client_send_vendor_options; Set *dhcp6_request_options; char *dhcp6_netlabel; + bool dhcp6_send_release; /* DHCP Server Support */ bool dhcp_server; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 497b2afb2f..a9fa78569d 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -264,6 +264,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable); +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, |