1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-22 22:03:43 +03:00

send dhcpv6 release when stopping

This commit is contained in:
chris 2023-01-07 21:11:28 +01:00 committed by Yu Watanabe
parent 1200777b21
commit b895aa5ff5
12 changed files with 168 additions and 16 deletions

5
NEWS
View File

@ -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

View File

@ -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>

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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]);

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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;

View File

@ -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,