From e49bad0179cb670edeac12ef062887af0ac4c8c5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 6 Dec 2021 01:35:55 +0900 Subject: [PATCH] network: dhcp-pd: add 6rd support Closes #19152. --- network/80-6rd-tunnel.network | 18 + network/meson.build | 3 +- src/network/netdev/tunnel.c | 143 ++++++++ src/network/netdev/tunnel.h | 4 + src/network/networkd-dhcp-prefix-delegation.c | 337 +++++++++++++++++- src/network/networkd-dhcp-prefix-delegation.h | 4 + src/network/networkd-dhcp4.c | 35 +- src/network/networkd-dhcp4.h | 1 + src/network/networkd-link.c | 1 + src/network/networkd-link.h | 1 + src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.h | 1 + .../fuzz-network-parser/directives.network | 1 + 13 files changed, 534 insertions(+), 16 deletions(-) create mode 100644 network/80-6rd-tunnel.network 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 #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 +#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=