int socket_priority;
bool socket_priority_set;
bool ipv6_acquired;
+ bool bootp;
};
static const uint8_t default_req_opts[] = {
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);
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;
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,
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;
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)
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;
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;
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)
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),
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);
}
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;
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:
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);