]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: dhcp-pd: add 6rd support
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 5 Dec 2021 16:35:55 +0000 (01:35 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 7 Dec 2021 11:33:16 +0000 (20:33 +0900)
Closes #19152.

13 files changed:
network/80-6rd-tunnel.network [new file with mode: 0644]
network/meson.build
src/network/netdev/tunnel.c
src/network/netdev/tunnel.h
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-prefix-delegation.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.h
test/fuzz/fuzz-network-parser/directives.network

diff --git a/network/80-6rd-tunnel.network b/network/80-6rd-tunnel.network
new file mode 100644 (file)
index 0000000..2e479eb
--- /dev/null
@@ -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
index ccfd79e8dabad0a8052ab02f095eee544b7acc4c..f4ae2194d05a4518b07777d8d03dae93fc66617b 100644 (file)
@@ -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',
index 98986f4dc6766b0dbb93b53afcc2f08f88e0058c..5dbbb83975d686fe3c8a165543050dc36ac980df 100644 (file)
@@ -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;
index 35021e9409fc8055b293981b0db4a3ab4df23cf7..0f387ae68dc28a1a48923dacfe49c03a35170e81 100644 (file)
@@ -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);
index fc0caace770321475415387db0b5b8eca0f85ade..e1d4c6af1f92c6ff44b8abcb70ada5f6f1e6608d 100644 (file)
 #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(
index 1157f723c2efab58d573ac43e4caa50e455c3bb5..d67f919ba73f0cc1c1d576b20e229ae89054c8c3 100644 (file)
@@ -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);
index cd30cae7c9c4fc63635eb7c3b7b8f1171e74ead5..b8efb76858938bfe63d14b1f1f9adeb5bf1fac8a 100644 (file)
@@ -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);
 
index 339372963d01eadc84f2c4bd8e9c4644182960e2..830e39f66afe06a224f4896698a11badf423d09a 100644 (file)
@@ -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);
index 67b4579165b0a8b92d780b1fcbf8eef18e895246..b951d9aaf0c71108bd0fe92b426b9a7fb3f30097 100644 (file)
@@ -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);
index 8190f4f8bb7be389adb0a24eda0ee8348920d6c7..5895240674dbd6b6177810d7914f769e64b072a3 100644 (file)
@@ -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;
index bdc67842097f5ded956308633e9db682dca7f776..4c3bf97311d44979033e55c0ed9de021e498a8fd 100644 (file)
@@ -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
index 863ea24cecdbbf7c938a508ea276d09e83aef995..f7eb37acedafcb5877b64442d0999fbad05c7ba5 100644 (file)
@@ -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;
index ca9000cdee814d4963c9af6d1fbda188e654efe9..48f9ad6fba982521287de078b0994a4f101c8a84 100644 (file)
@@ -128,6 +128,7 @@ SendDecline=
 MUDURL=
 RouteMTUBytes=
 FallbackLeaseLifetimeSec=
+Use6RD=
 [DHCPv6]
 UseAddress=
 UseDelegatedPrefix=