summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchris <str77@pm.me>2023-01-07 21:11:28 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-01-17 21:26:18 +0900
commitb895aa5ff5c56e88756dfa04efc76e30cb8b0841 (patch)
tree85888e5daee0730cf00ab1eb1a3c6f3363ad5d8d
parent1200777b21936bf5647a90504e0ea27e3ec3e42b (diff)
downloadsystemd-b895aa5ff5c56e88756dfa04efc76e30cb8b0841.tar.gz
send dhcpv6 release when stopping
-rw-r--r--NEWS5
-rw-r--r--man/systemd.network.xml1
-rw-r--r--src/libsystemd-network/dhcp6-internal.h1
-rw-r--r--src/libsystemd-network/dhcp6-protocol.c1
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h1
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c83
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c82
-rw-r--r--src/network/networkd-dhcp6.c6
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/systemd/sd-dhcp6-client.h1
12 files changed, 168 insertions, 16 deletions
diff --git a/NEWS b/NEWS
index fe5741610e..eb7c24c830 100644
--- a/NEWS
+++ b/NEWS
@@ -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,