sd-dhcp-server: drop legacy DHCP relay mode

This commit is contained in:
Yu Watanabe
2026-05-21 16:55:59 +09:00
parent cb2bba6b04
commit 54c97ce6a4
10 changed files with 20 additions and 305 deletions
-31
View File
@@ -7,7 +7,6 @@
#include "alloc-util.h"
#include "dhcp-option.h"
#include "dhcp-server-internal.h"
#include "dns-domain.h"
#include "hostname-util.h"
#include "memory-util.h"
@@ -39,8 +38,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
assert(size > 0);
assert(offset);
int r;
if (code != SD_DHCP_OPTION_END)
/* always make sure there is space for an END option */
size--;
@@ -68,34 +65,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
*offset += 3 + optlen;
break;
case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: {
/* When called with raw data (optlen > 0), e.g. from SendOption=, append as a plain TLV.
* The structured handling below expects optval to be an sd_dhcp_server*. */
if (optlen > 0)
return dhcp_option_append_tlv(options, size, offset, code, optlen, optval);
sd_dhcp_server *server = (sd_dhcp_server *) optval;
size_t current_offset = *offset + 2;
if (server->agent_circuit_id) {
r = dhcp_option_append_tlv(options, size, &current_offset, SD_DHCP_RELAY_AGENT_CIRCUIT_ID,
strlen(server->agent_circuit_id), server->agent_circuit_id);
if (r < 0)
return r;
}
if (server->agent_remote_id) {
r = dhcp_option_append_tlv(options, size, &current_offset, SD_DHCP_RELAY_AGENT_REMOTE_ID,
strlen(server->agent_remote_id), server->agent_remote_id);
if (r < 0)
return r;
}
options[*offset] = code;
options[*offset + 1] = current_offset - *offset - 2;
assert(current_offset - *offset - 2 <= UINT8_MAX);
*offset = current_offset;
break;
}
default:
return dhcp_option_append_tlv(options, size, offset, code, optlen, optval);
}
@@ -32,14 +32,11 @@ typedef struct sd_dhcp_server {
sd_event *event;
int event_priority;
sd_event_source *receive_message;
sd_event_source *receive_broadcast;
int fd;
int fd_raw;
int fd_broadcast;
int ifindex;
char *ifname;
bool bind_to_interface;
be32_t address;
be32_t netmask;
be32_t subnet;
@@ -73,11 +70,6 @@ typedef struct sd_dhcp_server {
sd_dhcp_server_callback_t callback;
void *callback_userdata;
struct in_addr relay_target;
char *agent_circuit_id;
char *agent_remote_id;
int lease_dir_fd;
char *lease_file;
} sd_dhcp_server;
@@ -1,45 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include "fuzz.h"
#include "sd-dhcp-server.c"
ssize_t sendto(int __fd, const void *__buf, size_t __n, int flags, const struct sockaddr *__addr, socklen_t __addr_len) {
return __n;
}
ssize_t sendmsg(int __fd, const struct msghdr *__message, int flags) {
return 0;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
union in_addr_union relay_address;
_cleanup_free_ uint8_t *message = NULL;
if (size < sizeof(DHCPMessage))
return 0;
fuzz_setup_logging();
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
assert_se(in_addr_from_string(AF_INET, "192.168.5.1", &relay_address) >= 0);
assert_se(sd_dhcp_server_set_relay_target(server, &relay_address.in) >= 0);
assert_se(sd_dhcp_server_set_bind_to_interface(server, false) >= 0);
assert_se(sd_dhcp_server_set_relay_agent_information(server, "string:sample_circuit_id", "string:sample_remote_id") >= 0);
size_t buflen = size;
buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
assert_se(message = malloc(buflen));
memcpy(message, data, size);
server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
assert_se(server->fd >= 0);
(void) dhcp_server_relay_message(server, (DHCPMessage *) message, size - sizeof(DHCPMessage), buflen);
return 0;
}
-3
View File
@@ -145,9 +145,6 @@ executables += [
network_fuzz_template + {
'sources' : files('fuzz-dhcp-server.c'),
},
network_fuzz_template + {
'sources' : files('fuzz-dhcp-server-relay.c'),
},
network_fuzz_template + {
'sources' : files('fuzz-lldp-rx.c'),
},
+16 -204
View File
@@ -113,12 +113,6 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
return !!server->receive_message;
}
int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
assert_return(server, -EINVAL);
return in4_addr_is_set(&server->relay_target);
}
static sd_dhcp_server* dhcp_server_free(sd_dhcp_server *server) {
assert(server);
@@ -142,9 +136,6 @@ static sd_dhcp_server* dhcp_server_free(sd_dhcp_server *server) {
tlv_unref(server->extra_options);
tlv_unref(server->vendor_options);
free(server->agent_circuit_id);
free(server->agent_remote_id);
safe_close(server->lease_dir_fd);
free(server->lease_file);
@@ -168,11 +159,9 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
.n_ref = 1,
.fd_raw = -EBADF,
.fd = -EBADF,
.fd_broadcast = -EBADF,
.address = htobe32(INADDR_ANY),
.netmask = htobe32(INADDR_ANY),
.ifindex = ifindex,
.bind_to_interface = true,
.default_lease_time = DHCP_DEFAULT_LEASE_TIME_USEC,
.max_lease_time = DHCP_MAX_LEASE_TIME_USEC,
.rapid_commit = true,
@@ -289,11 +278,9 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
running = sd_dhcp_server_is_running(server);
server->receive_message = sd_event_source_disable_unref(server->receive_message);
server->receive_broadcast = sd_event_source_disable_unref(server->receive_broadcast);
server->fd_raw = safe_close(server->fd_raw);
server->fd = safe_close(server->fd);
server->fd_broadcast = safe_close(server->fd_broadcast);
if (running)
log_dhcp_server(server, "STOPPED");
@@ -385,27 +372,21 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
assert(message);
assert(len >= sizeof(DHCPMessage));
if (server->bind_to_interface) {
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
assert(cmsg);
cmsg = CMSG_FIRSTHDR(&msg);
assert(cmsg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
/* we attach source interface and address info to the message
rather than binding the socket. This will be mostly useful
when we gain support for arbitrary number of server addresses
*/
pktinfo = CMSG_TYPED_DATA(cmsg, struct in_pktinfo);
assert(pktinfo);
pktinfo = CMSG_TYPED_DATA(cmsg, struct in_pktinfo);
assert(pktinfo);
pktinfo->ipi_ifindex = server->ifindex;
pktinfo->ipi_spec_dst.s_addr = server->address;
}
pktinfo->ipi_ifindex = server->ifindex;
pktinfo->ipi_spec_dst.s_addr = server->address;
if (sendmsg(server->fd, &msg, 0) < 0)
return -errno;
@@ -1000,83 +981,6 @@ static bool address_is_in_pool(sd_dhcp_server *server, be32_t address) {
return true;
}
static int append_agent_information_option(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t size) {
int r;
size_t offset;
assert(server);
assert(message);
r = dhcp_option_find_option(message->options, opt_length, SD_DHCP_OPTION_END, &offset);
if (r < 0)
return r;
r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION, 0, server);
if (r < 0)
return r;
r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
return offset;
}
static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t buflen) {
_cleanup_free_ DHCPPacket *packet = NULL;
int r;
assert(server);
assert(message);
assert(sd_dhcp_server_is_in_relay_mode(server));
if (message->hlen == 0 || message->hlen > sizeof(message->chaddr) || memeqzero(message->chaddr, message->hlen))
return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
"(relay agent) received message without/invalid hardware address, discarding.");
if (message->op == BOOTREQUEST) {
log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
if (message->hops >= 16)
return -ETIME;
message->hops++;
/* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
if (message->giaddr == 0)
message->giaddr = server->address;
if (server->agent_circuit_id || server->agent_remote_id) {
r = append_agent_information_option(server, message, opt_length, buflen - sizeof(DHCPMessage));
if (r < 0)
return log_dhcp_server_errno(server, r, "could not append relay option: %m");
opt_length = r;
}
return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length);
} else if (message->op == BOOTREPLY) {
log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid));
if (message->giaddr != server->address)
return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
"(relay agent) BOOTREPLY giaddr mismatch, discarding");
int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL);
if (message_type < 0)
return message_type;
packet = malloc0(sizeof(DHCPPacket) + opt_length);
if (!packet)
return -ENOMEM;
memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length);
r = dhcp_option_remove_option(packet->dhcp.options, opt_length, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION);
if (r > 0)
opt_length = r;
bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
return dhcp_server_send(server, message->hlen, message->chaddr, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
}
return -EBADMSG;
}
static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
usec_t expiration;
int r;
@@ -1315,15 +1219,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 0;
}
static size_t relay_agent_information_length(const char* agent_circuit_id, const char* agent_remote_id) {
size_t sum = 0;
if (agent_circuit_id)
sum += 2 + strlen(agent_circuit_id);
if (agent_remote_id)
sum += 2 + strlen(agent_remote_id);
return sum;
}
static int server_receive_message(sd_event_source *s, int fd,
uint32_t revents, void *userdata) {
_cleanup_free_ DHCPMessage *message = NULL;
@@ -1351,10 +1246,6 @@ static int server_receive_message(sd_event_source *s, int fd,
}
size_t buflen = datagram_size;
if (sd_dhcp_server_is_in_relay_mode(server))
/* Preallocate the additional size for DHCP Relay Agent Information Option if needed */
buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
message = malloc0(buflen);
if (!message)
return -ENOMEM;
@@ -1377,15 +1268,10 @@ static int server_receive_message(sd_event_source *s, int fd,
if (info && info->ipi_ifindex != server->ifindex)
return 0;
if (sd_dhcp_server_is_in_relay_mode(server)) {
r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen);
if (r < 0)
log_dhcp_server_errno(server, r, "Couldn't relay message, ignoring: %m");
} else {
r = dhcp_server_handle_message(server, message, (size_t) len, TRIPLE_TIMESTAMP_FROM_CMSG(&msg));
if (r < 0)
log_dhcp_server_errno(server, r, "Couldn't process incoming message, ignoring: %m");
}
r = dhcp_server_handle_message(server, message, (size_t) len, TRIPLE_TIMESTAMP_FROM_CMSG(&msg));
if (r < 0)
log_dhcp_server_errno(server, r, "Couldn't process incoming message, ignoring: %m");
return 0;
}
@@ -1424,10 +1310,7 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
}
server->fd_raw = r;
if (server->bind_to_interface)
r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
else
r = dhcp_network_bind_udp_socket(0, server->address, DHCP_PORT_SERVER, -1);
r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
if (r < 0)
goto on_error;
server->fd = r;
@@ -1443,25 +1326,6 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
if (r < 0)
goto on_error;
if (!server->bind_to_interface) {
r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_BROADCAST, DHCP_PORT_SERVER, -1);
if (r < 0)
goto on_error;
server->fd_broadcast = r;
r = sd_event_add_io(server->event, &server->receive_broadcast,
server->fd_broadcast, EPOLLIN,
server_receive_message, server);
if (r < 0)
goto on_error;
r = sd_event_source_set_priority(server->receive_broadcast,
server->event_priority);
if (r < 0)
goto on_error;
}
r = dhcp_server_load_leases(server);
if (r < 0)
log_dhcp_server_errno(server, r, "Failed to load lease file %s, ignoring: %m", strna(server->lease_file));
@@ -1490,18 +1354,6 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
return r;
}
int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled) {
assert_return(server, -EINVAL);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
if (!!enabled == server->bind_to_interface)
return 0;
server->bind_to_interface = enabled;
return 1;
}
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
int r;
@@ -1649,46 +1501,6 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_
return 0;
}
int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr *address) {
assert_return(server, -EINVAL);
assert_return(address, -EINVAL);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
if (memcmp(address, &server->relay_target, sizeof(struct in_addr)) == 0)
return 0;
server->relay_target = *address;
return 1;
}
int sd_dhcp_server_set_relay_agent_information(
sd_dhcp_server *server,
const char *agent_circuit_id,
const char *agent_remote_id) {
_cleanup_free_ char *circuit_id_dup = NULL, *remote_id_dup = NULL;
assert_return(server, -EINVAL);
if (relay_agent_information_length(agent_circuit_id, agent_remote_id) > UINT8_MAX)
return -ENOBUFS;
if (agent_circuit_id) {
circuit_id_dup = strdup(agent_circuit_id);
if (!circuit_id_dup)
return -ENOMEM;
}
if (agent_remote_id) {
remote_id_dup = strdup(agent_remote_id);
if (!remote_id_dup)
return -ENOMEM;
}
free_and_replace(server->agent_circuit_id, circuit_id_dup);
free_and_replace(server->agent_remote_id, remote_id_dup);
return 0;
}
int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path) {
int r;
+4 -9
View File
@@ -22,7 +22,7 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) {
ASSERT_RETURN_IS_CRITICAL(false, ASSERT_ERROR(sd_dhcp_server_configure_pool(server, address, 8, 0, size), -ret));
}
static int test_basic(bool bind_to_interface) {
static int test_basic(void) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
struct in_addr address_lo = {
@@ -33,14 +33,13 @@ static int test_basic(bool bind_to_interface) {
};
int r;
log_debug("/* %s(bind_to_interface=%s) */", __func__, yes_no(bind_to_interface));
log_debug("/* %s */", __func__);
ASSERT_OK(sd_event_new(&event));
/* attach to loopback interface */
ASSERT_OK(sd_dhcp_server_new(&server, 1));
ASSERT_NOT_NULL(server);
server->bind_to_interface = bind_to_interface;
ASSERT_OK(sd_dhcp_server_attach_event(server, event, 0));
ASSERT_RETURN_EXPECTED(ASSERT_ERROR(sd_dhcp_server_attach_event(server, event, 0), EBUSY));
@@ -410,13 +409,9 @@ int main(int argc, char *argv[]) {
test_static_lease();
test_domain_name();
r = test_basic(true);
r = test_basic();
if (r < 0)
return log_tests_skipped_errno(r, "cannot start dhcp server(bound to interface)");
r = test_basic(false);
if (r < 0)
return log_tests_skipped_errno(r, "cannot start dhcp server(non-bound to interface)");
return log_tests_skipped_errno(r, "cannot start dhcp server");
test_message_handler();
-5
View File
@@ -57,7 +57,6 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, const struct in_addr *
int sd_dhcp_server_set_boot_server_address(sd_dhcp_server *server, const struct in_addr *address);
int sd_dhcp_server_set_boot_server_name(sd_dhcp_server *server, const char *name);
int sd_dhcp_server_set_boot_filename(sd_dhcp_server *server, const char *filename);
int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled);
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz);
int sd_dhcp_server_set_domain_name(sd_dhcp_server *server, const char *domain_name);
int sd_dhcp_server_set_router(sd_dhcp_server *server, const struct in_addr *router);
@@ -90,10 +89,6 @@ int sd_dhcp_server_set_rapid_commit(sd_dhcp_server *server, int enabled);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server);
int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address);
int sd_dhcp_server_set_relay_agent_information(sd_dhcp_server *server, const char* circuit_id, const char* remote_id);
int sd_dhcp_server_get_lease_address_by_name(sd_dhcp_server *server, const char *name, struct in_addr *ret);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref);