Skip to content

Backport BOOTP from systemd/main (v257-8) to debian/systemd v252 Bookworm .deb #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: agd-debian/252.31-1_deb12u1-base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
674 changes: 674 additions & 0 deletions debian/patches/backport-bootp-from-v258-main.patch

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions debian/patches/series
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch
debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
debian/Downgrade-a-couple-of-warnings-to-debug.patch
debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch
backport-bootp-from-v258-main.patch
9 changes: 9 additions & 0 deletions man/systemd.network.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,15 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
</listitem>
</varlistentry>

<varlistentry>
<term><varname>BOOTP=</varname></term>
<listitem>
<para>Takes a boolean. The DHCPv4 client can be configured to communicate with BOOP servers that
don't accept Option 53, DHCP Message Type. In this configuration, a BOOTP Request is sent without
any options by default. Expects a BOOTREPLY that contains a permanent unique IP assignment.</para>
</listitem>
</varlistentry>

<!-- How to use the DHCP lease -->

<varlistentry>
Expand Down
3 changes: 3 additions & 0 deletions src/libsystemd-network/dhcp-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,

int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);

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, uint32_t xid,
uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
size_t optlen, size_t *optoffset);
Expand Down
27 changes: 27 additions & 0 deletions src/libsystemd-network/dhcp-packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,33 @@

#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312

int bootp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr) {
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
assert(chaddr || hlen == 0);

message->op = op;
message->htype = arp_type;

/* RFC2131 section 4.1.1:
The client MUST include its hardware address in the ’chaddr’ field, if
necessary for delivery of DHCP reply messages.

RFC 4390 section 2.1:
A DHCP client, when working over an IPoIB interface, MUST follow the
following rules:
"htype" (hardware address type) MUST be 32 [ARPPARAM].
"hlen" (hardware address length) MUST be 0.
"chaddr" (client hardware address) field MUST be zeroed.
*/
message->hlen = arp_type == ARPHRD_INFINIBAND ? 0 : hlen;
memcpy_safe(message->chaddr, chaddr, message->hlen);

message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE);

return 0;
}

int dhcp_message_init(
DHCPMessage *message,
uint8_t op,
Expand Down
297 changes: 189 additions & 108 deletions src/libsystemd-network/sd-dhcp-client.c

Large diffs are not rendered by default.

213 changes: 213 additions & 0 deletions src/libsystemd-network/test-dhcp-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ static struct hw_addr_data hw_addr = {
};
typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);

struct bootp_addr_data {
uint8_t *offer_buf;
int offer_len;
int netmask_offset;
int ip_offset;
};
struct bootp_addr_data *bootp_test_context;

static bool verbose = true;
static int test_fd[2];
static test_callback_recv_t callback_recv;
Expand Down Expand Up @@ -531,6 +539,204 @@ static void test_addr_acq(sd_event *e) {
xid = 0;
}

static uint8_t test_addr_bootp_reply[] = {
0x45, 0x00, 0x01, 0x48, 0x00, 0x00, 0x40, 0x00,
0xff, 0x11, 0x70, 0xa3, 0x0a, 0x00, 0x00, 0x02,
0xff, 0xff, 0xff, 0xff, 0x00, 0x43, 0x00, 0x44,
0x01, 0x2c, 0x2b, 0x91, 0x02, 0x01, 0x06, 0x00,
0x69, 0xd3, 0x79, 0x11, 0x17, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0a, 0x46, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x2d, 0xf4, 0x1f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x63, 0x82, 0x53, 0x63, 0x01, 0x04, 0xff, 0x00,
0x00, 0x00, 0x36, 0x04, 0x0a, 0x00, 0x00, 0x02,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};

static uint8_t test_addr_bootp_reply_bootpd[] = {
0x45, 0x00, 0x01, 0x48, 0xbe, 0xad, 0x40, 0x00,
0x40, 0x11, 0x73, 0x43, 0xc0, 0xa8, 0x43, 0x31,
0xc0, 0xa8, 0x43, 0x32, 0x00, 0x43, 0x00, 0x44,
0x01, 0x34, 0x08, 0xfa, 0x02, 0x01, 0x06, 0x00,
0x82, 0x57, 0xda, 0xf1, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x43, 0x32,
0xc0, 0xa8, 0x43, 0x31, 0x00, 0x00, 0x00, 0x00,
0xc2, 0x3e, 0xa5, 0x53, 0x57, 0x72, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x63, 0x82, 0x53, 0x63, 0x01, 0x04, 0xff, 0xff,
0xff, 0xf0, 0x03, 0x04, 0xc0, 0xa8, 0x43, 0x31,
0x06, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x0c, 0x15,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2d, 0x74,
0x72, 0x69, 0x78, 0x69, 0x65, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

static struct bootp_addr_data bootp_addr_data[] = {
{
.offer_buf = test_addr_bootp_reply,
.offer_len = sizeof(test_addr_bootp_reply),
.netmask_offset = 270,
.ip_offset = 44,
},
{
.offer_buf = test_addr_bootp_reply_bootpd,
.offer_len = sizeof(test_addr_bootp_reply_bootpd),
.netmask_offset = 270,
.ip_offset = 44,
},
};

static int test_bootp_acquired(sd_dhcp_client *client, int event,
void *userdata) {
sd_event *e = userdata;
sd_dhcp_lease *lease;
struct in_addr addr;

assert_se(client);
assert_se(IN_SET(event, SD_DHCP_CLIENT_EVENT_IP_ACQUIRE, SD_DHCP_CLIENT_EVENT_SELECTING));

assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
assert_se(lease);

assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
assert_se(memcmp(&addr.s_addr, &bootp_test_context->offer_buf[bootp_test_context->ip_offset],
sizeof(addr.s_addr)) == 0);

assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
assert_se(memcmp(&addr.s_addr, &bootp_test_context->offer_buf[bootp_test_context->netmask_offset],
sizeof(addr.s_addr)) == 0);

if (verbose)
log_info(" BOOTP address acquired");

sd_event_exit(e, 0);

return 0;
}

static int test_bootp_recv_request(size_t size, DHCPMessage *request) {
uint16_t udp_check = 0;
int res;

xid = request->xid;

if (verbose)
log_info(" recv BOOTP Request 0x%08x", be32toh(xid));

callback_recv = NULL;

memcpy(&bootp_test_context->offer_buf[26], &udp_check, sizeof(udp_check));
memcpy(&bootp_test_context->offer_buf[32], &xid, sizeof(xid));
memcpy(&bootp_test_context->offer_buf[56], hw_addr.bytes, hw_addr.length);

res = write(test_fd[1], bootp_test_context->offer_buf,
bootp_test_context->offer_len);
assert_se(res == bootp_test_context->offer_len);

if (verbose)
log_info(" sent BOOTP Reply");

return 0;
};

static void test_acquire_bootp(sd_event *e) {
sd_dhcp_client *client;
int res, r;

if (verbose)
log_info("* %s", __func__);

r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);

r = sd_dhcp_client_attach_event(client, e, 0);
assert_se(r >= 0);

r = sd_dhcp_client_set_bootp(client, true);
assert_se(r >= 0);

assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0);

assert_se(sd_dhcp_client_set_callback(client, test_bootp_acquired, e) >= 0);

callback_recv = test_bootp_recv_request;

assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);

res = sd_dhcp_client_start(client);
assert_se(IN_SET(res, 0, -EINPROGRESS));

assert_se(sd_event_loop(e) >= 0);

assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0);
assert_se(sd_dhcp_client_stop(client) >= 0);
sd_dhcp_client_unref(client);

test_fd[1] = safe_close(test_fd[1]);

callback_recv = NULL;
xid = 0;
}

int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e;

Expand All @@ -546,6 +752,13 @@ int main(int argc, char *argv[]) {
test_discover_message(e);
test_addr_acq(e);

for (size_t i = 0; i < sizeof(bootp_addr_data) / sizeof(bootp_addr_data[0]); i++) {
sd_event_unref(e);
assert_se(sd_event_new(&e) >= 0);
bootp_test_context = &bootp_addr_data[i];
test_acquire_bootp(e);
}

#if VALGRIND
/* Make sure the async_close thread has finished.
* valgrind would report some of the phread_* structures
Expand Down
6 changes: 6 additions & 0 deletions src/network/networkd-dhcp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,12 @@ static int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to allocate DHCPv4 client: %m");

if (link->network->dhcp_send_bootp) {
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");
Expand Down
2 changes: 1 addition & 1 deletion src/network/networkd-network-gperf.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ DHCPv4.UseRoutes, config_parse_bool,
DHCPv4.UseGateway, config_parse_tristate, 0, offsetof(Network, dhcp_use_gateway)
DHCPv4.RequestOptions, config_parse_dhcp_request_options, AF_INET, 0
DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
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)
Expand Down
1 change: 1 addition & 0 deletions src/network/networkd-network.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ struct Network {
OrderedHashmap *dhcp_client_send_options;
OrderedHashmap *dhcp_client_send_vendor_options;
char *dhcp_netlabel;
bool dhcp_send_bootp;

/* DHCPv6 Client support */
bool dhcp6_use_address;
Expand Down
3 changes: 3 additions & 0 deletions src/systemd/sd-dhcp-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ int sd_dhcp_client_set_service_type(
int sd_dhcp_client_set_fallback_lease_lifetime(
sd_dhcp_client *client,
uint32_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);
Expand Down
2 changes: 1 addition & 1 deletion test/fuzz/fuzz-network-parser/directives
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ UseSIP=
UseMTU=
UseDomainName=
RouteMetric=
SendHostname=
BOOTP=
Anonymize=
VendorClassIdentifier=
Hostname=
Expand Down
9 changes: 9 additions & 0 deletions test/test-network/conf/25-bootp-client.network
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99

[Network]
DHCP=ipv4

[DHCPv4]
BOOTP=yes
21 changes: 21 additions & 0 deletions test/test-network/systemd-networkd-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5025,6 +5025,27 @@ def test_dhcp_client_with_ipv4ll(self):
self.assertNotIn('192.168.5.', output)
self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)

def test_bootp_client(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-bootp-client.network')
start_networkd()
self.wait_online('veth-peer:carrier')

start_dnsmasq('--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule')
# def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
self.wait_online('veth99:routable', 'veth-peer:routable')
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')

state = get_dhcp4_client_state('veth99')
print(f"DHCPv4 client state = {state}")
self.assertEqual(state, 'bound')

output = read_dnsmasq_log_file()
self.assertIn('BOOTP(veth-peer)', output)
self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
self.assertNotIn('DHCPOFFER(veth-peer)', output)
self.assertNotIn('DHCPREQUEST(veth-peer)', output)
self.assertNotIn('DHCPACK(veth-peer)', output)

def test_dhcp_client_use_dns(self):
def check(self, ipv4, ipv6):
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
Expand Down