From fff2138a9ab790ff49b93167e6fdd68853da6434 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Tue, 22 Oct 2024 13:26:36 -0500 Subject: [PATCH] network/dhcpv4: add ability to use 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 following network option to enable BOOTP: [DHCPv4] Bootp=yes This will allow a two message request / reply sequence that doesn't require DHCP message types. --- src/libsystemd-network/dhcp-packet.c | 31 ++++++++---- src/libsystemd-network/dhcp-packet.h | 8 ++++ src/libsystemd-network/sd-dhcp-client.c | 60 +++++++++++++++++------- src/network/networkd-dhcp4.c | 4 ++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + src/systemd/sd-dhcp-client.h | 3 ++ 8 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 75b1d7e6cc7..a4231451bac 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -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) diff --git a/src/libsystemd-network/dhcp-packet.h b/src/libsystemd-network/dhcp-packet.h index 751321b92f6..438594d0337 100644 --- a/src/libsystemd-network/dhcp-packet.h +++ b/src/libsystemd-network/dhcp-packet.h @@ -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, diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 7a899669126..1d287697841 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -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,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, bool 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); @@ -793,9 +803,15 @@ static int client_message_init( 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) { + 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 +841,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 @@ -1509,13 +1527,15 @@ 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) + log_dhcp_client(client, "No DHCP options found in bootp message, continuing."); + 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) @@ -1532,8 +1552,11 @@ static int client_parse_message( 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 unexpected message, ignoring."); + if (client->bootp) + log_dhcp_client(client, "Ignoring return value %d for bootp message", r); + else + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), + "received unexpected message, ignoring."); break; @@ -1561,6 +1584,9 @@ static int client_parse_message( lease->next_server = message->siaddr; lease->address = message->yiaddr; + if (client->bootp) + lease->lifetime = USEC_INFINITY; + if (lease->address == 0 || lease->server_address == 0 || lease->lifetime == 0) @@ -1601,7 +1627,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,7 +2033,7 @@ 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) + if (client->lease->rapid_commit || client->bootp) /* got a successful rapid commit */ return client_enter_bound(client, r); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 2dd29bca948..bcdc9c45265 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1485,6 +1485,10 @@ static int dhcp4_configure(Link *link) { if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to allocate DHCPv4 client: %m"); + r = sd_dhcp_client_set_bootp(link->dhcp_client, link->network->dhcp_send_bootp); + if (r < 0) + return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set BOOTP flag: %m"); + r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0); if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach event to DHCPv4 client: %m"); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 50be74e55f4..f4e614af80f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -243,6 +243,7 @@ DHCPv4.QuickAck, config_parse_bool, DHCPv4.RequestOptions, config_parse_dhcp_request_options, AF_INET, 0 DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize) DHCPv4.SendHostname, config_parse_dhcp_send_hostname, AF_INET, 0 +DHCPv4.Bootp, config_parse_bool, 0, offsetof(Network, dhcp_send_bootp) DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) DHCPv4.Label, config_parse_dhcp_label, 0, offsetof(Network, dhcp_label) DHCPv4.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 3d399fe876c..ea1efa98409 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -405,6 +405,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_broadcast = -1, .dhcp_ipv6_only_mode = -1, .dhcp_6rd_prefix_route_type = RTN_UNREACHABLE, + .dhcp_send_bootp = false, .dhcp6_use_address = true, .dhcp6_use_pd_prefix = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b4ab1179289..eced10425db 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -179,6 +179,7 @@ struct Network { OrderedHashmap *dhcp_client_send_vendor_options; char *dhcp_netlabel; NFTSetContext dhcp_nft_set_context; + bool dhcp_send_bootp; /* DHCPv6 Client support */ bool dhcp6_use_address; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 1483afa2203..fec2a74ecd4 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -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, + bool 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);