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. This adds the support for this exchange. Co-authored-by: Avram Dorfman <dorfman@est.org> Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
This commit is contained in:
committed by
Yu Watanabe
co-authored by
Avram Dorfman
Yu Watanabe
parent
98a9b0f92a
commit
4ad29bad7b
@@ -13,24 +13,17 @@
|
||||
|
||||
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
|
||||
|
||||
int dhcp_message_init(
|
||||
int bootp_message_init(
|
||||
DHCPMessage *message,
|
||||
uint8_t op,
|
||||
uint32_t xid,
|
||||
uint16_t arp_type,
|
||||
uint8_t hlen,
|
||||
const uint8_t *chaddr,
|
||||
uint8_t type,
|
||||
size_t optlen,
|
||||
size_t *ret_optoffset) {
|
||||
|
||||
size_t offset = 0;
|
||||
int r;
|
||||
const uint8_t *chaddr) {
|
||||
|
||||
assert(message);
|
||||
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
|
||||
assert(chaddr || hlen == 0);
|
||||
assert(ret_optoffset);
|
||||
|
||||
message->op = op;
|
||||
message->htype = arp_type;
|
||||
@@ -52,6 +45,31 @@ 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,
|
||||
uint16_t arp_type,
|
||||
uint8_t hlen,
|
||||
const uint8_t *chaddr,
|
||||
uint8_t type,
|
||||
size_t optlen,
|
||||
size_t *ret_optoffset) {
|
||||
|
||||
size_t offset = 0;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(chaddr || hlen == 0);
|
||||
assert(ret_optoffset);
|
||||
|
||||
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)
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
#include "dhcp-protocol.h"
|
||||
#include "forward.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,
|
||||
|
||||
@@ -102,6 +102,7 @@ struct sd_dhcp_client {
|
||||
int socket_priority;
|
||||
bool socket_priority_set;
|
||||
bool ipv6_acquired;
|
||||
bool bootp;
|
||||
};
|
||||
|
||||
static const uint8_t default_req_opts[] = {
|
||||
@@ -653,6 +654,15 @@ 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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void client_set_state(sd_dhcp_client *client, DHCPState state) {
|
||||
assert(client);
|
||||
|
||||
@@ -787,10 +797,18 @@ static int client_message_init(
|
||||
packet = malloc0(size);
|
||||
if (!packet)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid,
|
||||
client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
|
||||
type, optlen, &optoffset);
|
||||
if (client->bootp) {
|
||||
/* BOOTP supports options, but only DHCP_OPTION_END is used. The rest of the 64-byte buffer
|
||||
* is set to zero, per RFC1542. Allow for this by initialaizing optoffset to 0. */
|
||||
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, client->arp_type,
|
||||
client->hw_addr.length, client->hw_addr.bytes,
|
||||
type, optlen, &optoffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -820,6 +838,13 @@ static int client_message_init(
|
||||
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
|
||||
packet->dhcp.flags = htobe16(0x8000);
|
||||
|
||||
if (client->bootp) {
|
||||
*ret_optlen = optlen;
|
||||
*ret_optoffset = optoffset;
|
||||
*ret_packet = TAKE_PTR(packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@@ -1011,7 +1036,7 @@ static int client_append_common_discover_request_options(sd_dhcp_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_send_discover(sd_dhcp_client *client) {
|
||||
static int client_send_dhcp_discover(sd_dhcp_client *client) {
|
||||
_cleanup_free_ DHCPPacket *discover = NULL;
|
||||
size_t optoffset, optlen;
|
||||
int r;
|
||||
@@ -1064,12 +1089,50 @@ static int client_send_discover(sd_dhcp_client *client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_send_bootp_discover(sd_dhcp_client *client) {
|
||||
_cleanup_free_ DHCPPacket *discover = NULL;
|
||||
size_t optoffset, optlen;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
|
||||
|
||||
r = client_message_init(client, DHCP_DISCOVER, &discover, &optlen, &optoffset);
|
||||
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 (optoffset < 60 && optlen >= 60) {
|
||||
memzero(&discover->dhcp.options[optoffset], optlen - optoffset);
|
||||
optoffset = 60;
|
||||
}
|
||||
|
||||
r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_dhcp_client(client, "DISCOVER");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_send_request(sd_dhcp_client *client) {
|
||||
_cleanup_free_ DHCPPacket *request = NULL;
|
||||
size_t optoffset, optlen;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(!client->bootp);
|
||||
|
||||
r = client_message_init(client, DHCP_REQUEST, &request, &optlen, &optoffset);
|
||||
if (r < 0)
|
||||
@@ -1261,7 +1324,10 @@ static int client_timeout_resend(
|
||||
|
||||
switch (client->state) {
|
||||
case DHCP_STATE_INIT:
|
||||
r = client_send_discover(client);
|
||||
if (client->bootp)
|
||||
r = client_send_bootp_discover(client);
|
||||
else
|
||||
r = client_send_dhcp_discover(client);
|
||||
if (r >= 0) {
|
||||
client_set_state(client, DHCP_STATE_SELECTING);
|
||||
client->discover_attempt = 0;
|
||||
@@ -1270,7 +1336,10 @@ static int client_timeout_resend(
|
||||
break;
|
||||
|
||||
case DHCP_STATE_SELECTING:
|
||||
r = client_send_discover(client);
|
||||
if (client->bootp)
|
||||
r = client_send_bootp_discover(client);
|
||||
else
|
||||
r = client_send_dhcp_discover(client);
|
||||
if (r < 0 && client->discover_attempt >= client->max_discover_attempts)
|
||||
goto error;
|
||||
break;
|
||||
@@ -1472,29 +1541,18 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
|
||||
return client_initialize_time_events(client);
|
||||
}
|
||||
|
||||
static int client_parse_message(
|
||||
static int dhcp_option_parse_and_verify(
|
||||
sd_dhcp_client *client,
|
||||
DHCPMessage *message,
|
||||
size_t len,
|
||||
sd_dhcp_lease **ret) {
|
||||
sd_dhcp_lease *lease) {
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
_cleanup_free_ char *error_message = NULL;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
r = dhcp_lease_new(&lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_dhcp_client_id_is_set(&client->client_id)) {
|
||||
r = dhcp_lease_set_client_id(lease, &client->client_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
assert(lease);
|
||||
|
||||
r = dhcp_option_parse(message, len, dhcp_lease_parse_options, lease, &error_message);
|
||||
if (r < 0)
|
||||
@@ -1555,6 +1613,77 @@ static int client_parse_message(
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
"received lease lacks address, server address or lease lifetime, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bootp_option_parse_and_verify(
|
||||
sd_dhcp_client *client,
|
||||
DHCPMessage *message,
|
||||
size_t len,
|
||||
sd_dhcp_lease *lease) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(lease);
|
||||
|
||||
r = dhcp_option_parse(message, len, dhcp_lease_parse_options, lease, /* ret_error_message = */ NULL);
|
||||
if (r == -ENOMSG)
|
||||
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 BOOTP options, ignoring: %m");
|
||||
else
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), "Received unexpected message, ignoring.");
|
||||
|
||||
log_dhcp_client(client, "BOOTP identified, using infinite lease. BOOTP siaddr=(%#x), DHCP Server Identifier=(%#x)",
|
||||
message->siaddr, lease->server_address);
|
||||
|
||||
lease->lifetime = USEC_INFINITY;
|
||||
lease->address = message->yiaddr;
|
||||
if (lease->server_address == 0)
|
||||
lease->server_address = message->siaddr;
|
||||
|
||||
/* BOOTP protocol does not have any OFFER and REQUEST process. Hence, it is mostly equivalent to
|
||||
* Rapid Commit process in DHCP. */
|
||||
lease->rapid_commit = true;
|
||||
|
||||
if (lease->address == 0)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), "received lease lacks address, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_parse_message(
|
||||
sd_dhcp_client *client,
|
||||
DHCPMessage *message,
|
||||
size_t len,
|
||||
sd_dhcp_lease **ret) {
|
||||
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(ret);
|
||||
|
||||
r = dhcp_lease_new(&lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_dhcp_client_id_is_set(&client->client_id)) {
|
||||
r = dhcp_lease_set_client_id(lease, &client->client_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (client->bootp)
|
||||
r = bootp_option_parse_and_verify(client, message, len, lease);
|
||||
else
|
||||
r = dhcp_option_parse_and_verify(client, message, len, lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp_lease_set_default_subnet_mask(lease);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG),
|
||||
@@ -1842,13 +1971,18 @@ static int client_enter_bound_now(sd_dhcp_client *client, int notify_event) {
|
||||
if (r < 0)
|
||||
log_dhcp_client_errno(client, r, "could not set lease timeouts: %m");
|
||||
|
||||
r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "could not bind UDP socket: %m");
|
||||
if (client->bootp) {
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
client->fd = safe_close(client->fd);
|
||||
} else {
|
||||
r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "could not bind UDP socket: %m");
|
||||
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
close_and_replace(client->fd, r);
|
||||
client_initialize_io_events(client, client_receive_message_udp);
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
close_and_replace(client->fd, r);
|
||||
client_initialize_io_events(client, client_receive_message_udp);
|
||||
}
|
||||
|
||||
client_notify(client, notify_event);
|
||||
|
||||
@@ -2156,7 +2290,7 @@ static int client_receive_message_raw(
|
||||
}
|
||||
|
||||
int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
|
||||
if (!sd_dhcp_client_is_running(client) || client->state != DHCP_STATE_BOUND)
|
||||
if (!sd_dhcp_client_is_running(client) || client->state != DHCP_STATE_BOUND || client->bootp)
|
||||
return 0; /* do nothing */
|
||||
|
||||
client->start_delay = 0;
|
||||
@@ -2200,7 +2334,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
|
||||
previously connected, and if the link-layer address did not change,
|
||||
the client MAY issue a DHCPREQUEST to try to reclaim the current
|
||||
address. */
|
||||
if (client->last_addr && !client->anonymize)
|
||||
if (client->last_addr && !client->anonymize && !client->bootp)
|
||||
client_set_state(client, DHCP_STATE_INIT_REBOOT);
|
||||
|
||||
/* We currently ignore:
|
||||
@@ -2218,7 +2352,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, DHCP_RELEASE, &release, &optlen, &optoffset);
|
||||
|
||||
@@ -145,6 +145,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);
|
||||
|
||||
Reference in New Issue
Block a user