mirror of
https://github.com/systemd/systemd
synced 2024-10-04 15:21:01 +00:00
network: implement RFC4039 DHCP Rapid Commit
This implements the DHCPv4 equivalent of the DHCPv6 Rapid Commit option, enabling a lease to be selected in an accelerated 2-message exchange instead of the typical 4-message exchange.
This commit is contained in:
parent
5516b0dd20
commit
808b65a087
5
NEWS
5
NEWS
|
@ -73,6 +73,11 @@ CHANGES WITH 255 in spe:
|
|||
already use 'prefixstable' addresses with wireless networks, the
|
||||
stable address chosen will be changed by the update.
|
||||
|
||||
* The DHCPv4 client gained a RapidCommit option, default true, which
|
||||
enables RFC4039 Rapid Commit behavior to obtain a lease in a
|
||||
simplified 2-message exchange instead of the typical 4-message
|
||||
exchange if also supported by the DHCP server.
|
||||
|
||||
Changes in systemd-analyze:
|
||||
|
||||
* "systemd-analyze plot" has gained tooltips on each unit name with
|
||||
|
|
|
@ -2219,6 +2219,21 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RapidCommit=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. The DHCPv4 client can obtain configuration parameters from a DHCPv4 server
|
||||
through a rapid two-message exchange (discover and ack). When the rapid commit option is set by
|
||||
both the DHCPv4 client and the DHCPv4 server, the two-message exchange is used. Otherwise, the
|
||||
four-message exchange (discover, offer, request, and ack) is used. The two-message exchange
|
||||
provides faster client configuration. See
|
||||
<ulink url="https://tools.ietf.org/html/rfc4039">RFC 4039</ulink> for details.
|
||||
Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Anonymize=</varname></term>
|
||||
<listitem>
|
||||
|
|
|
@ -50,6 +50,8 @@ struct sd_dhcp_lease {
|
|||
struct in_addr *router;
|
||||
size_t router_size;
|
||||
|
||||
bool rapid_commit;
|
||||
|
||||
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
||||
|
||||
struct sd_dhcp_route *static_routes;
|
||||
|
|
|
@ -75,7 +75,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||
client->xid = 2;
|
||||
client->state = DHCP_STATE_SELECTING;
|
||||
|
||||
(void) client_handle_offer(client, (DHCPMessage*) data, size);
|
||||
(void) client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size);
|
||||
|
||||
assert_se(sd_dhcp_client_stop(client) >= 0);
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ struct sd_dhcp_client {
|
|||
bool request_broadcast;
|
||||
Set *req_opts;
|
||||
bool anonymize;
|
||||
bool rapid_commit;
|
||||
be32_t last_addr;
|
||||
struct hw_addr_data hw_addr;
|
||||
struct hw_addr_data bcast_addr;
|
||||
|
@ -576,6 +577,13 @@ int sd_dhcp_client_set_iaid_duid_raw(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->rapid_commit = !client->anonymize && rapid_commit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_hostname(
|
||||
sd_dhcp_client *client,
|
||||
const char *hostname) {
|
||||
|
@ -1131,6 +1139,13 @@ static int client_send_discover(sd_dhcp_client *client) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->rapid_commit) {
|
||||
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
||||
SD_DHCP_OPTION_RAPID_COMMIT, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1359,6 +1374,9 @@ static int client_timeout_resend(
|
|||
if (r < 0 && client->attempt >= client->max_attempts)
|
||||
goto error;
|
||||
|
||||
if (client->rapid_commit)
|
||||
client->request_sent = time_now;
|
||||
|
||||
break;
|
||||
|
||||
case DHCP_STATE_INIT_REBOOT:
|
||||
|
@ -1583,12 +1601,22 @@ static int client_parse_message(
|
|||
|
||||
switch (client->state) {
|
||||
case DHCP_STATE_SELECTING:
|
||||
if (r != DHCP_OFFER)
|
||||
if (r == DHCP_ACK) {
|
||||
if (!client->rapid_commit)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
"received unexpected ACK, ignoring.");
|
||||
if (!lease->rapid_commit)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
"received rapid ACK without Rapid Commit option, ignoring.");
|
||||
} else if (r == DHCP_OFFER) {
|
||||
if (lease->rapid_commit)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
"received OFFER with Rapid Commit option, ignoring");
|
||||
if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
|
||||
lease->lifetime = client->fallback_lease_lifetime;
|
||||
} else
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
"received message was not an OFFER, ignoring.");
|
||||
|
||||
if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
|
||||
lease->lifetime = client->fallback_lease_lifetime;
|
||||
"received unexpected message, ignoring.");
|
||||
|
||||
break;
|
||||
|
||||
|
@ -1641,7 +1669,7 @@ static int client_parse_message(
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
|
||||
static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len) {
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
int r;
|
||||
|
||||
|
@ -1654,6 +1682,11 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *message, siz
|
|||
|
||||
dhcp_lease_unref_and_replace(client->lease, lease);
|
||||
|
||||
if (client->lease->rapid_commit) {
|
||||
log_dhcp_client(client, "ACK");
|
||||
return SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
|
||||
}
|
||||
|
||||
if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
|
||||
return -ENOMSG;
|
||||
|
||||
|
@ -1955,6 +1988,27 @@ static int client_enter_bound(sd_dhcp_client *client, int notify_event) {
|
|||
return client_enter_bound_now(client, notify_event);
|
||||
}
|
||||
|
||||
static int client_restart(sd_dhcp_client *client) {
|
||||
int r;
|
||||
assert(client);
|
||||
|
||||
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
|
||||
|
||||
r = client_initialize(client);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = client_start_delayed(client);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC));
|
||||
|
||||
client->start_delay = CLAMP(client->start_delay * 2,
|
||||
RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
|
||||
DHCP_CLIENT_DONT_DESTROY(client);
|
||||
int r;
|
||||
|
@ -1966,13 +2020,26 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
switch (client->state) {
|
||||
case DHCP_STATE_SELECTING:
|
||||
|
||||
r = client_handle_offer(client, message, len);
|
||||
r = client_handle_offer_or_rapid_ack(client, message, len);
|
||||
if (ERRNO_IS_NEG_RESOURCE(r))
|
||||
goto error;
|
||||
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
/* got a rapid NAK, let's restart the client */
|
||||
r = client_restart(client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return 0; /* invalid message, let's ignore it */
|
||||
|
||||
r = client_enter_requesting(client);
|
||||
if (client->lease->rapid_commit)
|
||||
/* got a succssful rapid commit */
|
||||
r = client_enter_bound(client, r);
|
||||
else
|
||||
r = client_enter_requesting(client);
|
||||
break;
|
||||
|
||||
case DHCP_STATE_REBOOTING:
|
||||
|
@ -1985,20 +2052,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
goto error;
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
/* got a NAK, let's restart the client */
|
||||
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
|
||||
|
||||
r = client_initialize(client);
|
||||
r = client_restart(client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = client_start_delayed(client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC));
|
||||
|
||||
client->start_delay = CLAMP(client->start_delay * 2,
|
||||
RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
|
|
|
@ -735,6 +735,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
log_debug_errno(r, "Failed to parse router addresses, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_RAPID_COMMIT:
|
||||
if (len > 0)
|
||||
log_debug("Invalid DHCP Rapid Commit option, ignorning.");
|
||||
lease->rapid_commit = true;
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
|
||||
r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
|
||||
if (r < 0)
|
||||
|
|
|
@ -1485,6 +1485,10 @@ static int dhcp4_configure(Link *link) {
|
|||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach device: %m");
|
||||
|
||||
r = sd_dhcp_client_set_rapid_commit(link->dhcp_client, link->network->dhcp_use_rapid_commit);
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set rapid commit: %m");
|
||||
|
||||
r = sd_dhcp_client_set_mac(link->dhcp_client,
|
||||
link->hw_addr.bytes,
|
||||
link->bcast_addr.length > 0 ? link->bcast_addr.bytes : NULL,
|
||||
|
|
|
@ -261,6 +261,7 @@ DHCPv4.Use6RD, config_parse_bool,
|
|||
DHCPv4.IPv6OnlyMode, config_parse_tristate, 0, offsetof(Network, dhcp_ipv6_only_mode)
|
||||
DHCPv4.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_netlabel)
|
||||
DHCPv4.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp_nft_set_context)
|
||||
DHCPv4.RapidCommit config_parse_bool, 0, offsetof(Network, dhcp_use_rapid_commit)
|
||||
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
|
||||
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
|
||||
DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0
|
||||
|
|
|
@ -396,6 +396,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
|||
.dhcp_send_hostname = true,
|
||||
.dhcp_send_release = true,
|
||||
.dhcp_route_metric = DHCP_ROUTE_METRIC,
|
||||
.dhcp_use_rapid_commit = true,
|
||||
.dhcp_client_identifier = _DHCP_CLIENT_ID_INVALID,
|
||||
.dhcp_route_table = RT_TABLE_MAIN,
|
||||
.dhcp_ip_service_type = -1,
|
||||
|
|
|
@ -140,6 +140,7 @@ struct Network {
|
|||
bool dhcp_send_hostname;
|
||||
int dhcp_broadcast;
|
||||
int dhcp_ipv6_only_mode;
|
||||
bool dhcp_use_rapid_commit;
|
||||
bool dhcp_use_dns;
|
||||
bool dhcp_use_dns_set;
|
||||
bool dhcp_routes_to_dns;
|
||||
|
|
|
@ -104,6 +104,9 @@ __extension__ int sd_dhcp_client_set_iaid_duid_raw(
|
|||
uint16_t duid_type,
|
||||
const uint8_t *duid,
|
||||
size_t duid_len);
|
||||
__extension__ int sd_dhcp_client_set_rapid_commit(
|
||||
sd_dhcp_client *client,
|
||||
bool rapid_commit);
|
||||
int sd_dhcp_client_get_client_id(
|
||||
sd_dhcp_client *client,
|
||||
uint8_t *ret_type,
|
||||
|
|
Loading…
Reference in a new issue