summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-12-06 01:35:55 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-12-07 20:33:16 +0900
commite49bad0179cb670edeac12ef062887af0ac4c8c5 (patch)
tree3a2aad2367545af6964980ac6608d092448cb604
parentdbdcaca3abbab0763f367f4e1a805323e4553b3a (diff)
downloadsystemd-e49bad0179cb670edeac12ef062887af0ac4c8c5.tar.gz
network: dhcp-pd: add 6rd support
Closes #19152.
-rw-r--r--network/80-6rd-tunnel.network18
-rw-r--r--network/meson.build3
-rw-r--r--src/network/netdev/tunnel.c143
-rw-r--r--src/network/netdev/tunnel.h4
-rw-r--r--src/network/networkd-dhcp-prefix-delegation.c337
-rw-r--r--src/network/networkd-dhcp-prefix-delegation.h4
-rw-r--r--src/network/networkd-dhcp4.c35
-rw-r--r--src/network/networkd-dhcp4.h1
-rw-r--r--src/network/networkd-link.c1
-rw-r--r--src/network/networkd-link.h1
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network1
13 files changed, 534 insertions, 16 deletions
diff --git a/network/80-6rd-tunnel.network b/network/80-6rd-tunnel.network
new file mode 100644
index 0000000000..2e479eb1ac
--- /dev/null
+++ b/network/80-6rd-tunnel.network
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# 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.
+
+# This network file matches 6rd-* SIT devices which is automatically created by
+# systemd-networkd when DHCPv4 6RD option is received.
+
+[Match]
+Name=6rd-*
+Type=sit
+
+[Network]
+DHCPPrefixDelegation=yes
diff --git a/network/meson.build b/network/meson.build
index ccfd79e8da..f4ae2194d0 100644
--- a/network/meson.build
+++ b/network/meson.build
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
if conf.get('ENABLE_NETWORKD') == 1
- install_data('80-container-host0.network',
+ install_data('80-6rd-tunnel.network',
+ '80-container-host0.network',
'80-container-ve.network',
'80-container-vz.network',
'80-vm-vt.network',
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index 98986f4dc6..5dbbb83975 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -8,9 +8,12 @@
#include <linux/ip6_tunnel.h>
#include "conf-parser.h"
+#include "hexdecoct.h"
#include "missing_network.h"
#include "netlink-util.h"
+#include "networkd-manager.h"
#include "parse-util.h"
+#include "siphash24.h"
#include "string-table.h"
#include "string-util.h"
#include "tunnel.h"
@@ -29,6 +32,146 @@ static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode, "Failed to parse ip6 tunnel Mode");
+#define HASH_KEY SD_ID128_MAKE(74,c4,de,12,f3,d9,41,34,bb,3d,c1,a4,42,93,50,87)
+
+int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret) {
+ _cleanup_free_ char *ifname_alloc = NULL;
+ uint8_t ipv4masklen, sixrd_prefixlen, *buf, *p;
+ struct in_addr ipv4address;
+ struct in6_addr sixrd_prefix;
+ char ifname[IFNAMSIZ];
+ uint64_t result;
+ size_t sz;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &ipv4address);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get DHCPv4 address: %m");
+
+ r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get 6rd option: %m");
+
+ sz = sizeof(uint8_t) * 2 + sizeof(struct in6_addr) + sizeof(struct in_addr);
+ buf = newa(uint8_t, sz);
+ p = buf;
+ p = mempcpy(p, &ipv4masklen, sizeof(uint8_t));
+ p = mempcpy(p, &ipv4address, sizeof(struct in_addr));
+ p = mempcpy(p, &sixrd_prefixlen, sizeof(uint8_t));
+ p = mempcpy(p, &sixrd_prefix, sizeof(struct in6_addr));
+
+ result = siphash24(buf, sz, HASH_KEY.bytes);
+ memcpy(ifname, "6rd-", STRLEN("6rd-"));
+ ifname[STRLEN("6rd-") ] = urlsafe_base64char(result >> 54);
+ ifname[STRLEN("6rd-") + 1] = urlsafe_base64char(result >> 48);
+ ifname[STRLEN("6rd-") + 2] = urlsafe_base64char(result >> 42);
+ ifname[STRLEN("6rd-") + 3] = urlsafe_base64char(result >> 36);
+ ifname[STRLEN("6rd-") + 4] = urlsafe_base64char(result >> 30);
+ ifname[STRLEN("6rd-") + 5] = urlsafe_base64char(result >> 24);
+ ifname[STRLEN("6rd-") + 6] = urlsafe_base64char(result >> 18);
+ ifname[STRLEN("6rd-") + 7] = urlsafe_base64char(result >> 12);
+ ifname[STRLEN("6rd-") + 8] = urlsafe_base64char(result >> 6);
+ ifname[STRLEN("6rd-") + 9] = urlsafe_base64char(result);
+ ifname[STRLEN("6rd-") + 10] = '\0';
+ assert_cc(STRLEN("6rd-") + 10 <= IFNAMSIZ);
+
+ ifname_alloc = strdup(ifname);
+ if (!ifname_alloc)
+ return log_oom_debug();
+
+ *ret = TAKE_PTR(ifname_alloc);
+ return 0;
+}
+
+int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ uint8_t ipv4masklen, sixrd_prefixlen;
+ struct in_addr ipv4address, relay_prefix;
+ struct in6_addr sixrd_prefix;
+ int r;
+
+ assert(link);
+ assert(link->ifindex > 0);
+ assert(link->manager);
+ assert(link->dhcp_lease);
+ assert(link->dhcp4_6rd_tunnel_name);
+ assert(callback);
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &ipv4address);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get DHCPv4 address: %m");
+
+ r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to get 6rd option: %m");
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, link->dhcp4_6rd_tunnel_name);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IFNAME, attribute: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "sit");
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
+
+ r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &ipv4address);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, 64);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_TTL attribute: %m");
+
+ r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &sixrd_prefix);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_PREFIX attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, sixrd_prefixlen);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_PREFIXLEN attribute: %m");
+
+ relay_prefix = ipv4address;
+ (void) in4_addr_mask(&relay_prefix, ipv4masklen);
+ r = sd_netlink_message_append_u32(m, IFLA_IPTUN_6RD_RELAY_PREFIX, relay_prefix.s_addr);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_RELAY_PREFIX attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, ipv4masklen);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_RELAY_PREFIXLEN attribute: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+ r = netlink_call_async(link->manager->rtnl, NULL, m, callback,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Tunnel *t;
int r;
diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h
index 35021e9409..0f387ae68d 100644
--- a/src/network/netdev/tunnel.h
+++ b/src/network/netdev/tunnel.h
@@ -6,6 +6,7 @@
#include "conf-parser.h"
#include "fou-tunnel.h"
#include "netdev.h"
+#include "networkd-link.h"
typedef enum Ip6TnlMode {
NETDEV_IP6_TNL_MODE_IP6IP6,
@@ -60,6 +61,9 @@ typedef struct Tunnel {
uint8_t sixrd_prefixlen;
} Tunnel;
+int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret);
+int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback);
+
DEFINE_NETDEV_CAST(IPIP, Tunnel);
DEFINE_NETDEV_CAST(GRE, Tunnel);
DEFINE_NETDEV_CAST(GRETAP, Tunnel);
diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c
index fc0caace77..e1d4c6af1f 100644
--- a/src/network/networkd-dhcp-prefix-delegation.c
+++ b/src/network/networkd-dhcp-prefix-delegation.c
@@ -15,9 +15,11 @@
#include "networkd-queue.h"
#include "networkd-radv.h"
#include "networkd-route.h"
+#include "networkd-setlink.h"
#include "parse-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tunnel.h"
bool link_dhcp_pd_is_enabled(Link *link) {
assert(link);
@@ -49,6 +51,13 @@ bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto) {
return accept_auto;
}
+bool dhcp4_lease_has_pd_prefix(sd_dhcp_lease *lease) {
+ if (!lease)
+ return false;
+
+ return sd_dhcp_lease_get_6rd(lease, NULL, NULL, NULL, NULL, NULL) >= 0;
+}
+
bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
uint32_t lifetime_preferred_sec, lifetime_valid_sec;
struct in6_addr pd_prefix;
@@ -679,6 +688,35 @@ void dhcp_pd_prefix_lost(Link *uplink) {
set_clear(uplink->dhcp_pd_prefixes);
}
+void dhcp4_pd_prefix_lost(Link *uplink) {
+ Link *tunnel;
+
+ dhcp_pd_prefix_lost(uplink);
+
+ if (uplink->dhcp4_6rd_tunnel_name &&
+ link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &tunnel) >= 0)
+ (void) link_remove(tunnel);
+}
+
+static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->dhcp4_messages > 0);
+
+ link->dhcp4_messages--;
+
+ r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCP delegated prefix");
+ if (r <= 0)
+ return r;
+
+ r = dhcp4_check_ready(link);
+ if (r < 0)
+ link_enter_failed(link);
+
+ return 1;
+}
+
static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -759,6 +797,18 @@ static int dhcp_request_unreachable_route(
return 0;
}
+static int dhcp4_request_unreachable_route(
+ Link *link,
+ const struct in6_addr *addr,
+ uint8_t prefixlen,
+ usec_t lifetime_usec,
+ const union in_addr_union *server_address) {
+
+ return dhcp_request_unreachable_route(link, addr, prefixlen, lifetime_usec,
+ NETWORK_CONFIG_SOURCE_DHCP4, server_address,
+ &link->dhcp4_messages, dhcp4_unreachable_route_handler);
+}
+
static int dhcp6_request_unreachable_route(
Link *link,
const struct in6_addr *addr,
@@ -807,6 +857,229 @@ static int dhcp_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t
return 0;
}
+static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const struct in_addr *br_address, usec_t lifetime_usec) {
+ _cleanup_(route_freep) Route *route = NULL;
+ Route *existing;
+ int r;
+
+ assert(link);
+ assert(br_address);
+
+ r = route_new(&route);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to allocate default gateway for DHCP delegated prefix: %m");
+
+ route->source = NETWORK_CONFIG_SOURCE_DHCP_PD;
+ route->family = AF_INET6;
+ route->gw_family = AF_INET6;
+ route->gw.in6.s6_addr32[3] = br_address->s_addr;
+ route->scope = RT_SCOPE_UNIVERSE;
+ route->protocol = RTPROT_DHCP;
+ route->priority = IP6_RT_PRIO_USER;
+ route->lifetime_usec = lifetime_usec;
+
+ if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
+ link->dhcp_pd_configured = false;
+ else
+ route_unmark(existing);
+
+ r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
+ dhcp_pd_route_handler, NULL);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Failed to request default gateway for DHCP delegated prefix: %m");
+
+ return 0;
+}
+
+static void dhcp4_calculate_pd_prefix(
+ const struct in_addr *ipv4address,
+ uint8_t ipv4masklen,
+ const struct in6_addr *sixrd_prefix,
+ uint8_t sixrd_prefixlen,
+ struct in6_addr *ret_pd_prefix,
+ uint8_t *ret_pd_prefixlen) {
+
+ struct in6_addr pd_prefix;
+
+ assert(ipv4address);
+ assert(ipv4masklen <= 32);
+ assert(sixrd_prefix);
+ assert(32 - ipv4masklen + sixrd_prefixlen <= 128);
+ assert(ret_pd_prefix);
+
+ pd_prefix = *sixrd_prefix;
+ for (unsigned i = 0; i < (unsigned) (32 - ipv4masklen); i++)
+ if (ipv4address->s_addr & htobe32(UINT32_C(1) << (32 - ipv4masklen - i - 1)))
+ pd_prefix.s6_addr[(i + sixrd_prefixlen) / 8] |= 1 << (7 - (i + sixrd_prefixlen) % 8);
+
+ *ret_pd_prefix = pd_prefix;
+ if (ret_pd_prefixlen)
+ *ret_pd_prefixlen = 32 - ipv4masklen + sixrd_prefixlen;
+}
+
+static int dhcp4_pd_assign_subnet_prefix(Link *link, Link *uplink) {
+ uint8_t ipv4masklen, sixrd_prefixlen, pd_prefixlen;
+ struct in6_addr sixrd_prefix, pd_prefix;
+ const struct in_addr *br_addresses;
+ struct in_addr ipv4address;
+ uint32_t lifetime_sec;
+ usec_t lifetime_usec;
+ int r;
+
+ assert(link);
+ assert(uplink);
+ assert(uplink->dhcp_lease);
+
+ r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 address: %m");
+
+ r = sd_dhcp_lease_get_lifetime(uplink->dhcp_lease, &lifetime_sec);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
+
+ lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic()));
+
+ r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get 6rd option: %m");
+
+ dhcp4_calculate_pd_prefix(&ipv4address, ipv4masklen, &sixrd_prefix, sixrd_prefixlen, &pd_prefix, &pd_prefixlen);
+
+ if (pd_prefixlen > 64)
+ return 0;
+
+ r = dhcp_pd_prepare(link);
+ if (r <= 0)
+ return r;
+
+ if (streq_ptr(uplink->dhcp4_6rd_tunnel_name, link->ifname)) {
+ r = dhcp_pd_assign_prefix_on_uplink(link, &pd_prefix, pd_prefixlen, lifetime_usec, lifetime_usec);
+ if (r < 0)
+ return r;
+
+ r = dhcp4_pd_request_default_gateway_on_6rd_tunnel(link, &br_addresses[0], lifetime_usec);
+ if (r < 0)
+ return r;
+ } else {
+ r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefixlen, lifetime_usec, lifetime_usec);
+ if (r < 0)
+ return r;
+ }
+
+ return dhcp_pd_finalize(link);
+}
+
+static int dhcp4_pd_6rd_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->manager);
+ assert(link->dhcp4_6rd_tunnel_name);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ log_link_message_warning_errno(link, m, r, "Failed to create 6rd tunnel device");
+ link_enter_failed(link);
+ return 0;
+ }
+
+ return 0;
+}
+
+int dhcp4_pd_prefix_acquired(Link *uplink) {
+ _cleanup_free_ char *tunnel_name = NULL;
+ uint8_t ipv4masklen, sixrd_prefixlen, pd_prefixlen;
+ struct in6_addr sixrd_prefix, pd_prefix;
+ struct in_addr ipv4address;
+ union in_addr_union server_address;
+ uint32_t lifetime_sec;
+ usec_t lifetime_usec;
+ Link *link;
+ int r;
+
+ assert(uplink);
+ assert(uplink->dhcp_lease);
+
+ r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 address: %m");
+
+ r = sd_dhcp_lease_get_lifetime(uplink->dhcp_lease, &lifetime_sec);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
+
+ lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic()));
+
+ r = sd_dhcp_lease_get_server_identifier(uplink->dhcp_lease, &server_address.in);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv4 lease: %m");
+
+ r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
+ if (r < 0)
+ return log_link_warning_errno(uplink, r, "Failed to get 6rd option: %m");
+
+ /* Calculate PD prefix */
+ dhcp4_calculate_pd_prefix(&ipv4address, ipv4masklen, &sixrd_prefix, sixrd_prefixlen, &pd_prefix, &pd_prefixlen);
+
+ /* Register and log PD prefix */
+ r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefixlen);
+ if (r < 0)
+ return r;
+
+ /* Request unreachable route */
+ r = dhcp4_request_unreachable_route(uplink, &pd_prefix, pd_prefixlen, lifetime_usec, &server_address);
+ if (r < 0)
+ return r;
+
+ /* Generate 6rd SIT tunnel device name. */
+ r = dhcp4_pd_create_6rd_tunnel_name(uplink, &tunnel_name);
+ if (r < 0)
+ return r;
+
+ /* Remove old tunnel device if exists. */
+ if (!streq_ptr(uplink->dhcp4_6rd_tunnel_name, tunnel_name)) {
+ Link *old_tunnel;
+
+ if (uplink->dhcp4_6rd_tunnel_name &&
+ link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &old_tunnel) >= 0)
+ (void) link_remove(old_tunnel);
+
+ free_and_replace(uplink->dhcp4_6rd_tunnel_name, tunnel_name);
+ }
+
+ /* Create 6rd SIT tunnel device if it does not exist yet. */
+ if (link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, NULL) < 0) {
+ r = dhcp4_pd_create_6rd_tunnel(uplink, dhcp4_pd_6rd_tunnel_create_handler);
+ if (r < 0)
+ return r;
+ }
+
+ /* Then, assign subnet prefixes to downstream interfaces. */
+ HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
+ if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true))
+ continue;
+
+ r = dhcp4_pd_assign_subnet_prefix(link, uplink);
+ if (r < 0) {
+ /* When failed on the upstream interface (i.e., the case link == uplink),
+ * immediately abort the assignment of the prefixes. As, the all assigned
+ * prefixes will be dropped soon in link_enter_failed(), and it is meaningless
+ * to continue the assignment. */
+ if (link == uplink)
+ return r;
+
+ link_enter_failed(link);
+ }
+ }
+
+ return 0;
+}
+
static int dhcp6_pd_assign_subnet_prefixes(Link *link, Link *uplink) {
usec_t timestamp_usec;
int r;
@@ -922,6 +1195,30 @@ int dhcp6_pd_prefix_acquired(Link *uplink) {
return 0;
}
+static bool dhcp4_pd_uplink_is_ready(Link *link) {
+ assert(link);
+
+ if (!link->network)
+ return false;
+
+ if (!link->network->dhcp_use_6rd)
+ return false;
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return false;
+
+ if (!link->dhcp_client)
+ return false;
+
+ if (sd_dhcp_client_is_running(link->dhcp_client) <= 0)
+ return false;
+
+ if (!link->dhcp_lease)
+ return false;
+
+ return dhcp4_lease_has_pd_prefix(link->dhcp_lease);
+}
+
static bool dhcp6_pd_uplink_is_ready(Link *link) {
assert(link);
@@ -965,20 +1262,30 @@ int dhcp_pd_find_uplink(Link *link, Link **ret) {
return r;
if (uplink) {
- if (!dhcp6_pd_uplink_is_ready(uplink))
- return -EBUSY;
+ if (dhcp4_pd_uplink_is_ready(uplink)) {
+ *ret = uplink;
+ return AF_INET;
+ }
- *ret = uplink;
- return 0;
+ if (dhcp6_pd_uplink_is_ready(uplink)) {
+ *ret = uplink;
+ return AF_INET6;
+ }
+
+ return -EBUSY;
}
HASHMAP_FOREACH(uplink, link->manager->links_by_index) {
- if (!dhcp6_pd_uplink_is_ready(uplink))
- continue;
-
/* Assume that there exists at most one link which acquired delegated prefixes. */
- *ret = uplink;
- return 0;
+ if (dhcp4_pd_uplink_is_ready(uplink)) {
+ *ret = uplink;
+ return AF_INET;
+ }
+
+ if (dhcp6_pd_uplink_is_ready(uplink)) {
+ *ret = uplink;
+ return AF_INET6;
+ }
}
return -ENODEV;
@@ -986,17 +1293,23 @@ int dhcp_pd_find_uplink(Link *link, Link **ret) {
int dhcp_request_prefix_delegation(Link *link) {
Link *uplink;
+ int r;
assert(link);
if (!link_dhcp_pd_is_enabled(link))
return 0;
- if (dhcp_pd_find_uplink(link, &uplink) < 0)
+ r = dhcp_pd_find_uplink(link, &uplink);
+ if (r < 0)
return 0;
- log_link_debug(link, "Requesting subnets of delegated prefixes acquired by %s", uplink->ifname);
- return dhcp6_pd_assign_subnet_prefixes(link, uplink);
+ log_link_debug(link, "Requesting subnets of delegated prefixes acquired by DHCPv%c client on %s",
+ r == AF_INET ? '4' : '6', uplink->ifname);
+
+ return r == AF_INET ?
+ dhcp4_pd_assign_subnet_prefix(link, uplink) :
+ dhcp6_pd_assign_subnet_prefixes(link, uplink);
}
int config_parse_dhcp_pd_subnet_id(
diff --git a/src/network/networkd-dhcp-prefix-delegation.h b/src/network/networkd-dhcp-prefix-delegation.h
index 1157f723c2..d67f919ba7 100644
--- a/src/network/networkd-dhcp-prefix-delegation.h
+++ b/src/network/networkd-dhcp-prefix-delegation.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
+#include "sd-dhcp-lease.h"
#include "sd-dhcp6-lease.h"
#include "conf-parser.h"
@@ -12,10 +13,13 @@ typedef struct Link Link;
bool link_dhcp_pd_is_enabled(Link *link);
bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto);
int dhcp_pd_find_uplink(Link *link, Link **ret);
+bool dhcp4_lease_has_pd_prefix(sd_dhcp_lease *lease);
bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
int dhcp_pd_remove(Link *link, bool only_marked);
int dhcp_request_prefix_delegation(Link *link);
+int dhcp4_pd_prefix_acquired(Link *uplink);
int dhcp6_pd_prefix_acquired(Link *uplink);
void dhcp_pd_prefix_lost(Link *uplink);
+void dhcp4_pd_prefix_lost(Link *uplink);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index cd30cae7c9..b8efb76858 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -12,6 +12,7 @@
#include "parse-util.h"
#include "network-internal.h"
#include "networkd-address.h"
+#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp4.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
@@ -27,7 +28,6 @@
#include "sysctl-util.h"
static int dhcp4_request_address_and_routes(Link *link, bool announce);
-static int dhcp4_check_ready(Link *link);
void network_adjust_dhcp4(Network *network) {
assert(network);
@@ -119,7 +119,7 @@ static int dhcp4_address_ready_callback(Address *address) {
return dhcp4_check_ready(address->link);
}
-static int dhcp4_check_ready(Link *link) {
+int dhcp4_check_ready(Link *link) {
Address *address;
int r;
@@ -789,11 +789,16 @@ int dhcp4_lease_lost(Link *link) {
assert(link);
assert(link->dhcp_lease);
+ assert(link->network);
log_link_info(link, "DHCP lease lost");
link->dhcp4_configured = false;
+ if (link->network->dhcp_use_6rd &&
+ dhcp4_lease_has_pd_prefix(link->dhcp_lease))
+ dhcp4_pd_prefix_lost(link);
+
k = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
if (k < 0)
r = k;
@@ -964,20 +969,31 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
}
static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *old_lease = NULL;
sd_dhcp_lease *lease;
int r;
assert(link);
+ assert(link->network);
assert(client);
r = sd_dhcp_client_get_lease(client, &lease);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
- sd_dhcp_lease_unref(link->dhcp_lease);
+ old_lease = TAKE_PTR(link->dhcp_lease);
link->dhcp_lease = sd_dhcp_lease_ref(lease);
link_dirty(link);
+ if (link->network->dhcp_use_6rd) {
+ if (dhcp4_lease_has_pd_prefix(link->dhcp_lease)) {
+ r = dhcp4_pd_prefix_acquired(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to process 6rd option: %m");
+ } else if (dhcp4_lease_has_pd_prefix(old_lease))
+ dhcp4_pd_prefix_lost(link);
+ }
+
return dhcp4_request_address_and_routes(link, false);
}
@@ -1043,6 +1059,13 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
+ if (link->network->dhcp_use_6rd &&
+ dhcp4_lease_has_pd_prefix(link->dhcp_lease)) {
+ r = dhcp4_pd_prefix_acquired(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to process 6rd option: %m");
+ }
+
return dhcp4_request_address_and_routes(link, true);
}
@@ -1439,6 +1462,12 @@ static int dhcp4_configure(Link *link) {
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for timezone: %m");
}
+ if (link->network->dhcp_use_6rd) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_6RD);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for 6rd: %m");
+ }
+
SET_FOREACH(request_options, link->network->dhcp_request_options) {
uint32_t option = PTR_TO_UINT32(request_options);
diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h
index 339372963d..830e39f66a 100644
--- a/src/network/networkd-dhcp4.h
+++ b/src/network/networkd-dhcp4.h
@@ -23,6 +23,7 @@ void network_adjust_dhcp4(Network *network);
int dhcp4_update_mac(Link *link);
int dhcp4_start(Link *link);
int dhcp4_lease_lost(Link *link);
+int dhcp4_check_ready(Link *link);
int request_process_dhcp4_client(Request *req);
int link_request_dhcp4_client(Link *link);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 67b4579165..b951d9aaf0 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -208,6 +208,7 @@ static void link_free_engines(Link *link) {
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
+ link->dhcp4_6rd_tunnel_name = mfree(link->dhcp4_6rd_tunnel_name);
link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx);
link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx);
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 8190f4f8bb..5895240674 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -114,6 +114,7 @@ typedef struct Link {
bool dhcp4_route_failed:1;
bool dhcp4_route_retrying:1;
bool dhcp4_configured:1;
+ char *dhcp4_6rd_tunnel_name;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address_configured:1;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index bdc6784209..4c3bf97311 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -240,6 +240,7 @@ DHCPv4.SendOption, config_parse_dhcp_send_option,
DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options)
DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
+DHCPv4.Use6RD, config_parse_bool, 0, offsetof(Network, dhcp_use_6rd)
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 863ea24cec..f7eb37aced 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -153,6 +153,7 @@ struct Network {
int dhcp_use_gateway;
bool dhcp_use_timezone;
bool dhcp_use_hostname;
+ bool dhcp_use_6rd;
bool dhcp_send_release;
bool dhcp_send_decline;
DHCPUseDomains dhcp_use_domains;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index ca9000cdee..48f9ad6fba 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -128,6 +128,7 @@ SendDecline=
MUDURL=
RouteMTUBytes=
FallbackLeaseLifetimeSec=
+Use6RD=
[DHCPv6]
UseAddress=
UseDelegatedPrefix=