sd-dhcp-relay: reimplement DHCP relay agent and drop legacy DHCP relay mode from sd-dhcp-server (#42130)

This commit is contained in:
Daan De Meyer
2026-05-21 16:23:44 +02:00
committed by GitHub
53 changed files with 2674 additions and 470 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);
}
@@ -0,0 +1,503 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if_arp.h>
#include "sd-event.h"
#include "dhcp-message.h"
#include "dhcp-protocol.h"
#include "dhcp-relay-internal.h"
#include "errno-util.h"
#include "ether-addr-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "siphash24.h"
#include "socket-util.h"
int sd_dhcp_relay_downstream_set_circuit_id(sd_dhcp_relay_interface *interface, const struct iovec *iov) {
assert_return(interface, -EINVAL);
assert_return(!interface->upstream, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
return iovec_done_and_memdup(&interface->circuit_id, iov);
}
int sd_dhcp_relay_downstream_set_virtual_subnet_selection(sd_dhcp_relay_interface *interface, const struct iovec *iov) {
assert_return(interface, -EINVAL);
assert_return(!interface->upstream, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
return iovec_done_and_memdup(&interface->vss, iov);
}
int downstream_set_extra_options(sd_dhcp_relay_interface *interface, TLV *options) {
assert(interface);
assert(!interface->upstream);
assert(!sd_dhcp_relay_interface_is_running(interface));
return unref_and_replace_new_ref(interface->extra_options, options, tlv_ref, tlv_unref);
}
int sd_dhcp_relay_downstream_set_gateway_address(sd_dhcp_relay_interface *interface, const struct in_addr *address) {
assert_return(interface, -EINVAL);
assert_return(!interface->upstream, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
if (address)
interface->gateway_address = *address;
else
interface->gateway_address = (struct in_addr) {};
return 0;
}
static void downstream_hash_func(const sd_dhcp_relay_interface *interface, struct siphash *state) {
int b;
assert(interface);
assert(!interface->upstream);
assert(state);
siphash24_compress_typesafe(interface->gateway_address, state);
b = iovec_is_set(&interface->circuit_id);
siphash24_compress_typesafe(b, state);
if (b)
siphash24_compress_iovec(&interface->circuit_id, state);
b = iovec_is_set(&interface->vss);
siphash24_compress_typesafe(b, state);
if (b)
siphash24_compress_iovec(&interface->vss, state);
}
static int downstream_compare_func(const sd_dhcp_relay_interface *a, const sd_dhcp_relay_interface *b) {
int r;
assert(a);
assert(!a->upstream);
assert(b);
assert(!b->upstream);
r = CMP(a->gateway_address.s_addr, b->gateway_address.s_addr);
if (r != 0)
return r;
r = iovec_memcmp(&a->circuit_id, &b->circuit_id);
if (r != 0)
return r;
return iovec_memcmp(&a->vss, &b->vss);
}
DEFINE_PRIVATE_HASH_OPS(
downstream_hash_ops,
sd_dhcp_relay_interface,
downstream_hash_func,
downstream_compare_func);
int downstream_register(sd_dhcp_relay_interface *interface) {
assert(interface);
assert(interface->relay);
assert(!interface->upstream);
assert(in4_addr_is_set(&interface->address));
assert(in4_addr_is_set(&interface->gateway_address));
assert(!sd_dhcp_relay_interface_is_running(interface));
/* Do not use a Set; otherwise, we cannot deduplicate entries. */
return hashmap_ensure_put(&interface->relay->downstream_interfaces, &downstream_hash_ops, interface, interface);
}
void downstream_unregister(sd_dhcp_relay_interface *interface) {
assert(interface);
assert(interface->relay);
assert(!interface->upstream);
hashmap_remove_value(interface->relay->downstream_interfaces, interface, interface);
}
void downstream_done(sd_dhcp_relay_interface *interface) {
assert(interface);
assert(!interface->upstream);
downstream_unregister(interface);
iovec_done(&interface->circuit_id);
iovec_done(&interface->vss);
interface->extra_options = tlv_unref(interface->extra_options);
}
int downstream_get(sd_dhcp_relay *relay, sd_dhcp_message *message, sd_dhcp_relay_interface **ret) {
int r;
assert(relay);
assert(message);
/* RFC 3046 section 2.2:
* DHCP servers claiming to support the Relay Agent Information option SHALL echo the entire contents
* of the Relay Agent Information option in all replies.
*
* So, first try to find the suitable downstream interface by the gateway address and circuit ID in
* the reply message. */
sd_dhcp_relay_interface key = {
.gateway_address.s_addr = message->header.giaddr,
};
_cleanup_(tlv_unrefp) TLV *agent_info = NULL;
r = dhcp_message_get_option_sub_tlv(
message,
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION,
TLV_DHCP4_SUBOPTION,
&agent_info);
if (r < 0 && r != -ENODATA)
return r;
if (agent_info) {
r = tlv_get(agent_info, SD_DHCP_RELAY_AGENT_CIRCUIT_ID, &key.circuit_id);
if (r < 0 && r != -ENODATA)
return r;
r = tlv_get(agent_info, SD_DHCP_RELAY_AGENT_VIRTUAL_SUBNET_SELECTION, &key.vss);
if (r < 0 && r != -ENODATA)
return r;
}
sd_dhcp_relay_interface *interface = hashmap_get(relay->downstream_interfaces, &key);
if (!interface) {
/* Some DHCP servers may not understand the Relay Agent Information option and may not echo
* it back. To support this case, we fall back to finding a suitable downstream interface
* using only the gateway address. Note that if the downstream network uses VRF or the Link
* Selection sub-option, multiple interfaces may share the same gateway address. In such
* cases, we cannot reliably determine the correct downstream interface, so we must drop the
* packet. */
sd_dhcp_relay_interface *i;
HASHMAP_FOREACH(i, relay->downstream_interfaces) {
if (i->gateway_address.s_addr != message->header.giaddr)
continue;
if (interface)
/* multiple interfaces have the same gateway address?? */
return -ENOTUNIQ;
interface = i;
}
}
if (!interface)
return -ENODEV;
assert(!interface->upstream);
assert(interface->io_event_source);
if (ret)
*ret = interface;
return 0;
}
static int downstream_append_relay_agent_information(
sd_dhcp_relay_interface *interface,
sd_dhcp_message *message,
const struct in_pktinfo *pktinfo) {
int r;
assert(interface);
assert(interface->relay);
assert(!interface->upstream);
assert(message);
_cleanup_(tlv_done) TLV tlv = TLV_INIT(TLV_DHCP4_SUBOPTION);
/* First, set per-interface options. */
if (iovec_is_set(&interface->circuit_id)) {
r = tlv_append_iov(&tlv, SD_DHCP_RELAY_AGENT_CIRCUIT_ID, &interface->circuit_id);
if (r < 0)
return r;
}
if (iovec_is_set(&interface->vss)) {
r = tlv_append_iov(&tlv, SD_DHCP_RELAY_AGENT_VIRTUAL_SUBNET_SELECTION, &interface->vss);
if (r < 0)
return r;
}
if (!in4_addr_equal(&interface->address, &interface->gateway_address)) {
/* RFC 3527 section 3
* The link-selection sub-option is used by any DHCP relay agent that desires to specify a
* subnet/link for a DHCP client request that it is relaying but needs the subnet/link
* specification to be different from the IP address the DHCP server should use when
* communicating with the relay agent. */
r = tlv_append(&tlv, SD_DHCP_RELAY_AGENT_LINK_SELECTION, sizeof(struct in_addr), &interface->address);
if (r < 0)
return r;
}
r = tlv_append_tlv(&tlv, interface->extra_options);
if (r < 0)
return r;
/* Then, set agent-wide options. */
if (iovec_is_set(&interface->relay->remote_id)) {
r = tlv_append_iov(&tlv, SD_DHCP_RELAY_AGENT_REMOTE_ID, &interface->relay->remote_id);
if (r < 0)
return r;
}
if (interface->relay->server_identifier_override) {
/* RFC 5107 section 1:
* This DHCP relay agent suboption, Server Identifier Override, allows the relay agent to
* tell the DHCP server what value to place into the Server Identifier option. Using this,
* the relay agent can force a host in RENEWING state to send DHCPREQUEST messages to the
* relay agent instead of directly to the server. */
r = tlv_append(&tlv, SD_DHCP_RELAY_AGENT_SERVER_IDENTIFIER_OVERRIDE, sizeof(struct in_addr), &interface->address);
if (r < 0)
return r;
/* RFC 5107 section 4:
* DHCP relay agents implementing this suboption SHOULD also implement and use the DHCPv4
* Relay Agent Flags Suboption in order to specify whether the DHCP relay agent received the
* original message as a broadcast or unicast. */
uint8_t flags = 0;
SET_FLAG(flags, DHCP_RELAY_AGENT_FLAG_UNICAST,
pktinfo &&
pktinfo->ipi_addr.s_addr != INADDR_BROADCAST &&
pktinfo->ipi_addr.s_addr != interface->subnet_broadcast.s_addr);
r = tlv_append(&tlv, SD_DHCP_RELAY_AGENT_FLAGS, sizeof(uint8_t), &flags);
if (r < 0)
return r;
}
r = tlv_append_tlv(&tlv, interface->relay->extra_options);
if (r < 0)
return r;
if (tlv_isempty(&tlv))
return 0;
return dhcp_message_append_option_sub_tlv(message, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION, &tlv);
}
int downstream_process_message(
sd_dhcp_relay_interface *interface,
const struct iovec *iov,
const struct in_pktinfo *pktinfo) {
int r;
assert(interface);
assert(interface->relay);
assert(!interface->upstream);
assert(in4_addr_is_set(&interface->address));
assert(in4_addr_is_set(&interface->gateway_address));
assert(iov);
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *message = NULL;
r = dhcp_message_parse(
iov,
BOOTREQUEST,
/* xid= */ NULL,
ARPHRD_NONE,
/* hw_addr= */ NULL,
&message);
if (r < 0)
return r;
/* RFC 1542 section 4.1.1:
* The relay agent MUST silently discard BOOTREQUEST messages whose 'hops' field exceeds the value 16. */
if (message->header.hops >= 16)
return 0;
message->header.hops++;
/* RFC 3046 section 2.1.1:
* Relay agents configured to add a Relay Agent option which receive a client DHCP packet with a
* nonzero giaddr SHALL discard the packet if the giaddr spoofs a giaddr address implemented by the
* local agent itself. */
if (message->header.giaddr != INADDR_ANY) {
sd_dhcp_relay_interface *i;
HASHMAP_FOREACH(i, interface->relay->interfaces) {
if (i->upstream)
continue;
if (message->header.giaddr == i->address.s_addr ||
message->header.giaddr == i->gateway_address.s_addr)
return -EBADMSG;
}
}
/* RFC 1542 section 4.1.1:
* If the relay agent does decide to relay the request, it MUST examine the 'giaddr' ("gateway" IP
* address) field. If this field is zero, the relay agent MUST fill this field with the IP address of
* the interface on which the request was received. (snip) If the 'giaddr' field contains some
* non-zero value, the 'giaddr' field MUST NOT be modified.
*
* RFC 3046 section 2.1.1:
* the relay agent SHALL forward any received DHCP packet with a valid non-zero giaddr WITHOUT adding
* any relay agent options. Per RFC 2131, it shall also NOT modify the giaddr value.
*
* Therefore, we set giaddr and the Relay Agent Information option here only when the giaddr in the
* received message is zero. */
if (message->header.giaddr == INADDR_ANY) {
message->header.giaddr = interface->gateway_address.s_addr;
/* RFC 3046 section 2.1:
* Relay agents receiving a DHCP packet from an untrusted circuit with giaddr set to zero
* (indicating that they are the first-hop router) but with a Relay Agent Information option
* already present in the packet SHALL discard the packet and increment an error count. */
if (dhcp_message_has_option(message, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION))
return -EBADMSG;
r = downstream_append_relay_agent_information(interface, message, pktinfo);
if (r < 0)
return r;
}
log_dhcp_relay_interface(interface, "Received BOOTREQUEST (0x%"PRIx32")", be32toh(message->header.xid));
sd_dhcp_relay_interface *upstream;
r = upstream_get(interface->relay, &upstream);
if (r < 0)
return r;
return upstream_send_message(upstream, message);
}
static int downstream_acquire_raw_socket(sd_dhcp_relay_interface *interface) {
int r;
assert(interface);
assert(!interface->upstream);
if (interface->socket_fd >= 0)
/* When a socket fd is given externally, unconditionally use it. */
return interface->socket_fd;
if (interface->raw_socket_fd >= 0)
/* Already opened. */
return interface->raw_socket_fd;
/* This is a send-only socket, hence it is opened with protocol=0, and do not call bind().
* The interface binding will be done on send. */
_cleanup_close_ int fd = RET_NERRNO(socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, /* protocol= */ 0));
if (fd < 0)
return fd;
r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, tos_to_priority(interface->ip_service_type));
if (r < 0)
return r;
return interface->raw_socket_fd = TAKE_FD(fd);
}
static int downstream_send_l2_unicast(
sd_dhcp_relay_interface *interface,
sd_dhcp_message *message,
const struct hw_addr_data *hw_addr) {
int r;
assert(interface);
assert(!interface->upstream);
assert(message);
assert(message->header.yiaddr != INADDR_ANY);
assert(!hw_addr_is_null(hw_addr));
int fd = downstream_acquire_raw_socket(interface);
if (fd < 0)
return fd;
r = dhcp_message_send_raw(
message,
fd,
interface->ifindex,
interface->address.s_addr,
interface->port,
hw_addr,
message->header.yiaddr,
DHCP_PORT_CLIENT,
interface->ip_service_type);
if (r < 0)
return r;
log_dhcp_relay_interface(interface, "Forwarded BOOTREPLY (0x%"PRIx32") to %s (L2 unicast).",
be32toh(message->header.xid),
IN4_ADDR_TO_STRING(&(struct in_addr) { .s_addr = message->header.yiaddr }));
return 0;
}
static int downstream_send_udp(
sd_dhcp_relay_interface *interface,
sd_dhcp_message *message,
be32_t address) {
int r;
assert(interface);
assert(!interface->upstream);
assert(message);
assert(address != INADDR_ANY);
int fd = sd_event_source_get_io_fd(interface->io_event_source);
if (fd < 0)
return fd;
r = dhcp_message_send_udp(
message,
fd,
interface->address.s_addr,
address,
DHCP_PORT_CLIENT);
if (r < 0)
return r;
log_dhcp_relay_interface(interface, "Forwarded BOOTREPLY (0x%"PRIx32") to %s (UDP).",
be32toh(message->header.xid),
IN4_ADDR_TO_STRING(&(struct in_addr) { .s_addr = address }));
return 0;
}
int downstream_send_message(sd_dhcp_relay_interface *interface, sd_dhcp_message *message) {
int r;
assert(interface);
assert(!interface->upstream);
assert(message);
assert(message->header.op == BOOTREPLY);
/* See RFC 2131 Section 4.1
*
* (Note, we are a relay agent, hence conditions for giaddr in the statements are ignored.) */
uint8_t type;
r = dhcp_message_get_option_u8(message, SD_DHCP_OPTION_MESSAGE_TYPE, &type);
if (r < 0)
return r;
/* the server broadcasts any DHCPNAK messages to 0xffffffff. */
if (type == DHCP_NAK)
return downstream_send_udp(interface, message, INADDR_BROADCAST);
/* If (...) the ’ciaddr’ field is nonzero, then the server unicasts DHCPOFFER and DHCPACK messages
* to the address in ’ciaddr’. */
if (message->header.ciaddr != INADDR_ANY)
return downstream_send_udp(interface, message, message->header.ciaddr);
/* If (...) ’ciaddr’ is zero, and the broadcast bit is set, then the server broadcasts DHCPOFFER
* and DHCPACK messages to 0xffffffff.
*
* (Note, even the broadcast flag is unset, we may not know the client hardware address, e.g.
* InfiniBand. In that case, we cannot unicast in the below, so need to broadcast. Also, for other
* message types we do not support, also broadcast if 'yiaddr' is zero.) */
struct hw_addr_data hw_addr = {};
if (!dhcp_message_has_broadcast_flag(message) &&
message->header.yiaddr != INADDR_ANY) {
r = dhcp_message_get_hw_addr(message, &hw_addr);
if (r < 0)
return r;
}
if (hw_addr_is_null(&hw_addr))
return downstream_send_udp(interface, message, INADDR_BROADCAST);
/* If the broadcast bit is not set (...) and ’ciaddr’ is zero, then the server unicasts DHCPOFFER
* and DHCPACK messages to the client’s hardware address and ’yiaddr’ address. */
return downstream_send_l2_unicast(interface, message, &hw_addr);
}
@@ -0,0 +1,334 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <netinet/ip.h>
#include "sd-event.h"
#include "alloc-util.h"
#include "dhcp-protocol.h"
#include "dhcp-relay-internal.h"
#include "errno-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "socket-util.h"
#include "string-util.h"
static sd_dhcp_relay_interface* dhcp_relay_interface_free(sd_dhcp_relay_interface *interface) {
if (!interface)
return NULL;
assert(interface->relay);
sd_event_source_disable_unref(interface->io_event_source);
safe_close(interface->socket_fd);
safe_close(interface->raw_socket_fd);
if (interface->upstream)
upstream_done(interface);
else
downstream_done(interface);
hashmap_remove_value(interface->relay->interfaces, INT_TO_PTR(interface->ifindex), interface);
sd_dhcp_relay_unref(interface->relay);
free(interface->ifname);
return mfree(interface);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_relay_interface, sd_dhcp_relay_interface, dhcp_relay_interface_free);
int sd_dhcp_relay_add_interface(sd_dhcp_relay *relay, int ifindex, int is_upstream, sd_dhcp_relay_interface **ret) {
int r;
assert_return(relay, -EINVAL);
assert_return(ifindex > 0 || (ifindex == DHCP_RELAY_IFINDEX_UNBOUND && !!is_upstream), -EINVAL);
assert_return(ret, -EINVAL);
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *interface = new(sd_dhcp_relay_interface, 1);
if (!interface)
return -ENOMEM;
/* RFC 1542 section 5.4:
* The server SHOULD next check the 'giaddr' field. If this field is non-zero, the server SHOULD send
* the BOOTREPLY as an IP unicast to the IP address identified in the 'giaddr' field. The UDP
* destination port MUST be set to BOOTPS (67).
*
* Hence, the relay agent needs to use DHCP_PORT_SERVER (67) for both source and destination port. */
*interface = (sd_dhcp_relay_interface) {
.n_ref = 1,
.relay = sd_dhcp_relay_ref(relay),
.upstream = !!is_upstream,
.ifindex = ifindex,
.port = DHCP_PORT_SERVER,
.socket_fd = -EBADF,
.raw_socket_fd = -EBADF,
.ip_service_type = IPTOS_CLASS_CS6, /* Defaults to CS6 (Internetwork Control). */
};
r = hashmap_ensure_put(&relay->interfaces, NULL, INT_TO_PTR(interface->ifindex), interface);
if (r < 0)
return r;
*ret = TAKE_PTR(interface);
return 0;
}
int sd_dhcp_relay_interface_set_ifname(sd_dhcp_relay_interface *interface, const char *ifname) {
assert_return(interface, -EINVAL);
return free_and_strdup(&interface->ifname, ifname);
}
int sd_dhcp_relay_interface_get_ifname(sd_dhcp_relay_interface *interface, const char **ret) {
int r;
assert_return(interface, -EINVAL);
r = get_ifname(interface->ifindex, &interface->ifname);
if (r < 0)
return r;
if (ret)
*ret = interface->ifname;
return 0;
}
int sd_dhcp_relay_interface_set_address(sd_dhcp_relay_interface *interface, const struct in_addr *address, uint8_t prefixlen) {
assert_return(interface, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
assert_return(prefixlen <= sizeof(struct in_addr) * 8, -EINVAL);
if (address)
interface->address = *address;
else
interface->address = (struct in_addr) {};
if (in4_addr_is_set(&interface->address)) {
interface->address_prefixlen = prefixlen;
struct in_addr netmask;
in4_addr_prefixlen_to_netmask(&netmask, prefixlen);
interface->subnet_broadcast.s_addr = (interface->address.s_addr & netmask.s_addr) | ~netmask.s_addr;
} else {
interface->address_prefixlen = 0;
interface->subnet_broadcast = (struct in_addr) {};
}
return 0;
}
int sd_dhcp_relay_interface_get_address(sd_dhcp_relay_interface *interface, struct in_addr *ret_address, uint8_t *ret_prefixlen) {
assert_return(interface, -EINVAL);
if (ret_address)
*ret_address = interface->address;
if (ret_prefixlen)
*ret_prefixlen = interface->address_prefixlen;
return in4_addr_is_set(&interface->address);
}
int sd_dhcp_relay_interface_set_port(sd_dhcp_relay_interface *interface, uint16_t port) {
assert_return(interface, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
interface->port = port;
return 0;
}
int sd_dhcp_relay_interface_set_ip_service_type(sd_dhcp_relay_interface *interface, uint8_t type) {
assert_return(interface, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
interface->ip_service_type = type;
return 0;
}
int sd_dhcp_relay_interface_is_running(sd_dhcp_relay_interface *interface) {
return interface && sd_event_source_get_enabled(interface->io_event_source, /* ret= */ NULL) > 0;
}
static int interface_open_socket(sd_dhcp_relay_interface *interface) {
int r;
assert(interface);
_cleanup_close_ int fd = RET_NERRNO(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
if (fd < 0)
return fd;
if (interface->ifindex > 0) {
r = socket_bind_to_ifindex(fd, interface->ifindex);
if (r < 0)
return r;
}
r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return r;
r = setsockopt_int(fd, SOL_SOCKET, SO_BROADCAST, true);
if (r < 0)
return r;
r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, tos_to_priority(interface->ip_service_type));
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_TOS, interface->ip_service_type);
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
return r;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(interface->port),
.in.sin_addr.s_addr = INADDR_ANY,
};
if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
return -errno;
return TAKE_FD(fd);
}
static int interface_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_dhcp_relay_interface *interface = ASSERT_PTR(userdata);
int r;
assert(fd >= 0);
ssize_t buflen = next_datagram_size_fd(fd);
if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
return 0;
if (buflen < 0) {
log_dhcp_relay_interface_errno(
interface, buflen,
"Failed to determine datagram size to read, ignoring: %m");
return 0;
}
_cleanup_free_ void *buf = malloc0(buflen);
if (!buf)
return log_oom_debug();
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
struct msghdr msg = {
.msg_iov = &IOVEC_MAKE(buf, buflen),
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
ssize_t len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
return 0;
if (len < 0) {
log_dhcp_relay_interface_errno(
interface, len,
"Could not receive message, ignoring: %m");
return 0;
}
if (interface->upstream)
r = upstream_process_message(
interface,
&IOVEC_MAKE(buf, len),
CMSG_FIND_DATA(&msg, IPPROTO_IP, IP_PKTINFO, struct in_pktinfo));
else
r = downstream_process_message(
interface,
&IOVEC_MAKE(buf, len),
CMSG_FIND_DATA(&msg, IPPROTO_IP, IP_PKTINFO, struct in_pktinfo));
if (r < 0)
log_dhcp_relay_interface_errno(
interface, r,
"Could not process message, ignoring: %m");
return 0;
}
int sd_dhcp_relay_interface_start(sd_dhcp_relay_interface *interface) {
int r;
assert_return(interface, -EINVAL);
assert_return(interface->relay, -ESTALE);
assert_return(interface->relay->event, -EINVAL);
if (in4_addr_is_null(&interface->relay->server_address) ||
(!interface->upstream && in4_addr_is_null(&interface->address)) ||
(!interface->upstream && in4_addr_is_null(&interface->gateway_address)))
return -EADDRNOTAVAIL;
if (sd_event_source_get_enabled(interface->io_event_source, /* ret= */ NULL) > 0)
return 0; /* Already started. */
_cleanup_close_ int fd_close = -EBADF;
int fd;
if (interface->socket_fd >= 0)
/* When a socket fd is given externally, unconditionally use it and do not close the socket
* even if we fail to set up the event source. */
fd = interface->socket_fd;
else {
/* Otherwise, open a new socket. */
fd = fd_close = interface_open_socket(interface);
if (fd < 0)
return fd;
}
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
r = sd_event_add_io(interface->relay->event, &s, fd, EPOLLIN,
interface_receive_message, interface);
if (r < 0)
return r;
r = sd_event_source_set_priority(s, interface->relay->event_priority);
if (r < 0)
return r;
const char *name, *description;
if (sd_dhcp_relay_interface_get_ifname(interface, &name) >= 0)
description = strjoina("dhcp-relay-interface-io-event-source-", name);
else
description = "dhcp-relay-interface-io-event-source";
(void) sd_event_source_set_description(s, description);
if (fd_close >= 0) {
r = sd_event_source_set_io_fd_own(s, true);
if (r < 0)
return r;
TAKE_FD(fd_close);
}
/* This may potentially fail, in which case the event source should be discarded. */
if (interface->upstream)
r = upstream_register(interface);
else
r = downstream_register(interface);
if (r < 0)
return r;
sd_event_source_disable_unref(interface->io_event_source);
interface->io_event_source = TAKE_PTR(s);
return 0;
}
int sd_dhcp_relay_interface_stop(sd_dhcp_relay_interface *interface) {
if (!interface)
return 0;
interface->raw_socket_fd = safe_close(interface->raw_socket_fd);
interface->io_event_source = sd_event_source_disable_unref(interface->io_event_source);
if (interface->upstream)
upstream_unregister(interface);
else
downstream_unregister(interface);
return 0;
}
@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-dhcp-relay.h"
#include "dhcp-message.h"
#include "ether-addr-util.h"
#include "network-common.h"
#include "sd-forward.h"
#include "tlv-util.h"
#define DHCP_RELAY_IFINDEX_UNBOUND (-100)
struct sd_dhcp_relay {
unsigned n_ref;
sd_event *event;
int event_priority;
Hashmap *interfaces; /* All interfaces by their ifindex. */
Prioq *upstream_interfaces; /* Active upstream interfaces by their priorities. */
Hashmap *downstream_interfaces; /* Active downstream interfaces by their gateway address, circuit ID, and VSS. */
struct in_addr server_address;
uint16_t server_port;
/* Global Relay Agent Information option (82) */
struct iovec remote_id; /* Agent Remote ID Sub-option (2) */
bool server_identifier_override; /* Relay Agent Flags (10) and Server Identifier Override Sub-option (11) */
TLV *extra_options;
};
struct sd_dhcp_relay_interface {
unsigned n_ref;
sd_dhcp_relay *relay;
bool upstream;
int ifindex;
char *ifname;
/* The address used for:
* - the source IP of forwarded packets (both downstream and upstream),
* - the Server Identifier Override Sub-option (when sd_dhcp_relay.server_identifier_override is true),
* - the Link Selection Sub-option (when address != gateway_address).
* Typically, this is an address of the interface itself, but we can specify an address of another
* interface (e.g., for IP unnumbered setups). */
struct in_addr address;
uint8_t address_prefixlen;
/* subnet-directed broadcast address, e.g. 192.0.2.255 when address is 192.0.2.1/24. */
struct in_addr subnet_broadcast;
uint16_t port;
uint8_t ip_service_type; /* a.k.a. TOS */
int socket_fd; /* socket fd set externally, used by unit tests */
int raw_socket_fd; /* send-only raw socket fd, used on sending L2 unicast message. */
sd_event_source *io_event_source;
/* Mutually exclusive fields depending on the 'upstream' boolean */
union {
/* Upstream specific */
struct {
int priority;
unsigned priority_idx;
};
/* Downstream specific */
struct {
/* The address set in the giaddr field of the DHCP message header. Typically, it is
* the same as 'address' above, but we can specify a different address, and it does
* not need to be an address assigned to the interface. */
struct in_addr gateway_address;
/* Per-interface Relay Agent Information option (82) */
struct iovec circuit_id; /* Agent Circuit ID Sub-option (1) */
struct iovec vss; /* DHCPv4 Virtual Subnet Selection Sub-Option (151) */
TLV *extra_options;
};
};
};
int dhcp_relay_set_extra_options(sd_dhcp_relay *relay, TLV *options);
int downstream_set_extra_options(sd_dhcp_relay_interface *interface, TLV *options);
int downstream_register(sd_dhcp_relay_interface *interface);
void downstream_unregister(sd_dhcp_relay_interface *interface);
void downstream_done(sd_dhcp_relay_interface *interface);
int downstream_get(sd_dhcp_relay *relay, sd_dhcp_message *message, sd_dhcp_relay_interface **ret);
int downstream_process_message(
sd_dhcp_relay_interface *interface,
const struct iovec *iov,
const struct in_pktinfo *pktinfo);
int downstream_send_message(sd_dhcp_relay_interface *interface, sd_dhcp_message *message);
int upstream_register(sd_dhcp_relay_interface *interface);
void upstream_unregister(sd_dhcp_relay_interface *interface);
void upstream_done(sd_dhcp_relay_interface *interface);
int upstream_get(sd_dhcp_relay *relay, sd_dhcp_relay_interface **ret);
int upstream_process_message(
sd_dhcp_relay_interface *interface,
const struct iovec *iov,
const struct in_pktinfo *pktinfo);
int upstream_send_message(sd_dhcp_relay_interface *interface, sd_dhcp_message *message);
#define log_dhcp_relay_interface_errno(interface, error, fmt, ...) \
log_interface_prefix_full_errno( \
"DHCPv4 relay: ", \
sd_dhcp_relay_interface, interface, \
error, fmt, ##__VA_ARGS__)
#define log_dhcp_relay_interface(interface, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"DHCPv4 relay: ", \
sd_dhcp_relay_interface, interface, \
0, fmt, ##__VA_ARGS__)
@@ -0,0 +1,150 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if_arp.h>
#include "sd-event.h"
#include "dhcp-message.h"
#include "dhcp-protocol.h"
#include "dhcp-relay-internal.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "prioq.h"
int sd_dhcp_relay_upstream_set_priority(sd_dhcp_relay_interface *interface, int priority) {
assert_return(interface, -EINVAL);
assert_return(interface->upstream, -EINVAL);
assert_return(!sd_dhcp_relay_interface_is_running(interface), -EBUSY);
interface->priority = priority;
return 0;
}
static int upstream_compare_func(const sd_dhcp_relay_interface *a, const sd_dhcp_relay_interface *b) {
assert(a);
assert(a->upstream);
assert(b);
assert(b->upstream);
/* Higher priority first */
return CMP(b->priority, a->priority);
}
int upstream_register(sd_dhcp_relay_interface *interface) {
assert(interface);
assert(interface->relay);
assert(interface->upstream);
assert(!sd_dhcp_relay_interface_is_running(interface));
interface->priority_idx = PRIOQ_IDX_NULL;
return prioq_ensure_put(&interface->relay->upstream_interfaces, upstream_compare_func, interface, &interface->priority_idx);
}
void upstream_unregister(sd_dhcp_relay_interface *interface) {
assert(interface);
assert(interface->relay);
assert(interface->upstream);
(void) prioq_remove(interface->relay->upstream_interfaces, interface, &interface->priority_idx);
}
void upstream_done(sd_dhcp_relay_interface *interface) {
upstream_unregister(interface);
}
int upstream_get(sd_dhcp_relay *relay, sd_dhcp_relay_interface **ret) {
sd_dhcp_relay_interface *interface = prioq_peek(relay->upstream_interfaces);
if (!interface)
return -ENETDOWN;
assert(interface->upstream);
assert(interface->io_event_source);
if (ret)
*ret = interface;
return 0;
}
int upstream_process_message(
sd_dhcp_relay_interface *interface,
const struct iovec *iov,
const struct in_pktinfo *pktinfo) {
int r;
assert(interface);
assert(interface->relay);
assert(interface->upstream);
assert(iov);
if (pktinfo && pktinfo->ipi_ifindex > 0 &&
interface->ifindex < 0 &&
hashmap_contains(interface->relay->interfaces, INT_TO_PTR(pktinfo->ipi_ifindex)))
return 0; /* This message is not for the catch-all interface. */
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *message = NULL;
r = dhcp_message_parse(
iov,
BOOTREPLY,
/* xid= */ NULL,
ARPHRD_NONE,
/* hw_addr= */ NULL,
&message);
if (r < 0)
return r;
if (message->header.giaddr == INADDR_ANY)
return 0; /* Not a relay message, so it is probably not for us. */
if (pktinfo && pktinfo->ipi_addr.s_addr != message->header.giaddr)
return -EBADMSG;
log_dhcp_relay_interface(interface, "Received BOOTREPLY (0x%"PRIx32")", be32toh(message->header.xid));
sd_dhcp_relay_interface *downstream;
r = downstream_get(interface->relay, message, &downstream);
if (r < 0)
return r;
/* RFC 3046 abstract:
* The DHCP Server echoes the option back verbatim to the relay agent in server-to-client
* replies, and the relay agent strips the option before forwarding the reply to the client.
*
* RFC 3046 section 2.1:
* The Relay Agent Information option echoed by a server MUST be removed by either the relay
* agent or the trusted downstream network element which added it when forwarding a
* server-to-client response back to the client.
*
* Here, we do not check the contents of the option, and unconditionally remove it. */
dhcp_message_remove_option(message, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION);
return downstream_send_message(downstream, message);
}
int upstream_send_message(sd_dhcp_relay_interface *interface, sd_dhcp_message *message) {
int r;
assert(interface);
assert(interface->upstream);
assert(message);
assert(message->header.op == BOOTREQUEST);
assert(message->header.giaddr != INADDR_ANY);
int fd = sd_event_source_get_io_fd(interface->io_event_source);
if (fd < 0)
return fd;
r = dhcp_message_send_udp(
message,
fd,
interface->address.s_addr,
interface->relay->server_address.s_addr,
interface->relay->server_port);
if (r < 0)
return r;
log_dhcp_relay_interface(interface, "Forwarded BOOTREQUEST (0x%"PRIx32") to %s (UDP).",
be32toh(message->header.xid),
IN4_ADDR_TO_STRING(&interface->relay->server_address));
return 0;
}
@@ -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;
+61
View File
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/socket.h>
#include "sd-event.h"
#include "dhcp-relay-internal.h"
#include "fd-util.h"
#include "fuzz.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "tests.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
union in_addr_union a;
fuzz_setup_logging();
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
ASSERT_OK(sd_event_new(&e));
_cleanup_(sd_dhcp_relay_unrefp) sd_dhcp_relay *relay = NULL;
ASSERT_OK(sd_dhcp_relay_new(&relay));
ASSERT_OK(sd_dhcp_relay_attach_event(relay, e, SD_EVENT_PRIORITY_NORMAL));
ASSERT_OK(in_addr_from_string(AF_INET, "198.51.100.1", &a));
ASSERT_OK(sd_dhcp_relay_set_server_address(relay, &a.in));
ASSERT_OK(sd_dhcp_relay_set_remote_id(relay, &IOVEC_MAKE_STRING("test-remote-id")));
ASSERT_OK(sd_dhcp_relay_set_server_identifier_override(relay, true));
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *upstream = NULL;
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4242, /* is_upstream= */ true, &upstream));
ASSERT_OK(sd_dhcp_relay_interface_set_ifname(upstream, "test-upstream"));
ASSERT_OK(in_addr_from_string(AF_INET, "198.51.100.2", &a));
ASSERT_OK(sd_dhcp_relay_interface_set_address(upstream, &a.in, 24));
_cleanup_close_pair_ int upstream_fd[2] = EBADF_PAIR;
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, upstream_fd));
upstream->socket_fd = TAKE_FD(upstream_fd[0]);
ASSERT_OK(sd_dhcp_relay_interface_start(upstream));
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *downstream = NULL;
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4343, /* is_upstream= */ false, &downstream));
ASSERT_OK(sd_dhcp_relay_interface_set_ifname(downstream, "test-downstream"));
ASSERT_OK(in_addr_from_string(AF_INET, "192.0.2.1", &a));
ASSERT_OK(sd_dhcp_relay_interface_set_address(downstream, &a.in, 24));
ASSERT_OK(in_addr_from_string(AF_INET, "203.0.113.1", &a));
ASSERT_OK(sd_dhcp_relay_downstream_set_gateway_address(downstream, &a.in));
ASSERT_OK(sd_dhcp_relay_downstream_set_circuit_id(downstream, &IOVEC_MAKE_STRING("test-circuit-id")));
ASSERT_OK(sd_dhcp_relay_downstream_set_virtual_subnet_selection(downstream, &IOVEC_MAKE_STRING("test-virtual-net")));
_cleanup_close_pair_ int downstream_fd[2] = EBADF_PAIR;
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, downstream_fd));
downstream->socket_fd = TAKE_FD(downstream_fd[0]);
ASSERT_OK(sd_dhcp_relay_interface_start(downstream));
(void) downstream_process_message(downstream, &IOVEC_MAKE(data, size), /* pktinfo= */ NULL);
(void) upstream_process_message(upstream, &IOVEC_MAKE(data, size), /* pktinfo= */ NULL);
return 0;
}
@@ -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;
}
+10 -3
View File
@@ -9,6 +9,9 @@ libsystemd_network_sources = files(
'dhcp-option.c',
'dhcp-packet.c',
'dhcp-protocol.c',
'dhcp-relay-downstream.c',
'dhcp-relay-interface.c',
'dhcp-relay-upstream.c',
'dhcp-route.c',
'dhcp6-network.c',
'dhcp6-option.c',
@@ -25,6 +28,7 @@ libsystemd_network_sources = files(
'sd-dhcp-client.c',
'sd-dhcp-duid.c',
'sd-dhcp-lease.c',
'sd-dhcp-relay.c',
'sd-dhcp-server-lease.c',
'sd-dhcp-server.c',
'sd-dhcp6-client.c',
@@ -92,6 +96,9 @@ executables += [
network_test_template + {
'sources' : files('test-dhcp-option.c'),
},
network_test_template + {
'sources' : files('test-dhcp-relay.c'),
},
network_test_template + {
'sources' : files('test-dhcp-server.c'),
},
@@ -129,15 +136,15 @@ executables += [
network_fuzz_template + {
'sources' : files('fuzz-dhcp-client.c'),
},
network_fuzz_template + {
'sources' : files('fuzz-dhcp-relay.c'),
},
network_fuzz_template + {
'sources' : files('fuzz-dhcp6-client.c'),
},
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'),
},
+136
View File
@@ -0,0 +1,136 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-event.h"
#include "alloc-util.h"
#include "dhcp-protocol.h"
#include "dhcp-relay-internal.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "prioq.h"
static sd_dhcp_relay* dhcp_relay_free(sd_dhcp_relay *relay) {
if (!relay)
return NULL;
assert(hashmap_isempty(relay->interfaces));
hashmap_free(relay->interfaces);
assert(hashmap_isempty(relay->downstream_interfaces));
hashmap_free(relay->downstream_interfaces);
assert(prioq_isempty(relay->upstream_interfaces));
prioq_free(relay->upstream_interfaces);
sd_event_unref(relay->event);
iovec_done(&relay->remote_id);
tlv_unref(relay->extra_options);
return mfree(relay);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_relay, sd_dhcp_relay, dhcp_relay_free);
int sd_dhcp_relay_new(sd_dhcp_relay **ret) {
assert_return(ret, -EINVAL);
sd_dhcp_relay *relay = new(sd_dhcp_relay, 1);
if (!relay)
return -ENOMEM;
*relay = (sd_dhcp_relay) {
.n_ref = 1,
.server_port = DHCP_PORT_SERVER,
};
*ret = TAKE_PTR(relay);
return 0;
}
int sd_dhcp_relay_is_running(sd_dhcp_relay *relay) {
if (!relay)
return false;
return
!prioq_isempty(relay->upstream_interfaces) ||
!hashmap_isempty(relay->downstream_interfaces);
}
int sd_dhcp_relay_attach_event(sd_dhcp_relay *relay, sd_event *event, int64_t priority) {
int r;
assert_return(relay, -EINVAL);
assert_return(!relay->event, -EBUSY);
if (event)
relay->event = sd_event_ref(event);
else {
r = sd_event_default(&relay->event);
if (r < 0)
return r;
}
relay->event_priority = priority;
return 0;
}
int sd_dhcp_relay_detach_event(sd_dhcp_relay *relay) {
assert_return(relay, -EINVAL);
assert_return(!sd_dhcp_relay_is_running(relay), -EBUSY);
relay->event = sd_event_unref(relay->event);
return 0;
}
sd_event* sd_dhcp_relay_get_event(sd_dhcp_relay *relay) {
assert_return(relay, NULL);
return relay->event;
}
int sd_dhcp_relay_set_server_address(sd_dhcp_relay *relay, const struct in_addr *address) {
assert_return(relay, -EINVAL);
assert_return(!sd_dhcp_relay_is_running(relay), -EBUSY);
if (address)
relay->server_address = *address;
else
relay->server_address = (struct in_addr) {};
return 0;
}
int sd_dhcp_relay_get_server_address(sd_dhcp_relay *relay, struct in_addr *ret) {
assert_return(relay, -EINVAL);
if (ret)
*ret = relay->server_address;
return in4_addr_is_set(&relay->server_address);
}
int sd_dhcp_relay_set_server_port(sd_dhcp_relay *relay, uint16_t port) {
assert_return(relay, -EINVAL);
assert_return(!sd_dhcp_relay_is_running(relay), -EBUSY);
relay->server_port = port;
return 0;
}
int sd_dhcp_relay_set_remote_id(sd_dhcp_relay *relay, const struct iovec *iov) {
assert_return(relay, -EINVAL);
return iovec_done_and_memdup(&relay->remote_id, iov);
}
int sd_dhcp_relay_set_server_identifier_override(sd_dhcp_relay *relay, int b) {
assert_return(relay, -EINVAL);
relay->server_identifier_override = !!b;
return 0;
}
int dhcp_relay_set_extra_options(sd_dhcp_relay *relay, TLV *options) {
assert(relay);
return unref_and_replace_new_ref(relay->extra_options, options, tlv_ref, tlv_unref);
}
+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;
+383
View File
@@ -0,0 +1,383 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if_arp.h>
#include <sys/socket.h>
#include "sd-event.h"
#include "dhcp-protocol.h"
#include "dhcp-relay-internal.h" /* IWYU pragma: keep */
#include "ether-addr-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "ip-util.h"
#include "socket-util.h"
#include "tests.h"
static uint32_t xid = 12345;
static const struct hw_addr_data hw_addr = {
.length = ETH_ALEN,
.ether = {{ 'A', 'B', 'C', '1', '2', '3' }},
};
static unsigned fake_server_message_count = 0;
static unsigned fake_client_message_count = 0;
TEST(sd_dhcp_relay_ref_unref) {
_cleanup_(sd_dhcp_relay_unrefp) sd_dhcp_relay *relay = NULL;
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *upstream = NULL, *downstream = NULL;
ASSERT_OK(sd_dhcp_relay_new(&relay));
ASSERT_NOT_NULL(relay);
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4242, /* is_upstream= */ true, &upstream));
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4343, /* is_upstream= */ false, &downstream));
ASSERT_PTR_EQ(hashmap_get(relay->interfaces, INT_TO_PTR(4242)), upstream);
ASSERT_PTR_EQ(hashmap_get(relay->interfaces, INT_TO_PTR(4343)), downstream);
/* Each interface holds a reference to the sd_dhcp_relay object, so we can safely drop our reference. */
relay = sd_dhcp_relay_unref(relay);
ASSERT_PTR_EQ(hashmap_get(upstream->relay->interfaces, INT_TO_PTR(4242)), upstream);
ASSERT_PTR_EQ(hashmap_get(downstream->relay->interfaces, INT_TO_PTR(4343)), downstream);
/* Still upstream interface has the reference. */
downstream = sd_dhcp_relay_interface_unref(downstream);
ASSERT_PTR_EQ(hashmap_get(upstream->relay->interfaces, INT_TO_PTR(4242)), upstream);
ASSERT_FALSE(hashmap_contains(upstream->relay->interfaces, INT_TO_PTR(4343)));
/* Everything should be freed with this. */
upstream = sd_dhcp_relay_interface_unref(upstream);
/* Let's check the inverse order. */
ASSERT_OK(sd_dhcp_relay_new(&relay));
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4242, /* is_upstream= */ true, &upstream));
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4343, /* is_upstream= */ false, &downstream));
ASSERT_PTR_EQ(hashmap_get(relay->interfaces, INT_TO_PTR(4242)), upstream);
ASSERT_PTR_EQ(hashmap_get(relay->interfaces, INT_TO_PTR(4343)), downstream);
downstream = sd_dhcp_relay_interface_unref(downstream);
ASSERT_PTR_EQ(hashmap_get(relay->interfaces, INT_TO_PTR(4242)), upstream);
ASSERT_FALSE(hashmap_contains(relay->interfaces, INT_TO_PTR(4343)));
upstream = sd_dhcp_relay_interface_unref(upstream);
ASSERT_FALSE(hashmap_contains(relay->interfaces, INT_TO_PTR(4242)));
ASSERT_FALSE(hashmap_contains(relay->interfaces, INT_TO_PTR(4343)));
}
static void send_message(int fd, sd_dhcp_message *m) {
ASSERT_OK(dhcp_message_send_udp(
m,
fd,
/* src_addr= */ INADDR_ANY,
/* dst_addr= */ INADDR_ANY,
/* dst_port= */ 0));
}
static int fake_server_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_dhcp_relay *relay = ASSERT_PTR(userdata);
fake_server_message_count++;
log_debug("%s: count=%u", __func__, fake_server_message_count);
ssize_t buflen = ASSERT_OK_POSITIVE(next_datagram_size_fd(fd));
_cleanup_free_ void *buf = ASSERT_NOT_NULL(malloc0(buflen));
struct msghdr msg = {
.msg_iov = &IOVEC_MAKE(buf, buflen),
.msg_iovlen = 1,
};
ssize_t len = ASSERT_OK_ERRNO(recvmsg_safe(fd, &msg, MSG_DONTWAIT));
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
ASSERT_OK(dhcp_message_parse(
&IOVEC_MAKE(buf, len),
BOOTREQUEST,
&xid,
ARPHRD_ETHER,
&hw_addr,
&m));
ASSERT_EQ(m->header.hops, 1u);
sd_dhcp_relay_interface *downstream;
ASSERT_OK(downstream_get(relay, m, &downstream));
ASSERT_FALSE(downstream->upstream);
ASSERT_EQ(m->header.giaddr, downstream->gateway_address.s_addr);
_cleanup_(tlv_unrefp) TLV *agent_info = NULL;
ASSERT_OK(dhcp_message_get_option_sub_tlv(
m,
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION,
TLV_DHCP4_SUBOPTION,
&agent_info));
void *key, *value;
HASHMAP_FOREACH_KEY(value, key, agent_info->entries) {
uint32_t tag = PTR_TO_UINT32(key);
_cleanup_(iovec_done) struct iovec iov = {};
ASSERT_OK(tlv_get_alloc(agent_info, tag, &iov));
switch (tag) {
case SD_DHCP_RELAY_AGENT_CIRCUIT_ID:
ASSERT_TRUE(iovec_equal(&iov, &downstream->circuit_id));
break;
case SD_DHCP_RELAY_AGENT_REMOTE_ID:
ASSERT_TRUE(iovec_equal(&iov, &relay->remote_id));
break;
case SD_DHCP_RELAY_AGENT_LINK_SELECTION:
ASSERT_TRUE(iovec_equal(&iov, &IOVEC_MAKE(&downstream->address, sizeof(struct in_addr))));
break;
case SD_DHCP_RELAY_AGENT_FLAGS: {
ASSERT_TRUE(relay->server_identifier_override);
ASSERT_EQ(iov.iov_len, 1u);
uint8_t flags = *(uint8_t*) iov.iov_base;
/* In the unit test, we cannot detect if the message is broadcast or unicast because
* AF_UNIX is used; therefore, the unicast flag is not set. */
ASSERT_FALSE(FLAGS_SET(flags, DHCP_RELAY_AGENT_FLAG_UNICAST));
break;
}
case SD_DHCP_RELAY_AGENT_SERVER_IDENTIFIER_OVERRIDE:
ASSERT_TRUE(relay->server_identifier_override);
ASSERT_TRUE(iovec_equal(&iov, &IOVEC_MAKE(&downstream->address, sizeof(struct in_addr))));
break;
case SD_DHCP_RELAY_AGENT_VIRTUAL_SUBNET_SELECTION:
ASSERT_TRUE(iovec_equal(&iov, &downstream->vss));
break;
default:
assert_not_reached();
}
}
uint8_t t;
ASSERT_OK(dhcp_message_get_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, &t));
switch (fake_server_message_count) {
case 1:
ASSERT_EQ(t, DHCP_DISCOVER);
break;
case 2:
ASSERT_EQ(t, DHCP_REQUEST);
break;
case 3:
ASSERT_EQ(t, DHCP_RELEASE);
if (fake_client_message_count == 3)
ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 0));
break;
default:
assert_not_reached();
}
return 0;
}
static void fake_client_verify(int fd, uint8_t type, bool raw) {
ssize_t buflen = ASSERT_OK_POSITIVE(next_datagram_size_fd(fd));
_cleanup_free_ void *buf = ASSERT_NOT_NULL(malloc0(buflen));
struct msghdr msg = {
.msg_iov = &IOVEC_MAKE(buf, buflen),
.msg_iovlen = 1,
};
ssize_t len = ASSERT_OK_ERRNO(recvmsg_safe(fd, &msg, MSG_DONTWAIT));
struct iovec payload = IOVEC_MAKE(buf, len);
if (raw)
ASSERT_OK(udp_packet_verify(&payload, DHCP_PORT_CLIENT, /* checksum= */ true, &payload));
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
ASSERT_OK(dhcp_message_parse(
&payload,
BOOTREPLY,
&xid,
ARPHRD_ETHER,
&hw_addr,
&m));
ASSERT_EQ(m->header.hops, 0u);
ASSERT_FALSE(dhcp_message_has_option(m, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION));
uint8_t t;
ASSERT_OK(dhcp_message_get_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, &t));
ASSERT_EQ(t, type);
}
static int fake_client_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
fake_client_message_count++;
log_debug("%s: count=%u", __func__, fake_client_message_count);
switch (fake_client_message_count) {
case 1:
fake_client_verify(fd, DHCP_OFFER, /* raw= */ true);
break;
case 2:
fake_client_verify(fd, DHCP_ACK, /* raw= */ false);
break;
case 3:
fake_client_verify(fd, DHCP_NAK, /* raw= */ false);
if (fake_server_message_count == 3)
ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 0));
break;
default:
assert_not_reached();
}
return 0;
}
TEST(forwarding) {
union in_addr_union a;
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
ASSERT_OK(sd_event_new(&e));
_cleanup_(sd_dhcp_relay_unrefp) sd_dhcp_relay *relay = NULL;
ASSERT_OK(sd_dhcp_relay_new(&relay));
ASSERT_OK(sd_dhcp_relay_attach_event(relay, e, SD_EVENT_PRIORITY_NORMAL));
ASSERT_OK(in_addr_from_string(AF_INET, "198.51.100.1", &a));
ASSERT_OK(sd_dhcp_relay_set_server_address(relay, &a.in));
ASSERT_OK(sd_dhcp_relay_set_remote_id(relay, &IOVEC_MAKE_STRING("test-remote-id")));
ASSERT_OK(sd_dhcp_relay_set_server_identifier_override(relay, true));
/* Setting up an upstream interface. */
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *upstream = NULL;
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4242, /* is_upstream= */ true, &upstream));
ASSERT_OK(sd_dhcp_relay_interface_set_ifname(upstream, "test-upstream"));
ASSERT_OK_ZERO(sd_dhcp_relay_interface_get_address(upstream, /* ret_address= */ NULL, /* ret_prefixlen= */ NULL));
ASSERT_OK(in_addr_from_string(AF_INET, "198.51.100.2", &a));
ASSERT_OK(sd_dhcp_relay_interface_set_address(upstream, &a.in, 24));
ASSERT_OK_POSITIVE(sd_dhcp_relay_interface_get_address(upstream, /* ret_address= */ NULL, /* ret_prefixlen= */ NULL));
_cleanup_close_pair_ int upstream_fd[2] = EBADF_PAIR;
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, upstream_fd));
upstream->socket_fd = TAKE_FD(upstream_fd[0]);
ASSERT_OK(sd_dhcp_relay_interface_start(upstream));
/* IO event source for the server side. */
_cleanup_(sd_event_source_unrefp) sd_event_source *fake_server = NULL;
ASSERT_OK(sd_event_add_io(e, &fake_server, upstream_fd[1], EPOLLIN, fake_server_handler, relay));
ASSERT_OK(sd_event_source_set_priority(fake_server, SD_EVENT_PRIORITY_IMPORTANT));
ASSERT_OK(sd_event_source_set_description(fake_server, "fake-server-io-event-source"));
/* Setting up a downstream interface. */
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *downstream = NULL;
ASSERT_OK(sd_dhcp_relay_add_interface(relay, 4343, /* is_upstream= */ false, &downstream));
ASSERT_OK(sd_dhcp_relay_interface_set_ifname(downstream, "test-downstream"));
ASSERT_OK(in_addr_from_string(AF_INET, "192.0.2.1", &a));
ASSERT_OK(sd_dhcp_relay_interface_set_address(downstream, &a.in, 24));
ASSERT_OK(in_addr_from_string(AF_INET, "203.0.113.1", &a));
ASSERT_OK(sd_dhcp_relay_downstream_set_gateway_address(downstream, &a.in));
ASSERT_OK(sd_dhcp_relay_downstream_set_circuit_id(downstream, &IOVEC_MAKE_STRING("test-circuit-id")));
ASSERT_OK(sd_dhcp_relay_downstream_set_virtual_subnet_selection(downstream, &IOVEC_MAKE_STRING("test-virtual-net")));
_cleanup_close_pair_ int downstream_fd[2] = EBADF_PAIR;
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, downstream_fd));
downstream->socket_fd = TAKE_FD(downstream_fd[0]);
ASSERT_OK(sd_dhcp_relay_interface_start(downstream));
/* IO event source for the client side. */
_cleanup_(sd_event_source_unrefp) sd_event_source *fake_client = NULL;
ASSERT_OK(sd_event_add_io(e, &fake_client, downstream_fd[1], EPOLLIN, fake_client_handler, relay));
ASSERT_OK(sd_event_source_set_priority(fake_client, SD_EVENT_PRIORITY_NORMAL));
ASSERT_OK(sd_event_source_set_description(fake_client, "fake-client-io-event-source"));
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
ASSERT_OK(dhcp_message_new(&m));
ASSERT_OK(dhcp_message_init_header(
m,
BOOTREQUEST,
xid,
ARPHRD_ETHER,
&hw_addr));
/* Test: downstream -> upstream */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_DISCOVER));
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_REQUEST));
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
/* Invalid message (unexpected BOOTP operation). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 100));
m->header.op = BOOTREPLY;
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.op = BOOTREQUEST;
/* Invalid message (too large hops). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 101));
m->header.hops = 16;
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.hops = 0;
/* Invalid message (invalid giaddr). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 102));
m->header.giaddr = downstream->address.s_addr;
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.giaddr = INADDR_ANY;
/* Invalid message (unexpected relay agent information). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 103));
ASSERT_OK(dhcp_message_append_option_flag(m, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION));
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
dhcp_message_remove_option(m, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION);
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_RELEASE));
send_message(downstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
/* Test: upstream -> downstream */
m->header.op = BOOTREPLY;
m->header.giaddr = downstream->gateway_address.s_addr;
m->header.yiaddr = 0x12345678;
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_OFFER));
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_ACK));
m->header.ciaddr = 0x12345678;
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.ciaddr = 0;
/* Invalid message (unexpected BOOTP operation). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 200));
m->header.op = BOOTREQUEST;
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.op = BOOTREPLY;
/* Invalid message (NULL giaddr). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 201));
m->header.giaddr = INADDR_ANY;
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.giaddr = downstream->gateway_address.s_addr;
/* Invalid message (unexpected giaddr). */
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, 202));
m->header.giaddr = 1234567;
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
m->header.giaddr = downstream->gateway_address.s_addr;
ASSERT_OK(dhcp_message_append_option_u8(m, SD_DHCP_OPTION_MESSAGE_TYPE, DHCP_NAK));
send_message(upstream_fd[1], m);
dhcp_message_remove_option(m, SD_DHCP_OPTION_MESSAGE_TYPE);
ASSERT_OK(sd_event_loop(e));
}
DEFINE_TEST_MAIN(LOG_DEBUG);
+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();
+3 -1
View File
@@ -79,8 +79,10 @@ typedef struct sd_ipv4ll sd_ipv4ll;
typedef struct sd_dhcp_client sd_dhcp_client;
typedef struct sd_dhcp_lease sd_dhcp_lease;
typedef struct sd_dhcp_route sd_dhcp_route;
typedef struct sd_dns_resolver sd_dns_resolver;
typedef struct sd_dhcp_relay sd_dhcp_relay;
typedef struct sd_dhcp_relay_interface sd_dhcp_relay_interface;
typedef struct sd_dhcp_server sd_dhcp_server;
typedef struct sd_dns_resolver sd_dns_resolver;
typedef struct sd_ndisc sd_ndisc;
typedef struct sd_radv sd_radv;
typedef struct sd_dhcp6_client sd_dhcp6_client;
+1
View File
@@ -43,6 +43,7 @@ systemd_networkd_extract_sources = files(
'networkd-conf.c',
'networkd-dhcp-common.c',
'networkd-dhcp-prefix-delegation.c',
'networkd-dhcp-relay.c',
'networkd-dhcp-server-bus.c',
'networkd-dhcp-server-static-lease.c',
'networkd-dhcp-server.c',
+7
View File
@@ -19,6 +19,7 @@
#include "networkd-address.h"
#include "networkd-address-pool.h"
#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp-relay.h"
#include "networkd-dhcp-server.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
@@ -245,6 +246,9 @@ static Address* address_detach_impl(Address *address) {
if (address->network->dhcp_server_address == address)
address->network->dhcp_server_address = NULL;
if (address->network->dhcp_relay_agent_address == address)
address->network->dhcp_relay_agent_address = NULL;
address->network = NULL;
return address;
}
@@ -925,6 +929,8 @@ static int address_drop(Address *in, bool removed_by_us) {
ipv4acd_detach(link, address);
(void) link_dhcp_relay_address_dropped(link, address);
address_detach(address);
if (!removed_by_us) {
@@ -2517,5 +2523,6 @@ int network_drop_invalid_addresses(Network *network) {
if (r < 0)
return r;
network_adjust_dhcp_relay(network);
return 0;
}
+1
View File
@@ -22,6 +22,7 @@ int manager_parse_config_file(Manager *m) {
"DHCPv4\0"
"DHCPv6\0"
"DHCPServer\0"
"DHCPRelay\0"
"DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup,
CONFIG_PARSE_WARN,
+380
View File
@@ -0,0 +1,380 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/rtnetlink.h>
#include "sd-event.h"
#include "sd-id128.h"
#include "conf-parser.h"
#include "dhcp-relay-internal.h"
#include "hashmap.h"
#include "iovec-util.h"
#include "networkd-address.h"
#include "networkd-dhcp-relay.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-queue.h"
#include "string-table.h"
#include "string-util.h" /* IWYU pragma: keep */
#define DHCP_RELAY_APP_REMOTE_ID SD_ID128_MAKE(85,bb,eb,d2,b8,56,47,0b,b0,86,4c,f3,d3,9b,c1,b5)
void network_adjust_dhcp_relay(Network *network) {
assert(network);
assert(network->manager);
if (network->dhcp_relay_interface_mode < 0)
return;
if (network->bond) {
log_warning("%s: DHCPRelay= is enabled for bond slave. Disabling DHCP relay agent.",
network->filename);
network->dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID;
return;
}
if (network->dhcp_server) {
log_warning("%s: DHCPRelay= cannot be enabled when DHCPServer= is enabled. Disabling DHCP relay agent.",
network->filename);
network->dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID;
return;
}
if (FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
log_warning("%s: DHCPRelay= cannot be enabled when DHCPv4 client is enabled. Disabling DHCP relay agent.",
network->filename);
network->dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID;
return;
}
if (in4_addr_is_null(&network->manager->dhcp_relay_server_address)) {
log_warning("%s: DHCPRelay= is enabled, but [DHCPRelay] ServerAddress= in networkd.conf is not configured. Disabling DHCP relay agent.",
network->filename);
network->dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID;
return;
}
Address *a;
ORDERED_HASHMAP_FOREACH(a, network->addresses_by_section) {
assert(!section_is_invalid(a->section));
if (a->family != AF_INET)
continue;
if (in4_addr_is_set(&a->in_addr_peer.in))
continue;
if (in4_addr_is_set(&network->dhcp_relay_agent_address_in_addr)) {
if (!in4_addr_equal(&a->in_addr.in, &network->dhcp_relay_agent_address_in_addr))
continue;
} else {
if (in4_addr_is_localhost(&a->in_addr.in))
continue;
if (in4_addr_is_link_local(&a->in_addr.in))
continue;
if (a->scope != RT_SCOPE_UNIVERSE)
continue;
}
network->dhcp_relay_agent_address = a;
break;
}
if (!network->dhcp_relay_agent_address) {
if (in4_addr_is_set(&network->dhcp_relay_agent_address_in_addr))
log_warning("%s: Configured AgentAddress=%s not found among static addresses. Disabling DHCP relay agent.",
network->filename, IN4_ADDR_TO_STRING(&network->dhcp_relay_agent_address_in_addr));
else
log_warning("%s: DHCPRelay= is enabled, but no suitable static address configured. Disabling DHCP relay agent.",
network->filename);
network->dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID;
return;
}
}
static int manager_configure_dhcp_relay(Manager *manager) {
int r;
assert(manager);
assert(manager->event);
if (manager->dhcp_relay)
return 0;
if (in4_addr_is_null(&manager->dhcp_relay_server_address))
return -EADDRNOTAVAIL;
_cleanup_(sd_dhcp_relay_unrefp) sd_dhcp_relay *relay = NULL;
r = sd_dhcp_relay_new(&relay);
if (r < 0)
return r;
r = sd_dhcp_relay_attach_event(relay, manager->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return r;
r = sd_dhcp_relay_set_server_address(relay, &manager->dhcp_relay_server_address);
if (r < 0)
return r;
if (iovec_is_set(&manager->dhcp_relay_remote_id)) {
r = sd_dhcp_relay_set_remote_id(relay, &manager->dhcp_relay_remote_id);
if (r < 0)
return r;
} else {
sd_id128_t id;
r = sd_id128_get_machine_app_specific(DHCP_RELAY_APP_REMOTE_ID, &id);
if (r < 0)
return r;
r = sd_dhcp_relay_set_remote_id(relay, &IOVEC_MAKE_STRING(SD_ID128_TO_STRING(id)));
if (r < 0)
return r;
}
r = sd_dhcp_relay_set_server_identifier_override(relay, manager->dhcp_relay_override_server_id);
if (r < 0)
return r;
r = dhcp_relay_set_extra_options(relay, &manager->dhcp_relay_extra_options);
if (r < 0)
return r;
manager->dhcp_relay = TAKE_PTR(relay);
return 0;
}
static int link_configure_dhcp_relay(Link *link) {
int r;
assert(link);
assert(link->manager);
assert(link->network);
assert(!link->dhcp_relay_interface);
assert(link->network->dhcp_relay_agent_address);
assert(link->network->dhcp_relay_interface_mode >= 0 && link->network->dhcp_relay_interface_mode < _DHCP_RELAY_INTERFACE_MAX);
r = manager_configure_dhcp_relay(link->manager);
if (r < 0)
return r;
bool upstream = link->network->dhcp_relay_interface_mode == DHCP_RELAY_INTERFACE_UPSTREAM;
_cleanup_(sd_dhcp_relay_interface_unrefp) sd_dhcp_relay_interface *interface = NULL;
r = sd_dhcp_relay_add_interface(link->manager->dhcp_relay, link->ifindex, upstream, &interface);
if (r < 0)
return r;
r = sd_dhcp_relay_interface_set_ifname(interface, link->ifname);
if (r < 0)
return r;
r = sd_dhcp_relay_interface_set_address(
interface,
&link->network->dhcp_relay_agent_address->in_addr.in,
link->network->dhcp_relay_agent_address->prefixlen);
if (r < 0)
return r;
if (upstream) {
r = sd_dhcp_relay_upstream_set_priority(interface, link->network->dhcp_relay_interface_priority);
if (r < 0)
return r;
} else {
if (in4_addr_is_set(&link->network->dhcp_relay_gateway_address))
r = sd_dhcp_relay_downstream_set_gateway_address(interface, &link->network->dhcp_relay_gateway_address);
else
r = sd_dhcp_relay_downstream_set_gateway_address(interface, &link->network->dhcp_relay_agent_address->in_addr.in);
if (r < 0)
return r;
if (iovec_is_set(&link->network->dhcp_relay_circuit_id))
r = sd_dhcp_relay_downstream_set_circuit_id(interface, &link->network->dhcp_relay_circuit_id);
else
r = sd_dhcp_relay_downstream_set_circuit_id(interface, &IOVEC_MAKE_STRING(link->ifname));
if (r < 0)
return r;
r = sd_dhcp_relay_downstream_set_virtual_subnet_selection(interface, &link->network->dhcp_relay_vss);
if (r < 0)
return r;
r = downstream_set_extra_options(interface, &link->network->dhcp_relay_extra_options);
if (r < 0)
return r;
}
link->dhcp_relay_interface = TAKE_PTR(interface);
if (link->network->dhcp_relay_interface_mode == DHCP_RELAY_INTERFACE_COMPAT &&
!link->dhcp_relay_interface_compat) {
r = sd_dhcp_relay_add_interface(
link->manager->dhcp_relay,
DHCP_RELAY_IFINDEX_UNBOUND,
/* is_upstream= */ true,
&link->dhcp_relay_interface_compat);
if (r < 0)
return r;
}
return 0;
}
static bool dhcp_relay_is_ready_to_configure(Link *link) {
assert(link);
assert(link->network);
if (!link_is_ready_to_configure(link, /* allow_unmanaged= */ false))
return false;
if (!link_has_carrier(link))
return false;
if (!link->static_addresses_configured)
return false;
Address *a;
if (address_get(link, link->network->dhcp_relay_agent_address, &a) < 0)
return false;
if (!address_is_ready(a))
return false;
return true;
}
static int dhcp_relay_process_request(Request *req, Link *link, void *userdata) {
int r;
assert(link);
if (!dhcp_relay_is_ready_to_configure(link))
return 0;
r = link_configure_dhcp_relay(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure DHCP relay agent: %m");
log_link_debug(link, "DHCP relay agent is configured.");
r = link_start_dhcp_relay(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start DHCP relay agent: %m");
return 1;
}
int link_request_dhcp_relay(Link *link) {
int r;
assert(link);
assert(link->manager);
if (link->manager->state != MANAGER_RUNNING)
return 0;
if (!link->network)
return 0;
if (link->network->dhcp_relay_interface_mode < 0)
return 0;
if (link->dhcp_relay_interface)
return 0;
r = link_queue_request(link, REQUEST_TYPE_DHCP_RELAY, dhcp_relay_process_request, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to request configuring of the DHCP relay agent: %m");
log_link_debug(link, "Requested configuring of the DHCP relay agent.");
return 0;
}
int link_start_dhcp_relay(Link *link) {
int r;
assert(link);
assert(link->manager);
if (!link->dhcp_relay_interface)
return 0; /* Not configured yet. */
if (!link_has_carrier(link))
return 0;
r = sd_dhcp_relay_interface_start(link->dhcp_relay_interface);
if (r < 0)
return r;
if (link->dhcp_relay_interface_compat) {
r = sd_dhcp_relay_interface_start(link->dhcp_relay_interface_compat);
if (r < 0)
return r;
}
log_link_debug(link, "Relaying DHCPv4 messages.");
return 0;
}
int link_dhcp_relay_address_dropped(Link *link, const Address *address) {
int r;
assert(link);
assert(link->manager);
assert(address);
/* This is called when an address is removed from the interface. */
if (link->manager->state != MANAGER_RUNNING)
return 0;
if (!link->network)
return 0;
if (!link->dhcp_relay_interface)
return 0;
if (address->family != AF_INET)
return 0;
struct in_addr a;
uint8_t prefixlen;
r = sd_dhcp_relay_interface_get_address(link->dhcp_relay_interface, &a, &prefixlen);
if (r <= 0)
return r;
if (!in4_addr_equal(&address->in_addr.in, &a))
return 0;
if (address->prefixlen != prefixlen)
return 0;
r = sd_dhcp_relay_interface_stop(link->dhcp_relay_interface);
if (r < 0)
return r;
link->dhcp_relay_interface = sd_dhcp_relay_interface_unref(link->dhcp_relay_interface);
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return 0;
/* The address may be reconfigured later. Let's reconfigure DHCP relay interface when the address comes back. */
return link_request_dhcp_relay(link);
}
static const char * const dhcp_relay_interface_mode_table[_DHCP_RELAY_INTERFACE_MAX] = {
[DHCP_RELAY_INTERFACE_UPSTREAM] = "upstream",
[DHCP_RELAY_INTERFACE_DOWNSTREAM] = "downstream",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_relay_interface_mode, DHCPRelayInterfaceMode);
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(
config_parse_dhcp_relay_interface_mode,
dhcp_relay_interface_mode,
DHCPRelayInterfaceMode,
_DHCP_RELAY_INTERFACE_INVALID);
+20
View File
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "networkd-forward.h"
typedef enum DHCPRelayInterfaceMode {
DHCP_RELAY_INTERFACE_UPSTREAM,
DHCP_RELAY_INTERFACE_DOWNSTREAM,
DHCP_RELAY_INTERFACE_COMPAT,
_DHCP_RELAY_INTERFACE_MAX,
_DHCP_RELAY_INTERFACE_INVALID = -EINVAL,
} DHCPRelayInterfaceMode;
void network_adjust_dhcp_relay(Network *network);
int link_request_dhcp_relay(Link *link);
int link_start_dhcp_relay(Link *link);
int link_dhcp_relay_address_dropped(Link *link, const Address *address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_relay_interface_mode);
+4 -13
View File
@@ -30,9 +30,6 @@ static int property_get_leases(
if (!s)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname);
if (sd_dhcp_server_is_in_relay_mode(s))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has DHCP relay agent active.", l->ifname);
r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)");
if (r < 0)
return r;
@@ -82,15 +79,12 @@ static int property_get_pool_size(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Link *l = ASSERT_PTR(userdata);
sd_dhcp_server *s;
uint32_t v;
assert(reply);
s = l->dhcp_server;
v = s && !sd_dhcp_server_is_in_relay_mode(s) ? s->pool_size : UINT32_MAX;
uint32_t v = l->dhcp_server ? l->dhcp_server->pool_size : UINT32_MAX;
return sd_bus_message_append_basic(reply, 'u', &v);
}
@@ -102,15 +96,12 @@ static int property_get_pool_offset(
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Link *l = ASSERT_PTR(userdata);
sd_dhcp_server *s;
uint32_t v;
assert(reply);
s = l->dhcp_server;
v = s && !sd_dhcp_server_is_in_relay_mode(s) ? s->pool_offset : UINT32_MAX;
uint32_t v = l->dhcp_server ? l->dhcp_server->pool_offset : UINT32_MAX;
return sd_bus_message_append_basic(reply, 'u', &v);
}
+26 -49
View File
@@ -16,6 +16,7 @@
#include "fileio.h"
#include "hashmap.h"
#include "hostname-setup.h"
#include "iovec-util.h"
#include "network-common.h"
#include "networkd-address.h"
#include "networkd-dhcp-server.h"
@@ -66,6 +67,31 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
return 0;
}
if (in4_addr_is_set(&network->dhcp_relay_target_address)) {
if (!in4_addr_is_set(&network->manager->dhcp_relay_server_address)) {
/* [DHCPServer] RelayTarget= in .network file is replaced with
* [DHCPRelay] ServerAddress= in networkd.conf. */
network->manager->dhcp_relay_server_address = network->dhcp_relay_target_address;
/* [DHCPServer] RelayAgentRemoteId= in .network file is replaced with
* [DHCPRelay] RemoteId= in networkd.conf. */
if (!iovec_is_set(&network->manager->dhcp_relay_remote_id)) {
iovec_done(&network->manager->dhcp_relay_remote_id);
network->manager->dhcp_relay_remote_id = TAKE_STRUCT(network->dhcp_relay_remote_id);
}
}
/* Copy [DHCPServer] ServerAddress= to [DHCPRelay] AgentAddress= if unspecified. */
if (!in4_addr_is_set(&network->dhcp_relay_agent_address_in_addr))
network->dhcp_relay_agent_address_in_addr = network->dhcp_server_address_in_addr;
/* Assume this interface acts as a downstream interface of the DHCP relay agent. Also,
* configure a catch-all upstream socket. */
network->dhcp_relay_interface_mode = DHCP_RELAY_INTERFACE_COMPAT;
network->dhcp_server = false;
return 0;
}
assert(network->dhcp_server_address_prefixlen <= 32);
if (network->dhcp_server_address_prefixlen == 0) {
@@ -160,9 +186,6 @@ static DHCPServerPersistLeases link_get_dhcp_server_persist_leases(Link *link) {
assert(link->manager);
assert(link->network);
if (in4_addr_is_set(&link->network->dhcp_server_relay_target))
return DHCP_SERVER_PERSIST_LEASES_NO; /* On relay mode. Nothing saved in the persistent storage. */
if (link->network->dhcp_server_persist_leases >= 0)
return link->network->dhcp_server_persist_leases;
@@ -571,7 +594,6 @@ static int dhcp4_server_configure(Link *link) {
DHCPStaticLease *static_lease;
Link *uplink = NULL;
Address *address;
bool bind_to_interface;
int r;
assert(link);
@@ -681,19 +703,6 @@ static int dhcp4_server_configure(Link *link) {
return log_link_error_errno(link, r, "Failed to set router address for DHCP server: %m");
}
r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m");
bind_to_interface = sd_dhcp_server_is_in_relay_mode(link->dhcp_server) ? false : link->network->dhcp_server_bind_to_interface;
r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, bind_to_interface);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m");
r = sd_dhcp_server_set_relay_agent_information(link->dhcp_server, link->network->dhcp_server_relay_agent_circuit_id, link->network->dhcp_server_relay_agent_remote_id);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set agent circuit/remote id for DHCP server: %m");
if (link->network->dhcp_server_emit_timezone) {
_cleanup_free_ char *buffer = NULL;
const char *tz = NULL;
@@ -815,38 +824,6 @@ int link_request_dhcp_server(Link *link) {
return 0;
}
int config_parse_dhcp_server_relay_agent_suboption(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char **suboption_value = data;
assert(filename);
assert(lvalue);
assert(rvalue);
if (isempty(rvalue)) {
*suboption_value = mfree(*suboption_value);
return 0;
}
const char *p = startswith(rvalue, "string:");
if (!p) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Failed to parse %s=%s'. Invalid format, ignoring.", lvalue, rvalue);
return 0;
}
return free_and_strdup(suboption_value, empty_to_null(p));
}
int config_parse_dhcp_server_emit(
const char *unit,
const char *filename,
-1
View File
@@ -18,7 +18,6 @@ int link_request_dhcp_server(Link *link);
int link_start_dhcp4_server(Link *link);
void manager_toggle_dhcp4_server_state(Manager *manager, bool start);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ipv6_only_preferred);
+4
View File
@@ -45,6 +45,10 @@ DHCPv6.UseDomains, config_parse_use_domains,
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp6_duid)
DHCPServer.PersistLeases, config_parse_dhcp_server_persist_leases, 0, offsetof(Manager, dhcp_server_persist_leases)
DHCPRelay.ServerAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Manager, dhcp_relay_server_address)
DHCPRelay.OverrideServerIdentifier, config_parse_bool, 0, offsetof(Manager, dhcp_relay_override_server_id)
DHCPRelay.RemoteId, config_parse_dhcp_option, /* check_length= */ true, offsetof(Manager, dhcp_relay_remote_id)
DHCPRelay.ExtraOption, config_parse_dhcp_option_tlv, 0, offsetof(Manager, dhcp_relay_extra_options)
/* Deprecated */
DHCP.DUIDType, config_parse_manager_duid_type, 0, 0
DHCP.DUIDRawData, config_parse_manager_duid_rawdata, 0, 0
+1 -2
View File
@@ -855,8 +855,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r < 0)
return 0;
if (streq(interface, "org.freedesktop.network1.DHCPServer") &&
(!link->dhcp_server || sd_dhcp_server_is_in_relay_mode(link->dhcp_server)))
if (streq(interface, "org.freedesktop.network1.DHCPServer") && !link->dhcp_server)
return 0;
if (streq(interface, "org.freedesktop.network1.DHCPv4Client") && !link->dhcp_client)
+28
View File
@@ -10,6 +10,7 @@
#include "sd-bus.h"
#include "sd-dhcp-client.h"
#include "sd-dhcp-relay.h"
#include "sd-dhcp-server.h"
#include "sd-dhcp6-client.h"
#include "sd-dhcp6-lease.h"
@@ -41,6 +42,7 @@
#include "networkd-bridge-mdb.h"
#include "networkd-bridge-vlan.h"
#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp-relay.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
@@ -240,6 +242,8 @@ static void link_free_engines(Link *link) {
if (!link)
return;
link->dhcp_relay_interface = sd_dhcp_relay_interface_unref(link->dhcp_relay_interface);
link->dhcp_relay_interface_compat = sd_dhcp_relay_interface_unref(link->dhcp_relay_interface_compat);
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
@@ -424,6 +428,14 @@ int link_stop_engines(Link *link, bool may_keep_dynamic) {
ndisc_flush(link);
}
r = sd_dhcp_relay_interface_stop(link->dhcp_relay_interface);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCP relay agent: %m"));
r = sd_dhcp_relay_interface_stop(link->dhcp_relay_interface_compat);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCP relay agent (compat): %m"));
r = sd_dhcp_server_stop(link->dhcp_server);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 server: %m"));
@@ -739,6 +751,10 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
log_link_debug(link, "Acquiring IPv4 link-local address.");
}
r = link_start_dhcp_relay(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start DHCP relay agent: %m");
r = link_start_dhcp4_server(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
@@ -1196,6 +1212,8 @@ static int link_drop_dynamic_config(Link *link, Network *network) {
RET_GATHER(r, link_drop_dhcp4_config(link, network));
RET_GATHER(r, link_drop_dhcp6_config(link, network));
RET_GATHER(r, link_drop_dhcp_pd_config(link, network));
link->dhcp_relay_interface = sd_dhcp_relay_interface_unref(link->dhcp_relay_interface);
link->dhcp_relay_interface_compat = sd_dhcp_relay_interface_unref(link->dhcp_relay_interface_compat);
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx); /* TODO: keep the received neighbors. */
link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx);
@@ -1314,6 +1332,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
r = link_request_dhcp_relay(link);
if (r < 0)
return r;
r = link_request_dhcp_server(link);
if (r < 0)
return r;
@@ -2645,6 +2667,12 @@ static int link_update_name(Link *link, sd_netlink_message *message) {
return log_link_debug_errno(link, r, "Failed to update interface name in NDisc: %m");
}
if (link->dhcp_relay_interface) {
r = sd_dhcp_relay_interface_set_ifname(link->dhcp_relay_interface, link->ifname);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to update interface name in DHCP relay interface: %m");
}
if (link->dhcp_server) {
r = sd_dhcp_server_set_ifname(link->dhcp_server, link->ifname);
if (r < 0)
+2
View File
@@ -143,6 +143,8 @@ typedef struct Link {
bool bridge_vlan_set:1;
bool bearer_configured:1;
sd_dhcp_relay_interface *dhcp_relay_interface;
sd_dhcp_relay_interface *dhcp_relay_interface_compat;
sd_dhcp_server *dhcp_server;
sd_ndisc *ndisc;
+7
View File
@@ -5,6 +5,7 @@
#include <sys/socket.h>
#include "sd-bus.h"
#include "sd-dhcp-relay.h"
#include "sd-event.h"
#include "sd-netlink.h"
#include "sd-resolve.h"
@@ -25,6 +26,7 @@
#include "errno-util.h"
#include "fd-util.h"
#include "initrd-util.h"
#include "iovec-util.h"
#include "mount-util.h"
#include "netlink-internal.h"
#include "netlink-util.h"
@@ -701,6 +703,7 @@ int manager_new(Manager **ret, bool test_mode) {
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
.dhcp_relay_extra_options = TLV_INIT(TLV_DHCP4_SUBOPTION),
.dhcp_server_persist_leases = DHCP_SERVER_PERSIST_LEASES_YES,
.serialization_fd = -EBADF,
.ip_forwarding = { -1, -1, },
@@ -760,6 +763,10 @@ Manager* manager_free(Manager *m) {
sd_netlink_unref(m->nfnl);
sd_resolve_unref(m->resolve);
iovec_done(&m->dhcp_relay_remote_id);
tlv_done(&m->dhcp_relay_extra_options);
sd_dhcp_relay_unref(m->dhcp_relay);
m->routes = set_free(m->routes);
m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
+8
View File
@@ -3,6 +3,7 @@
#include "networkd-forward.h"
#include "networkd-network.h"
#include "tlv-util.h"
typedef enum ManagerState {
MANAGER_RUNNING,
@@ -74,6 +75,13 @@ typedef struct Manager {
bool has_product_uuid;
bool product_uuid_requested;
/* DHCP relay agent */
sd_dhcp_relay *dhcp_relay;
struct in_addr dhcp_relay_server_address;
bool dhcp_relay_override_server_id;
struct iovec dhcp_relay_remote_id;
TLV dhcp_relay_extra_options;
char* dynamic_hostname;
char* dynamic_timezone;
+11 -4
View File
@@ -132,6 +132,7 @@ Network.VLAN, config_parse_stacked_netdev,
Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names)
Network.Xfrm, config_parse_stacked_netdev, NETDEV_KIND_XFRM, offsetof(Network, stacked_netdev_names)
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
Network.DHCPRelay, config_parse_dhcp_relay_interface_mode, 0, offsetof(Network, dhcp_relay_interface_mode)
Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
Network.LinkLocalAddressing, config_parse_link_local_address_family, 0, offsetof(Network, link_local)
Network.IPv6LinkLocalAddressGenerationMode, config_parse_ipv6_link_local_address_gen_mode, 0, offsetof(Network, ipv6ll_address_gen_mode)
@@ -360,11 +361,17 @@ IPv6AcceptRA.RouteDenyList, config_parse_in_addr_prefixes,
IPv6AcceptRA.Token, config_parse_address_generation_type, 0, offsetof(Network, ndisc_tokens)
IPv6AcceptRA.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, ndisc_netlabel)
IPv6AcceptRA.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, ndisc_nft_set_context)
DHCPRelay.AgentAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_relay_agent_address_in_addr)
DHCPRelay.GatewayAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_relay_gateway_address)
DHCPRelay.CircuitId, config_parse_dhcp_option, /* check_length= */ true, offsetof(Network, dhcp_relay_circuit_id)
DHCPRelay.VirtualSubnetSelection, config_parse_dhcp_option, /* check_length= */ true, offsetof(Network, dhcp_relay_vss)
DHCPRelay.ExtraOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_relay_extra_options)
DHCPRelay.InterfacePriority, config_parse_int, 0, offsetof(Network, dhcp_relay_interface_priority)
DHCPServer.ServerAddress, config_parse_dhcp_server_address, 0, 0
DHCPServer.UplinkInterface, config_parse_uplink, 0, 0
DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_relay_target)
DHCPServer.RelayAgentCircuitId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_circuit_id)
DHCPServer.RelayAgentRemoteId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_remote_id)
DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_relay_target_address) /* deprecated */
DHCPServer.RelayAgentCircuitId, config_parse_dhcp_option, /* check_length= */ true, offsetof(Network, dhcp_relay_circuit_id) /* deprecated */
DHCPServer.RelayAgentRemoteId, config_parse_dhcp_option, /* check_length= */ true, offsetof(Network, dhcp_relay_remote_id) /* deprecated */
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.IPv6OnlyPreferredSec, config_parse_dhcp_server_ipv6_only_preferred, 0, offsetof(Network, dhcp_server_ipv6_only_preferred_usec)
@@ -390,7 +397,7 @@ DHCPServer.PoolOffset, config_parse_uint32,
DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
DHCPServer.SendOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_server_extra_options)
DHCPServer.SendVendorOption, config_parse_dhcp_option_tlv, 0, offsetof(Network, dhcp_server_vendor_options)
DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface)
DHCPServer.BindToInterface, config_parse_warn_compat, DISABLED_LEGACY, 0
DHCPServer.BootServerAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_boot_server_address)
DHCPServer.BootServerName, config_parse_dns_name, 0, offsetof(Network, dhcp_server_boot_server_name)
DHCPServer.BootFilename, config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename)
+11 -3
View File
@@ -7,6 +7,7 @@
#include "conf-files.h"
#include "conf-parser.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "net-condition.h"
#include "netdev/macvlan.h"
#include "netif-sriov.h"
@@ -433,7 +434,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_pd_subnet_id = -1,
.dhcp_pd_route_metric = DHCP6PD_ROUTE_METRIC,
.dhcp_server_bind_to_interface = true,
.dhcp_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID,
.dhcp_relay_extra_options = TLV_INIT(TLV_DHCP4_SUBOPTION),
.dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
.dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
.dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
@@ -547,6 +550,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"DHCPv6\0"
"DHCPv6PrefixDelegation\0" /* compat */
"DHCPPrefixDelegation\0"
"DHCPRelay\0"
"DHCPServer\0"
"DHCPServerStaticLease\0"
"IPv6AcceptRA\0"
@@ -767,9 +771,13 @@ static Network *network_free(Network *network) {
ordered_set_free(network->route_domains);
set_free(network->dnssec_negative_trust_anchors);
/* DHCP relay agent */
iovec_done(&network->dhcp_relay_remote_id);
iovec_done(&network->dhcp_relay_circuit_id);
iovec_done(&network->dhcp_relay_vss);
tlv_done(&network->dhcp_relay_extra_options);
/* DHCP server */
free(network->dhcp_server_relay_agent_circuit_id);
free(network->dhcp_server_relay_agent_remote_id);
free(network->dhcp_server_boot_server_name);
free(network->dhcp_server_boot_filename);
free(network->dhcp_server_timezone);
+13 -4
View File
@@ -12,6 +12,7 @@
#include "network-util.h"
#include "networkd-bridge-vlan.h"
#include "networkd-dhcp-common.h"
#include "networkd-dhcp-relay.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
@@ -208,17 +209,25 @@ typedef struct Network {
uint32_t dhcp6_route_table;
bool dhcp6_route_table_set;
/* DHCP Relay Agent Support */
DHCPRelayInterfaceMode dhcp_relay_interface_mode;
Address *dhcp_relay_agent_address;
struct in_addr dhcp_relay_target_address; /* for deprecated DHCPServer.RelayTarget= */
struct in_addr dhcp_relay_agent_address_in_addr;
struct in_addr dhcp_relay_gateway_address;
struct iovec dhcp_relay_remote_id; /* for deprecated DHCPServer.RelayAgentRemoteId= */
struct iovec dhcp_relay_circuit_id;
struct iovec dhcp_relay_vss;
TLV dhcp_relay_extra_options;
int dhcp_relay_interface_priority;
/* DHCP Server Support */
bool dhcp_server;
bool dhcp_server_bind_to_interface;
unsigned char dhcp_server_address_prefixlen;
struct in_addr dhcp_server_address_in_addr;
const Address *dhcp_server_address;
int dhcp_server_uplink_index;
char *dhcp_server_uplink_name;
struct in_addr dhcp_server_relay_target;
char *dhcp_server_relay_agent_circuit_id;
char *dhcp_server_relay_agent_remote_id;
NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
bool dhcp_server_emit_router;
struct in_addr dhcp_server_router;
+1
View File
@@ -365,6 +365,7 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
[REQUEST_TYPE_ADDRESS_LABEL] = "address label",
[REQUEST_TYPE_BRIDGE_FDB] = "bridge FDB",
[REQUEST_TYPE_BRIDGE_MDB] = "bridge MDB",
[REQUEST_TYPE_DHCP_RELAY] = "DHCP relay agent",
[REQUEST_TYPE_DHCP_SERVER] = "DHCP server",
[REQUEST_TYPE_DHCP4_CLIENT] = "DHCPv4 client",
[REQUEST_TYPE_DHCP6_CLIENT] = "DHCPv6 client",
+1
View File
@@ -13,6 +13,7 @@ typedef enum RequestType {
REQUEST_TYPE_ADDRESS_LABEL,
REQUEST_TYPE_BRIDGE_FDB,
REQUEST_TYPE_BRIDGE_MDB,
REQUEST_TYPE_DHCP_RELAY,
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
+1
View File
@@ -29,6 +29,7 @@ _not_installed_headers = [
'sd-dhcp-duid.h',
'sd-dhcp-lease.h',
'sd-dhcp-protocol.h',
'sd-dhcp-relay.h',
'sd-dhcp-server-lease.h',
'sd-dhcp-server.h',
'sd-dhcp6-client.h',
+71
View File
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddhcprelayhfoo
#define foosddhcprelayhfoo
/***
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include <netinet/in.h>
#include <sys/uio.h>
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
typedef struct sd_event sd_event;
typedef struct sd_dhcp_relay sd_dhcp_relay;
typedef struct sd_dhcp_relay_interface sd_dhcp_relay_interface;
_SD_DECLARE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_relay);
_SD_DECLARE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_relay_interface);
int sd_dhcp_relay_new(sd_dhcp_relay **ret);
int sd_dhcp_relay_is_running(sd_dhcp_relay *relay);
int sd_dhcp_relay_attach_event(sd_dhcp_relay *relay, sd_event *event, int64_t priority);
int sd_dhcp_relay_detach_event(sd_dhcp_relay *relay);
sd_event *sd_dhcp_relay_get_event(sd_dhcp_relay *relay);
int sd_dhcp_relay_set_server_address(sd_dhcp_relay *relay, const struct in_addr *address);
int sd_dhcp_relay_get_server_address(sd_dhcp_relay *relay, struct in_addr *ret);
int sd_dhcp_relay_set_server_port(sd_dhcp_relay *relay, uint16_t port);
int sd_dhcp_relay_set_remote_id(sd_dhcp_relay *relay, const struct iovec *iov);
int sd_dhcp_relay_set_server_identifier_override(sd_dhcp_relay *relay, int b);
int sd_dhcp_relay_add_interface(sd_dhcp_relay *relay, int ifindex, int is_upstream, sd_dhcp_relay_interface **ret);
int sd_dhcp_relay_interface_set_ifname(sd_dhcp_relay_interface *interface, const char *ifname);
int sd_dhcp_relay_interface_get_ifname(sd_dhcp_relay_interface *interface, const char **ret);
int sd_dhcp_relay_interface_set_address(sd_dhcp_relay_interface *interface, const struct in_addr *address, uint8_t prefixlen);
int sd_dhcp_relay_interface_get_address(sd_dhcp_relay_interface *interface, struct in_addr *ret_address, uint8_t *ret_prefixlen);
int sd_dhcp_relay_interface_set_port(sd_dhcp_relay_interface *interface, uint16_t port);
int sd_dhcp_relay_interface_set_ip_service_type(sd_dhcp_relay_interface *interface, uint8_t type);
int sd_dhcp_relay_interface_is_running(sd_dhcp_relay_interface *interface);
int sd_dhcp_relay_interface_start(sd_dhcp_relay_interface *interface);
int sd_dhcp_relay_interface_stop(sd_dhcp_relay_interface *interface);
int sd_dhcp_relay_downstream_set_gateway_address(sd_dhcp_relay_interface *interface, const struct in_addr *address);
int sd_dhcp_relay_downstream_set_circuit_id(sd_dhcp_relay_interface *interface, const struct iovec *iov);
int sd_dhcp_relay_downstream_set_virtual_subnet_selection(sd_dhcp_relay_interface *interface, const struct iovec *iov);
int sd_dhcp_relay_upstream_set_priority(sd_dhcp_relay_interface *interface, int priority);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_relay, sd_dhcp_relay_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_relay_interface, sd_dhcp_relay_interface_unref);
_SD_END_DECLARATIONS;
#endif
-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);