1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-11 20:58:27 +03:00

sd-dhcp-client: add ability to support bootp

BOOTP can be used to sign a static IP to clients. Instead of using the
four message exchange, and Option 53 (DHCP Message Type) there is only a
two message exchange.

Add the support for this exchange.
This commit is contained in:
Colin Foster 2024-10-29 20:50:58 -05:00 committed by Avram Dorfman
parent 96ec3911f3
commit 83cd170172
4 changed files with 109 additions and 36 deletions

View File

@ -14,19 +14,13 @@
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(
int bootp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0;
int r;
const uint8_t *chaddr) {
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
assert(chaddr || hlen == 0);
@ -51,6 +45,27 @@ int dhcp_message_init(
message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE);
return 0;
}
int dhcp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0;
int r;
r = bootp_message_init(message, op, xid, arp_type, hlen, chaddr);
if (r < 0)
return r;
r = dhcp_option_append(message, optlen, &offset, 0,
SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
if (r < 0)

View File

@ -6,6 +6,14 @@
#include "dhcp-protocol.h"
int bootp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr);
int dhcp_message_init(
DHCPMessage *message,
uint8_t op,

View File

@ -105,6 +105,7 @@ struct sd_dhcp_client {
int socket_priority;
bool socket_priority_set;
bool ipv6_acquired;
bool bootp;
};
static const uint8_t default_req_opts[] = {
@ -656,6 +657,19 @@ int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint64_t
return 0;
}
int sd_dhcp_client_set_bootp(sd_dhcp_client *client, int bootp) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->bootp = bootp;
/* For BOOTP mode, we don't want to send any request options by default. */
set_free(client->req_opts);
client->req_opts = NULL;
return 0;
}
static void client_set_state(sd_dhcp_client *client, DHCPState state) {
assert(client);
@ -792,10 +806,14 @@ static int client_message_init(
packet = malloc0(size);
if (!packet)
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
optlen, &optoffset);
if (client->bootp) {
optoffset = 0;
r = bootp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, client->arp_type,
client->hw_addr.length, client->hw_addr.bytes);
} else
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
optlen, &optoffset);
if (r < 0)
return r;
@ -825,14 +843,16 @@ static int client_message_init(
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
packet->dhcp.flags = htobe16(0x8000);
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id.size,
client->client_id.raw);
if (r < 0)
return r;
if (!client->bootp) {
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id.size,
client->client_id.raw);
if (r < 0)
return r;
}
/* RFC2131 section 3.5:
in its initial DHCPDISCOVER or DHCPREQUEST message, a
@ -847,7 +867,7 @@ static int client_message_init(
MAY contain the Parameter Request List option. */
/* NOTE: in case that there would be an option to do not send
* any PRL at all, the size should be checked before sending */
if (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
if (!set_isempty(client->req_opts) && type != DHCP_RELEASE && !client->bootp) {
_cleanup_free_ uint8_t *opts = NULL;
size_t n_opts, i = 0;
void *val;
@ -895,7 +915,7 @@ static int client_message_init(
*/
/* RFC7844 section 3:
SHOULD NOT contain any other option. */
if (!client->anonymize && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
if (!client->bootp && !client->anonymize && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
be16_t max_size = htobe16(MIN(client->mtu - DHCP_IP_UDP_SIZE, (uint32_t) UINT16_MAX));
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
@ -1037,7 +1057,7 @@ static int client_send_discover(sd_dhcp_client *client) {
*/
/* RFC7844 section 3:
SHOULD NOT contain any other option. */
if (!client->anonymize && client->last_addr != INADDR_ANY) {
if (!client->bootp && !client->anonymize && client->last_addr != INADDR_ANY) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
@ -1045,22 +1065,41 @@ static int client_send_discover(sd_dhcp_client *client) {
return r;
}
if (client->rapid_commit) {
if (!client->bootp && 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;
if (!client->bootp) {
r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
if (r < 0)
return r;
}
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
/* RFC1542 section 3.5:
* if the client has no information to communicate to the server,
* the octet immediately following the magic cookie SHOULD be set
* to the "End" tag (255) and the remaining octets of the 'vend'
* field SHOULD be set to zero.
*/
/* Use this RFC, along with the fact that some BOOTP servers require
* a 64-byte vend field, to suggest that we always zero and send 64
* bytes in the options field. The first four bites are the "magic"
* field, so this only needs to add 60 bytes.
*/
if (client->bootp)
if (optoffset < 60 && optlen >= 60) {
memset(&discover->dhcp.options[optoffset], 0, optlen - optoffset);
optoffset = 60;
}
/* We currently ignore:
The client SHOULD wait a random time between one and ten seconds to
desynchronize the use of DHCP at startup.
@ -1509,16 +1548,18 @@ static int client_parse_message(
}
r = dhcp_option_parse(message, len, dhcp_lease_parse_options, lease, &error_message);
if (r < 0)
if (r == -ENOMSG && client->bootp)
r = DHCP_ACK; /* BOOTP messages don't have a DHCP message type option */
else if (r < 0)
return log_dhcp_client_errno(client, r, "Failed to parse DHCP options, ignoring: %m");
switch (client->state) {
case DHCP_STATE_SELECTING:
if (r == DHCP_ACK) {
if (!client->rapid_commit)
if (!client->rapid_commit && !client->bootp)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received unexpected ACK, ignoring.");
if (!lease->rapid_commit)
if (!lease->rapid_commit && !client->bootp)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received rapid ACK without Rapid Commit option, ignoring.");
} else if (r == DHCP_OFFER) {
@ -1561,11 +1602,17 @@ static int client_parse_message(
lease->next_server = message->siaddr;
lease->address = message->yiaddr;
if (client->bootp)
lease->lifetime = USEC_INFINITY;
if (lease->server_address == 0 && !client->bootp)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received lease lacks server address, ignoring.");
if (lease->address == 0 ||
lease->server_address == 0 ||
lease->lifetime == 0)
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
"received lease lacks address, server address or lease lifetime, ignoring.");
"received lease lacks address or lease lifetime, ignoring.");
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0)
@ -1601,7 +1648,7 @@ static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage
dhcp_lease_unref_and_replace(client->lease, lease);
if (client->lease->rapid_commit) {
if (client->lease->rapid_commit || client->bootp) {
log_dhcp_client(client, "ACK");
return SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
}
@ -2007,8 +2054,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, s
if (r < 0)
return 0; /* invalid message, let's ignore it */
if (client->lease->rapid_commit)
/* got a successful rapid commit */
if (client->lease->rapid_commit || client->bootp)
/* got a successful rapid commit or bootp reply */
return client_enter_bound(client, r);
return client_enter_requesting(client);
@ -2225,7 +2272,7 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) {
size_t optoffset, optlen;
int r;
if (!sd_dhcp_client_is_running(client) || !client->lease)
if (!sd_dhcp_client_is_running(client) || !client->lease || client->bootp)
return 0; /* do nothing */
r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);

View File

@ -147,6 +147,9 @@ int sd_dhcp_client_set_socket_priority(
int sd_dhcp_client_set_fallback_lease_lifetime(
sd_dhcp_client *client,
uint64_t fallback_lease_lifetime);
int sd_dhcp_client_set_bootp(
sd_dhcp_client *client,
int bootp);
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);