sd-dhcp-lease: introduce dhcp_client_parse_message()
It parses a received DHCP message and create corresponding sd_dhcp_lease object for the message. Internally, it uses sd_dhcp_message object. Note, the new parser is not used yet. The currently used one will be replaced in later commit.
This commit is contained in:
@@ -6,10 +6,11 @@
|
||||
***/
|
||||
|
||||
#include "sd-dhcp-lease.h"
|
||||
#include "sd-forward.h"
|
||||
|
||||
#include "dhcp-client-id-internal.h"
|
||||
#include "dhcp-message.h"
|
||||
#include "dhcp-option.h"
|
||||
#include "sd-forward.h"
|
||||
#include "list.h"
|
||||
|
||||
struct sd_dhcp_raw_option {
|
||||
@@ -23,6 +24,8 @@ struct sd_dhcp_raw_option {
|
||||
struct sd_dhcp_lease {
|
||||
unsigned n_ref;
|
||||
|
||||
sd_dhcp_message *message;
|
||||
|
||||
/* each 0 if unset */
|
||||
usec_t t1;
|
||||
usec_t t2;
|
||||
@@ -87,5 +90,7 @@ void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *time
|
||||
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
|
||||
int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id);
|
||||
|
||||
int dhcp_client_parse_message(sd_dhcp_client *client, const struct iovec *iov, sd_dhcp_lease **ret);
|
||||
|
||||
#define dhcp_lease_unref_and_replace(a, b) \
|
||||
free_and_replace_full(a, b, sd_dhcp_lease_unref)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "sd-dhcp-lease.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-client-internal.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-option.h"
|
||||
#include "dhcp-route.h"
|
||||
@@ -22,9 +23,11 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "ip-util.h"
|
||||
#include "network-common.h"
|
||||
#include "network-internal.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
#include "sort-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -419,6 +422,8 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
|
||||
|
||||
assert(lease);
|
||||
|
||||
sd_dhcp_message_unref(lease->message);
|
||||
|
||||
while ((option = LIST_POP(options, lease->private_options))) {
|
||||
free(option->data);
|
||||
free(option);
|
||||
@@ -1803,3 +1808,434 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **ret) {
|
||||
*ret = lease->timezone;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp_lease_new_from_message(sd_dhcp_client *client, sd_dhcp_message *message, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
r = dhcp_lease_new(&lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* acquired address: mandatory */
|
||||
if (message->header.yiaddr == INADDR_ANY)
|
||||
return -EBADMSG;
|
||||
lease->address = message->header.yiaddr;
|
||||
|
||||
/* subnet mask: mandatory */
|
||||
if (dhcp_message_get_option_be32(message, SD_DHCP_OPTION_SUBNET_MASK, &lease->subnet_mask) < 0) {
|
||||
/* fall back to the default subnet masks based on address class */
|
||||
struct in_addr mask;
|
||||
r = in4_addr_default_subnet_mask(
|
||||
&(struct in_addr) {
|
||||
.s_addr = message->header.yiaddr,
|
||||
},
|
||||
&mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lease->subnet_mask = mask.s_addr;
|
||||
}
|
||||
|
||||
/* DHCP server address: mandatory */
|
||||
r = dhcp_message_get_option_be32(message, SD_DHCP_OPTION_SERVER_IDENTIFIER, &lease->server_address);
|
||||
if (r < 0) {
|
||||
if (!client->bootp)
|
||||
return log_dhcp_client_errno(client, r, "Failed to read %s option: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_SERVER_IDENTIFIER));
|
||||
|
||||
/* BOOTP typically does not use Server Identifier option, but uses the siaddr field. */
|
||||
lease->server_address = message->header.siaddr;
|
||||
}
|
||||
|
||||
/* lifetime: mandatory */
|
||||
if (client->bootp)
|
||||
lease->lifetime = USEC_INFINITY; /* BOOTP does not support lifetime. */
|
||||
else {
|
||||
r = dhcp_message_get_option_sec(message, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, /* max_as_infinity= */ true, &lease->lifetime);
|
||||
if (r < 0 || lease->lifetime == 0) {
|
||||
if (client->fallback_lease_lifetime == 0) {
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to read %s option: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME));
|
||||
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"The %s option set to 0 second.",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME));
|
||||
}
|
||||
|
||||
lease->lifetime = client->fallback_lease_lifetime;
|
||||
}
|
||||
|
||||
/* There is nothing mentioned about the valid range of the lifetime in RFC, but if it is too
|
||||
* short, then the network connection easily become unstable. Let's bump to 30 seconds in
|
||||
* that case.
|
||||
* TODO: filter short lifetime in selecting state. */
|
||||
if (lease->lifetime <= 30 * USEC_PER_SEC) {
|
||||
log_dhcp_client(client, "The %s option is too short (%s), bumping lease lifetime to 30 seconds.",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME),
|
||||
FORMAT_TIMESPAN(lease->lifetime, USEC_PER_SEC));
|
||||
lease->lifetime = 30 * USEC_PER_SEC;
|
||||
}
|
||||
|
||||
if (lease->lifetime != USEC_INFINITY) {
|
||||
/* T2 */
|
||||
r = dhcp_message_get_option_sec(message, SD_DHCP_OPTION_REBINDING_TIME, /* max_as_infinity= */ true, &lease->t2);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to read %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_REBINDING_TIME));
|
||||
|
||||
/* verify that 0 < t2 < lifetime */
|
||||
if (lease->t2 <= 0 || lease->t2 >= lease->lifetime)
|
||||
/* RFC2131 section 4.4.5: T2 defaults to (0.875 * duration_of_lease). */
|
||||
lease->t2 = lease->lifetime * 7 / 8;
|
||||
|
||||
/* T1 */
|
||||
r = dhcp_message_get_option_sec(message, SD_DHCP_OPTION_RENEWAL_TIME, /* max_as_infinity= */ true, &lease->t1);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to read %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_RENEWAL_TIME));
|
||||
|
||||
/* verify that 0 < t1 < t2 */
|
||||
if (lease->t1 <= 0 || lease->t1 >= lease->t2)
|
||||
/* RFC2131 section 4.4.5: T1 defaults to (0.5 * duration_of_lease). */
|
||||
lease->t1 = lease->lifetime / 2;
|
||||
|
||||
/* For the case when T2 is too small compared with lifetime. */
|
||||
if (lease->t1 >= lease->t2)
|
||||
/* RFC2131 section 4.4.5: T2 defaults to (0.875 * duration_of_lease). */
|
||||
lease->t2 = lease->lifetime * 7 / 8;
|
||||
|
||||
assert(lease->t1 > 0);
|
||||
assert(lease->t1 < lease->t2);
|
||||
assert(lease->t2 < lease->lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
r = dhcp_message_get_option_be32(message, SD_DHCP_OPTION_BROADCAST, &lease->broadcast);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_BROADCAST));
|
||||
|
||||
r = dhcp_message_get_option_addresses(
|
||||
message,
|
||||
SD_DHCP_OPTION_ROUTER,
|
||||
&lease->router_size,
|
||||
&lease->router);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_ROUTER));
|
||||
|
||||
r = dhcp_message_get_option_addresses(
|
||||
message,
|
||||
SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
|
||||
&lease->servers[SD_DHCP_LEASE_DNS].size,
|
||||
&lease->servers[SD_DHCP_LEASE_DNS].addr);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_DOMAIN_NAME_SERVER));
|
||||
|
||||
r = dhcp_message_get_option_addresses(
|
||||
message,
|
||||
SD_DHCP_OPTION_NTP_SERVER,
|
||||
&lease->servers[SD_DHCP_LEASE_NTP].size,
|
||||
&lease->servers[SD_DHCP_LEASE_NTP].addr);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_NTP_SERVER));
|
||||
|
||||
r = dhcp_message_get_option_addresses(
|
||||
message,
|
||||
SD_DHCP_OPTION_SIP_SERVER,
|
||||
&lease->servers[SD_DHCP_LEASE_SIP].size,
|
||||
&lease->servers[SD_DHCP_LEASE_SIP].addr);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_SIP_SERVER));
|
||||
|
||||
r = dhcp_message_get_option_routes(
|
||||
message,
|
||||
SD_DHCP_OPTION_STATIC_ROUTE,
|
||||
&lease->n_static_routes,
|
||||
&lease->static_routes);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_STATIC_ROUTE));
|
||||
|
||||
r = dhcp_message_get_option_routes(
|
||||
message,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
|
||||
&lease->n_classless_routes,
|
||||
&lease->classless_routes);
|
||||
if (r < 0) {
|
||||
if (r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE));
|
||||
|
||||
r = dhcp_message_get_option_routes(
|
||||
message,
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE,
|
||||
&lease->n_classless_routes,
|
||||
&lease->classless_routes);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE));
|
||||
}
|
||||
|
||||
r = dhcp_message_get_option_6rd(
|
||||
message,
|
||||
&lease->sixrd_ipv4masklen,
|
||||
&lease->sixrd_prefixlen,
|
||||
&lease->sixrd_prefix,
|
||||
&lease->sixrd_n_br_addresses,
|
||||
&lease->sixrd_br_addresses);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_6RD));
|
||||
|
||||
r = dhcp_message_get_option_dns_name(message, SD_DHCP_OPTION_DOMAIN_NAME, &lease->domainname);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_DOMAIN_NAME));
|
||||
|
||||
r = dhcp_message_get_option_hostname(message, &lease->hostname);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s and/or %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_FQDN),
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_HOST_NAME));
|
||||
|
||||
r = dhcp_message_get_option_domains(message, SD_DHCP_OPTION_DOMAIN_SEARCH, &lease->search_domains);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_DOMAIN_SEARCH));
|
||||
|
||||
r = dhcp_message_get_option_dnr(message, &lease->n_dnr, &lease->dnr);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_V4_DNR));
|
||||
|
||||
_cleanup_free_ char *captive_portal = NULL;
|
||||
r = dhcp_message_get_option_string(message, SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL, &captive_portal);
|
||||
if (r >= 0) {
|
||||
if (!in_charset(captive_portal, URI_VALID))
|
||||
log_dhcp_client(client, "Received invalid %s, ignoring: %s",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL),
|
||||
captive_portal);
|
||||
else
|
||||
lease->captive_portal = TAKE_PTR(captive_portal);
|
||||
} else if (r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL));
|
||||
|
||||
_cleanup_free_ char *tz = NULL;
|
||||
r = dhcp_message_get_option_string(message, SD_DHCP_OPTION_TZDB_TIMEZONE, &tz);
|
||||
if (r >= 0) {
|
||||
if (!timezone_is_valid(tz, LOG_DEBUG))
|
||||
log_dhcp_client(client, "Received invalid %s, ignoring: %s",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_TZDB_TIMEZONE), tz);
|
||||
else
|
||||
lease->timezone = TAKE_PTR(tz);
|
||||
} else if (r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_TZDB_TIMEZONE));
|
||||
|
||||
uint16_t mtu;
|
||||
r = dhcp_message_get_option_u16(message, SD_DHCP_OPTION_MTU_INTERFACE, &mtu);
|
||||
if (r >= 0) {
|
||||
/* RFC 2132 section 5.1 permits MTU values down to 68 bytes, which corresponds to the minimum
|
||||
* IPv4 datagram size defined in RFC 791.
|
||||
*
|
||||
* Such a small MTU is not generally usable for normal IP communication. RFC 791 and RFC 1122
|
||||
* require hosts to be able to reassemble datagrams of at least 576 bytes, which is treated
|
||||
* as the minimum safe size for IPv4 interoperability.
|
||||
*
|
||||
* Ignore MTU values smaller than 576 bytes. */
|
||||
if (mtu < IPV4_MIN_REASSEMBLY_SIZE)
|
||||
log_dhcp_client(client, "Received too small %s, ignoring: %u",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_MTU_INTERFACE), mtu);
|
||||
else
|
||||
lease->mtu = mtu;
|
||||
} else if (r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_MTU_INTERFACE));
|
||||
|
||||
/* RFC 8925 section 3.2
|
||||
* If the client did not include the IPv6-Only Preferred option code in the Parameter Request List in
|
||||
* the DHCPDISCOVER or DHCPREQUEST message, it MUST ignore the IPv6-Only Preferred option in any
|
||||
* messages received from the server. */
|
||||
if (!client->anonymize &&
|
||||
set_contains(client->req_opts, UINT_TO_PTR(SD_DHCP_OPTION_IPV6_ONLY_PREFERRED))) {
|
||||
usec_t t;
|
||||
r = dhcp_message_get_option_sec(
|
||||
message,
|
||||
SD_DHCP_OPTION_IPV6_ONLY_PREFERRED,
|
||||
/* max_as_infinity= */ false,
|
||||
&t);
|
||||
if (r >= 0) {
|
||||
/* RFC 8925 section 3.4
|
||||
* MIN_V6ONLY_WAIT: The lower boundary for V6ONLY_WAIT. */
|
||||
if (t < MIN_V6ONLY_WAIT_USEC && !network_test_mode_enabled())
|
||||
lease->ipv6_only_preferred_usec = MIN_V6ONLY_WAIT_USEC;
|
||||
else
|
||||
lease->ipv6_only_preferred_usec = t;
|
||||
} else if (r != -ENODATA)
|
||||
log_dhcp_client_errno(client, r, "Failed to parse %s option, ignoring: %m",
|
||||
dhcp_option_code_to_string(SD_DHCP_OPTION_IPV6_ONLY_PREFERRED));
|
||||
}
|
||||
|
||||
lease->message = sd_dhcp_message_ref(message);
|
||||
*ret = TAKE_PTR(lease);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_parse_bootreply(sd_dhcp_client *client, sd_dhcp_message *message, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
if (client->state != DHCP_STATE_SELECTING)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received unexpected BOOTREPLY.");
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
r = dhcp_lease_new_from_message(client, message, &lease);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to create BOOTP lease: %m");
|
||||
|
||||
log_dhcp_client(client, "Received BOOTREPLY from %s", IN4_ADDR_TO_STRING(&(struct in_addr) { .s_addr = lease->server_address }));
|
||||
|
||||
*ret = TAKE_PTR(lease);
|
||||
return DHCP_ACK;
|
||||
}
|
||||
|
||||
static int client_parse_ack(sd_dhcp_client *client, sd_dhcp_message *message, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
switch (client->state) {
|
||||
case DHCP_STATE_SELECTING:
|
||||
if (!client->rapid_commit)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received unexpected DHCPACK.");
|
||||
|
||||
r = dhcp_message_get_option_flag(message, SD_DHCP_OPTION_RAPID_COMMIT);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to get Rapid Commit option: %m");
|
||||
|
||||
break;
|
||||
case DHCP_STATE_REBOOTING:
|
||||
case DHCP_STATE_REQUESTING:
|
||||
case DHCP_STATE_RENEWING:
|
||||
case DHCP_STATE_REBINDING:
|
||||
break;
|
||||
default:
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received unexpected DHCPACK.");
|
||||
}
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
r = dhcp_lease_new_from_message(client, message, &lease);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to create DHCP lease: %m");
|
||||
|
||||
log_dhcp_client(client, "Received DHCPACK from %s", IN4_ADDR_TO_STRING(&(struct in_addr) { .s_addr = lease->server_address }));
|
||||
|
||||
*ret = TAKE_PTR(lease);
|
||||
return DHCP_ACK;
|
||||
}
|
||||
|
||||
static int client_parse_offer(sd_dhcp_client *client, sd_dhcp_message *message, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
if (client->state != DHCP_STATE_SELECTING)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received unexpected DHCPOFFER.");
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
r = dhcp_lease_new_from_message(client, message, &lease);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to create DHCP lease: %m");
|
||||
|
||||
log_dhcp_client(client, "Received DHCPOFFER from %s", IN4_ADDR_TO_STRING(&(struct in_addr) { .s_addr = lease->server_address }));
|
||||
|
||||
*ret = TAKE_PTR(lease);
|
||||
return DHCP_OFFER;
|
||||
}
|
||||
|
||||
static int client_parse_nak(sd_dhcp_client *client, sd_dhcp_message *message, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
/* DHCPNAK is a valid reply when we sent DHCPREQUEST. When we receive it after sending
|
||||
* DHCPDISCOVER (or even we sent nothing), we should ignore the message. */
|
||||
if (!IN_SET(client->state, DHCP_STATE_REBOOTING, DHCP_STATE_REQUESTING, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received unexpected DHCPNAK.");
|
||||
|
||||
/* Always ignore DHCPNAK without Server Identifier option. */
|
||||
struct in_addr a;
|
||||
r = dhcp_message_get_option_address(message, SD_DHCP_OPTION_SERVER_IDENTIFIER, &a);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to read Server Identifier option in DHCPNAK: %m");
|
||||
|
||||
if (client->lease && client->lease->server_address != a.s_addr)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received DHCPNAK from unexpected server (%s).",
|
||||
IN4_ADDR_TO_STRING(&a));
|
||||
|
||||
_cleanup_free_ char *e = NULL;
|
||||
(void) dhcp_message_get_option_string(message, SD_DHCP_OPTION_ERROR_MESSAGE, &e);
|
||||
log_dhcp_client(client, "Received DHCPNAK: %s", strna(e));
|
||||
|
||||
*ret = NULL;
|
||||
return DHCP_NAK;
|
||||
}
|
||||
|
||||
int dhcp_client_parse_message(sd_dhcp_client *client, const struct iovec *iov, sd_dhcp_lease **ret) {
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(iov);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *message = NULL;
|
||||
r = dhcp_message_parse(
|
||||
iov,
|
||||
BOOTREPLY,
|
||||
&client->xid,
|
||||
client->arp_type,
|
||||
&client->hw_addr,
|
||||
&message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->bootp)
|
||||
return client_parse_bootreply(client, message, ret);
|
||||
|
||||
uint8_t type;
|
||||
r = dhcp_message_get_option_u8(message, SD_DHCP_OPTION_MESSAGE_TYPE, &type);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to read Message Type option: %m");
|
||||
|
||||
switch (type) {
|
||||
case DHCP_OFFER:
|
||||
return client_parse_offer(client, message, ret);
|
||||
case DHCP_ACK:
|
||||
return client_parse_ack(client, message, ret);
|
||||
case DHCP_NAK:
|
||||
return client_parse_nak(client, message, ret);
|
||||
default:
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EBADMSG), "Received message with unexpected type (%u).", type);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user