]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: re-implement DHCP relay support with new sd-dhcp-relay
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 3 May 2026 00:56:45 +0000 (09:56 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 21 May 2026 07:55:59 +0000 (16:55 +0900)
15 files changed:
src/network/meson.build
src/network/networkd-address.c
src/network/networkd-conf.c
src/network/networkd-dhcp-relay.c [new file with mode: 0644]
src/network/networkd-dhcp-relay.h [new file with mode: 0644]
src/network/networkd-gperf.gperf
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-queue.c
src/network/networkd-queue.h

index 9844229ebdc58e8fe9b27b350dc2c2bb0875b73a..110af9511c11bc1023d4b60d7180cf8d27fb2d3c 100644 (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',
index 193c4a04b5f4456a1cad354fcb7edf9f2204dfcb..9737ae02881da51633a1b64a34db2b5f8a0b8981 100644 (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;
         }
@@ -893,6 +897,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) {
@@ -2485,5 +2491,6 @@ int network_drop_invalid_addresses(Network *network) {
         if (r < 0)
                 return r;
 
+        network_adjust_dhcp_relay(network);
         return 0;
 }
index bce16284f84e9a2e1e13a54b727e13743cca3c2f..ba3e2f5a43132af2f70496842980b0ffd78ebde5 100644 (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,
diff --git a/src/network/networkd-dhcp-relay.c b/src/network/networkd-dhcp-relay.c
new file mode 100644 (file)
index 0000000..5e793a0
--- /dev/null
@@ -0,0 +1,362 @@
+/* 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);
+        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;
+
+        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);
diff --git a/src/network/networkd-dhcp-relay.h b/src/network/networkd-dhcp-relay.h
new file mode 100644 (file)
index 0000000..7d56740
--- /dev/null
@@ -0,0 +1,19 @@
+/* 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_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);
index 3684a696ff89e56a7a85a1e781efd4e77cebc4b8..242da72c27ee86fa364b8b8b304ff1a3a3698ce7 100644 (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
index a69a5e8979c3c8ab66027b638691c579bacc1d38..969818e9f7622d181006ed68bce82c4e7644b829 100644 (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,7 @@ 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_server = sd_dhcp_server_unref(link->dhcp_server);
 
         link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
@@ -424,6 +427,10 @@ 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_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 +746,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");
@@ -1156,6 +1167,7 @@ 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_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);
@@ -1274,6 +1286,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;
@@ -2605,6 +2621,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)
index 456ec99185624cd685b01e48a7c5c594ac67ab39..4c3e53b93eabc7b2c26924d20bfb55733ef53375 100644 (file)
@@ -143,6 +143,7 @@ typedef struct Link {
         bool bridge_vlan_set:1;
         bool bearer_configured:1;
 
+        sd_dhcp_relay_interface *dhcp_relay_interface;
         sd_dhcp_server *dhcp_server;
 
         sd_ndisc *ndisc;
index f43709da356c959374bc5ff765548834ef661bf6..63ba0c166fa83166dbe8dded37668663cd85acc8 100644 (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);
index bacf3df444474429baa21ad915879c8a4610df5a..2e1b6430907ee36d6f4fdcfd02bd319a1b02f4f0 100644 (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;
 
index 9e6eb7c799572057631396e8513349d9be1b14fb..4b54c6d88de6f06ea90a7eedc654e599b12a15d2 100644 (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,6 +361,12 @@ 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)
index dfe524d150d23196f8d7bcce72a348f1dde69bbf..b1569866d10dadb16708f58a04f81f6afbcf7ef6 100644 (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,6 +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_relay_interface_mode = _DHCP_RELAY_INTERFACE_INVALID,
+                .dhcp_relay_extra_options = TLV_INIT(TLV_DHCP4_SUBOPTION),
+
                 .dhcp_server_bind_to_interface = true,
                 .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
                 .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
@@ -547,6 +551,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,6 +772,11 @@ 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_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);
index 625b3b9a08c3184d2c5d9acd9694e0efe2c40b5b..d9e516ff4ecaf171ea514cdaeb0875932eaf56ef 100644 (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,6 +209,16 @@ 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_agent_address_in_addr;
+        struct in_addr dhcp_relay_gateway_address;
+        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;
index 0ab372b4b21e788d733f8f7e97c5464b97b26c3a..ea846327b0db9f6124531a70585e5f79e2d97a66 100644 (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",
index d656d7aa7aa8af3139643421f2b762962262ff0a..70c8b2ca9eedae592728ec8fb0c3d9fcbf69c6f7 100644 (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,