diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 68d91390cc..6f152c9e14 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -854,18 +854,19 @@ Table=1234 Whether to enable or disable Router Advertisement sending on a link. Takes a boolean value. When enabled, prefixes configured in [IPv6Prefix] sections and routes configured in [IPv6RoutePrefix] sections are distributed as defined in the [IPv6SendRA] - section. If DHCPv6PrefixDelegation= is enabled, then the delegated - prefixes are also distributed. See DHCPv6PrefixDelegation= setting and the - [IPv6SendRA], [IPv6Prefix], [IPv6RoutePrefix], and [DHCPv6PrefixDelegation] sections for more + section. If DHCPPrefixDelegation= is enabled, then the delegated prefixes + are also distributed. See DCHPPrefixDelegation= setting and the + [IPv6SendRA], [IPv6Prefix], [IPv6RoutePrefix], and [DHCPPrefixDelegation] sections for more configuration options. - DHCPv6PrefixDelegation= - Takes a boolean value. When enabled, requests prefixes using a DHCPv6 client - configured on another link. By default, an address within each delegated prefix will be - assigned, and the prefixes will be announced through IPv6 Router Advertisement when - IPv6SendRA= is enabled. Such default settings can be configured in - [DHCPv6PrefixDelegation] section. Defaults to disabled. + DHCPPrefixDelegation= + Takes a boolean value. When enabled, requests subnet prefixes acquired by a + DHCPv6 client, or by a DHCPv4 client through the 6RD option configured on another link. By + default, an address within each delegated prefix will be assigned, and the prefixes will be + announced through IPv6 Router Advertisement when IPv6SendRA= is enabled. + Such default settings can be configured in [DHCPPrefixDelegation] section. Defaults to + disabled. IPv6MTUBytes= @@ -1900,6 +1901,15 @@ Table=1234 the local system. Defaults to false. + + Use6RD= + When true, subnets of the received IPv6 prefix are assigned to downstream + interfaces which enables DHCPPrefixDelegation=. See also + DHCPPrefixDelegation= in the [Network] section, the [DHCPPrefixDelegation] + section, and RFC 5969. Defaults to + false. + + FallbackLeaseLifetimeSec= @@ -2076,9 +2086,9 @@ Table=1234 When true (the default), the client will request the DHCPv6 server to delegate prefixes. If the server provides prefixes to be delegated, then subnets of the prefixes are - assigned to the interfaces which enables DHCPv6PrefixDelegation=. - See also DHCPv6PrefixDelegation= in [Network] section, - [DHCPv6PrefixDelegation] section, and + assigned to the interfaces which enables DHCPPrefixDelegation=. + See also DHCPPrefixDelegation= in [Network] section, + [DHCPPrefixDelegation] section, and RFC 8415. @@ -2102,9 +2112,9 @@ Table=1234 Allows DHCPv6 client to start without router advertisements's managed or other address configuration flag. Takes one of no, solicit or information-request. If this is not specified, - solicit is used when DHCPv6PrefixDelegation= is + solicit is used when DHCPPrefixDelegation= is enabled and UplinkInterface=:self is specified in the - [DHCPv6PrefixDelegation] section. Otherwise, defaults to no, and the + [DHCPPrefixDelegation] section. Otherwise, defaults to no, and the DHCPv6 client will be started when an RA is received. See also DHCPv6Client= setting in the [IPv6AcceptRA] section. @@ -2113,10 +2123,11 @@ Table=1234 - [DHCPv6PrefixDelegation] Section Options - The [DHCPv6PrefixDelegation] section configures delegated prefixes assigned by DHCPv6 server. - The settings in this section are used only when DHCPv6PrefixDelegation= setting - is enabled. + [DHCPPrefixDelegation] Section Options + The [DHCPPrefixDelegation] section configures subnet prefixes of the delegated prefixes + acquired by a DHCPv6 client, or by a DHCPv4 client through the 6RD option on another interface. + The settings in this section are used only when the DHCPPrefixDelegation= + setting in the [Network] section is enabled. @@ -2127,7 +2138,7 @@ Table=1234 interface itself is considered the uplink interface, and WithoutRA=solicit is implied if the setting is not explicitly specified. When :auto, the first link which acquired prefixes to be delegated from - the DHCPv6 server is selected. Defaults to :auto. + the DHCPv6 or DHCPv4 server is selected. Defaults to :auto. @@ -2147,7 +2158,8 @@ Table=1234 Takes a boolean. When enabled, and IPv6SendRA= in [Network] section is enabled, the delegated prefixes are distributed through the IPv6 Router Advertisement. - Defaults to yes. + This setting will be ignored when the DHCPPrefixDelegation= setting is + enabled on the upstream interface. Defaults to yes. @@ -2436,7 +2448,7 @@ Token=prefixstable:2002:da8:1:: to always, the DHCPv6 client will be started in managed mode when an RA is received, even if neither managed nor other information flag is set in the RA. This will be ignored when WithoutRA= in the [DHCPv6] section is enabled, or - UplinkInterface=:self in the [DHCPv6PrefixDelegation] section is + UplinkInterface=:self in the [DHCPPrefixDelegation] section is specified. Defaults to true. @@ -2710,8 +2722,8 @@ Token=prefixstable:2002:da8:1:: values :none and :auto. When emitting DNS servers or search domains is enabled but no servers are specified, the servers configured in the uplink interface will be emitted. When :auto, the value specified to the same - setting in the [DHCPv6PrefixDelegation] section will be used if - DHCPv6PrefixDelegation= is enabled, otherwise the link which has a default + setting in the [DHCPPrefixDelegation] section will be used if + DHCPPrefixDelegation= is enabled, otherwise the link which has a default gateway with the highest priority will be automatically selected. When :none, no uplink interface will be selected. Defaults to :auto. @@ -4375,7 +4387,7 @@ Name=enp2s0 [Network] IPv6SendRA=yes -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes This will enable DHCPv6-PD on the interface enp1s0 as an upstream interface where the DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to. 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/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 35a74dd7c2..992ac9f325 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -70,6 +70,12 @@ struct sd_dhcp_lease { char *timezone; + uint8_t sixrd_ipv4masklen; + uint8_t sixrd_prefixlen; + struct in6_addr sixrd_prefix; + struct in_addr *sixrd_br_addresses; + size_t sixrd_n_br_addresses; + LIST_HEAD(struct sd_dhcp_raw_option, private_options); }; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index dd82163d9f..7f6d15cb09 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -251,6 +251,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { return -ENODATA; } +int sd_dhcp_lease_get_6rd( + sd_dhcp_lease *lease, + uint8_t *ret_ipv4masklen, + uint8_t *ret_prefixlen, + struct in6_addr *ret_prefix, + const struct in_addr **ret_br_addresses, + size_t *ret_n_br_addresses) { + + assert_return(lease, -EINVAL); + + if (lease->sixrd_n_br_addresses <= 0) + return -ENODATA; + + if (ret_ipv4masklen) + *ret_ipv4masklen = lease->sixrd_ipv4masklen; + if (ret_prefixlen) + *ret_prefixlen = lease->sixrd_prefixlen; + if (ret_prefix) + *ret_prefix = lease->sixrd_prefix; + if (ret_br_addresses) + *ret_br_addresses = lease->sixrd_br_addresses; + if (ret_n_br_addresses) + *ret_n_br_addresses = lease->sixrd_n_br_addresses; + + return 0; +} + int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { assert_return(lease, -EINVAL); assert_return(data, -EINVAL); @@ -289,6 +316,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { free(lease->client_id); free(lease->vendor_specific); strv_free(lease->search_domains); + free(lease->sixrd_br_addresses); return mfree(lease); } @@ -534,6 +562,61 @@ static int lease_parse_classless_routes( return 0; } +static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + uint8_t ipv4masklen, prefixlen; + struct in6_addr prefix; + _cleanup_free_ struct in_addr *br_addresses = NULL; + size_t n_br_addresses; + + assert(lease); + assert(option); + + /* See RFC 5969 Section 7.1.1 */ + + if (lease->sixrd_n_br_addresses > 0) + /* Multiple 6rd option?? */ + return -EINVAL; + + /* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */ + if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) || + (len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0) + return -EINVAL; + + /* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses + * within a given 6rd domain. This may be any value between 0 and 32. Any value + * greater than 32 is invalid. */ + ipv4masklen = option[0]; + if (ipv4masklen > 32) + return -EINVAL; + + /* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the + * purpose of bounds checking by DHCP option processing, the sum of + * (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */ + prefixlen = option[1]; + if (32 - ipv4masklen + prefixlen > 128) + return -EINVAL; + + /* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address. + * The bits in the prefix after the 6rdPrefixlen number of bits are reserved and + * MUST be initialized to zero by the sender and ignored by the receiver. */ + memcpy(&prefix, option + 2, sizeof(struct in6_addr)); + (void) in6_addr_mask(&prefix, prefixlen); + + /* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */ + n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr); + br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses); + if (!br_addresses) + return -ENOMEM; + + lease->sixrd_ipv4masklen = ipv4masklen; + lease->sixrd_prefixlen = prefixlen; + lease->sixrd_prefix = prefix; + lease->sixrd_br_addresses = TAKE_PTR(br_addresses); + lease->sixrd_n_br_addresses = n_br_addresses; + + return 0; +} + int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) { sd_dhcp_lease *lease = userdata; int r; @@ -719,6 +802,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void lease->vendor_specific_len = len; break; + case SD_DHCP_OPTION_6RD: + r = lease_parse_6rd(lease, option, len); + if (r < 0) + log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m"); + break; + case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST: r = dhcp_lease_insert_private_option(lease, code, option, len); if (r < 0) 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-address-generation.c b/src/network/networkd-address-generation.c index 4cf4dd590e..c9c53b22cc 100644 --- a/src/network/networkd-address-generation.c +++ b/src/network/networkd-address-generation.c @@ -22,7 +22,7 @@ #define RESERVED_SUBNET_ANYCAST_ADDRESSES ((const struct in6_addr) { .s6_addr = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80 } }) #define RESERVED_SUBNET_ANYCAST_PREFIXLEN 57 -#define DHCP6PD_APP_ID SD_ID128_MAKE(fb,b9,37,ca,4a,ed,4a,4d,b0,70,7f,aa,71,c0,c9,85) +#define DHCP_PD_APP_ID SD_ID128_MAKE(fb,b9,37,ca,4a,ed,4a,4d,b0,70,7f,aa,71,c0,c9,85) #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) #define RADV_APP_ID SD_ID128_MAKE(1f,1e,90,c8,5c,78,4f,dc,8e,61,2d,59,0d,53,c1,25) @@ -252,8 +252,8 @@ static int generate_addresses( return 0; } -int dhcp6_pd_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) { - return generate_addresses(link, link->network->dhcp6_pd_tokens, &DHCP6PD_APP_ID, prefix, prefixlen, ret); +int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) { + return generate_addresses(link, link->network->dhcp_pd_tokens, &DHCP_PD_APP_ID, prefix, prefixlen, ret); } int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) { diff --git a/src/network/networkd-address-generation.h b/src/network/networkd-address-generation.h index d8d9d6e8f0..e9c924eb97 100644 --- a/src/network/networkd-address-generation.h +++ b/src/network/networkd-address-generation.h @@ -7,7 +7,7 @@ typedef struct Link Link; -int dhcp6_pd_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret); +int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret); int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret); int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret); diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 66d4153c84..7996960bd1 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -1267,9 +1267,9 @@ int config_parse_uplink( } else if (streq(section, "IPv6SendRA")) { index = &network->router_uplink_index; name = &network->router_uplink_name; - } else if (streq(section, "DHCPv6PrefixDelegation")) { - index = &network->dhcp6_pd_uplink_index; - name = &network->dhcp6_pd_uplink_name; + } else if (STR_IN_SET(section, "DHCPv6PrefixDelegation", "DHCPPrefixDelegation")) { + index = &network->dhcp_pd_uplink_index; + name = &network->dhcp_pd_uplink_name; accept_none = false; } else assert_not_reached(); diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index ade606c192..e1d4c6af1f 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -15,40 +15,49 @@ #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_dhcp6_pd_is_enabled(Link *link) { +bool link_dhcp_pd_is_enabled(Link *link) { assert(link); if (!link->network) return false; - return link->network->dhcp6_pd; + return link->network->dhcp_pd; } -bool dhcp6_pd_is_uplink(Link *link, Link *target, bool accept_auto) { +bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto) { assert(link); assert(target); - if (!link_dhcp6_pd_is_enabled(link)) + if (!link_dhcp_pd_is_enabled(link)) return false; - if (link->network->dhcp6_pd_uplink_name) - return streq_ptr(target->ifname, link->network->dhcp6_pd_uplink_name) || - strv_contains(target->alternative_names, link->network->dhcp6_pd_uplink_name); + if (link->network->dhcp_pd_uplink_name) + return streq_ptr(target->ifname, link->network->dhcp_pd_uplink_name) || + strv_contains(target->alternative_names, link->network->dhcp_pd_uplink_name); - if (link->network->dhcp6_pd_uplink_index > 0) - return target->ifindex == link->network->dhcp6_pd_uplink_index; + if (link->network->dhcp_pd_uplink_index > 0) + return target->ifindex == link->network->dhcp_pd_uplink_index; - if (link->network->dhcp6_pd_uplink_index == UPLINK_INDEX_SELF) + if (link->network->dhcp_pd_uplink_index == UPLINK_INDEX_SELF) return link == target; - assert(link->network->dhcp6_pd_uplink_index == UPLINK_INDEX_AUTO); + assert(link->network->dhcp_pd_uplink_index == UPLINK_INDEX_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; @@ -62,21 +71,21 @@ bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred_sec, &lifetime_valid_sec) >= 0; } -static void link_remove_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) { +static void link_remove_dhcp_pd_subnet_prefix(Link *link, const struct in6_addr *prefix) { void *key; assert(link); assert(link->manager); assert(prefix); - if (hashmap_get(link->manager->links_by_dhcp6_pd_prefix, prefix) != link) + if (hashmap_get(link->manager->links_by_dhcp_pd_subnet_prefix, prefix) != link) return; - hashmap_remove2(link->manager->links_by_dhcp6_pd_prefix, prefix, &key); + hashmap_remove2(link->manager->links_by_dhcp_pd_subnet_prefix, prefix, &key); free(key); } -static int link_add_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) { +static int link_add_dhcp_pd_subnet_prefix(Link *link, const struct in6_addr *prefix) { _cleanup_free_ struct in6_addr *copy = NULL; int r; @@ -87,7 +96,7 @@ static int link_add_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) { if (!copy) return -ENOMEM; - r = hashmap_ensure_put(&link->manager->links_by_dhcp6_pd_prefix, &in6_addr_hash_ops_free, copy, link); + r = hashmap_ensure_put(&link->manager->links_by_dhcp_pd_subnet_prefix, &in6_addr_hash_ops_free, copy, link); if (r < 0) return r; if (r > 0) @@ -96,13 +105,13 @@ static int link_add_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) { return 0; } -static int link_get_by_dhcp6_pd_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) { +static int link_get_by_dhcp_pd_subnet_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) { Link *link; assert(manager); assert(prefix); - link = hashmap_get(manager->links_by_dhcp6_pd_prefix, prefix); + link = hashmap_get(manager->links_by_dhcp_pd_subnet_prefix, prefix); if (!link) return -ENODEV; @@ -111,18 +120,18 @@ static int link_get_by_dhcp6_pd_prefix(Manager *manager, const struct in6_addr * return 0; } -static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) { +static int dhcp_pd_get_assigned_subnet_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) { assert(link); assert(pd_prefix); - if (!link_dhcp6_pd_is_enabled(link)) + if (!link_dhcp_pd_is_enabled(link)) return -ENOENT; - if (link->network->dhcp6_pd_assign) { + if (link->network->dhcp_pd_assign) { Address *address; SET_FOREACH(address, link->addresses) { - if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD) continue; assert(address->family == AF_INET6); @@ -141,7 +150,7 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_pr Route *route; SET_FOREACH(route, link->routes) { - if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD) continue; assert(route->family == AF_INET6); @@ -156,23 +165,23 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_pr return -ENOENT; } -int dhcp6_pd_remove(Link *link, bool only_marked) { +int dhcp_pd_remove(Link *link, bool only_marked) { int k, r = 0; assert(link); assert(link->manager); - if (!link_dhcp6_pd_is_enabled(link)) + if (!link_dhcp_pd_is_enabled(link)) return 0; if (!only_marked) - link->dhcp6_pd_configured = false; + link->dhcp_pd_configured = false; - if (!link->network->dhcp6_pd_assign) { + if (!link->network->dhcp_pd_assign) { Route *route; SET_FOREACH(route, link->routes) { - if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD) continue; if (only_marked && !route_is_marked(route)) continue; @@ -180,7 +189,7 @@ int dhcp6_pd_remove(Link *link, bool only_marked) { if (link->radv) (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); - link_remove_dhcp6_pd_prefix(link, &route->dst.in6); + link_remove_dhcp_pd_subnet_prefix(link, &route->dst.in6); k = route_remove(route); if (k < 0) @@ -194,7 +203,7 @@ int dhcp6_pd_remove(Link *link, bool only_marked) { SET_FOREACH(address, link->addresses) { struct in6_addr prefix; - if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD) continue; if (only_marked && !address_is_marked(address)) continue; @@ -205,7 +214,7 @@ int dhcp6_pd_remove(Link *link, bool only_marked) { if (link->radv) (void) sd_radv_remove_prefix(link->radv, &prefix, 64); - link_remove_dhcp6_pd_prefix(link, &prefix); + link_remove_dhcp_pd_subnet_prefix(link, &prefix); k = address_remove(address); if (k < 0) @@ -218,38 +227,38 @@ int dhcp6_pd_remove(Link *link, bool only_marked) { return r; } -static int dhcp6_pd_check_ready(Link *link); +static int dhcp_pd_check_ready(Link *link); -static int dhcp6_pd_address_ready_callback(Address *address) { +static int dhcp_pd_address_ready_callback(Address *address) { Address *a; assert(address); assert(address->link); SET_FOREACH(a, address->link->addresses) - if (a->source == NETWORK_CONFIG_SOURCE_DHCP6PD) + if (a->source == NETWORK_CONFIG_SOURCE_DHCP_PD) a->callback = NULL; - return dhcp6_pd_check_ready(address->link); + return dhcp_pd_check_ready(address->link); } -static int dhcp6_pd_check_ready(Link *link) { +static int dhcp_pd_check_ready(Link *link) { int r; assert(link); assert(link->network); - if (link->dhcp6_pd_messages > 0) { - log_link_debug(link, "%s(): DHCPv6PD addresses and routes are not set.", __func__); + if (link->dhcp_pd_messages > 0) { + log_link_debug(link, "%s(): DHCP-PD addresses and routes are not set.", __func__); return 0; } - if (link->network->dhcp6_pd_assign) { + if (link->network->dhcp_pd_assign) { bool has_ready = false; Address *address; SET_FOREACH(address, link->addresses) { - if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD) + if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD) continue; if (address_is_ready(address)) { has_ready = true; @@ -259,19 +268,19 @@ static int dhcp6_pd_check_ready(Link *link) { if (!has_ready) { SET_FOREACH(address, link->addresses) - if (address->source == NETWORK_CONFIG_SOURCE_DHCP6PD) - address->callback = dhcp6_pd_address_ready_callback; + if (address->source == NETWORK_CONFIG_SOURCE_DHCP_PD) + address->callback = dhcp_pd_address_ready_callback; - log_link_debug(link, "%s(): no DHCPv6PD address is ready.", __func__); + log_link_debug(link, "%s(): no DHCP-PD address is ready.", __func__); return 0; } } - link->dhcp6_pd_configured = true; + link->dhcp_pd_configured = true; - log_link_debug(link, "DHCPv6 PD addresses and routes set."); + log_link_debug(link, "DHCP-PD addresses and routes set."); - r = dhcp6_pd_remove(link, /* only_marked = */ true); + r = dhcp_pd_remove(link, /* only_marked = */ true); if (r < 0) return r; @@ -279,26 +288,26 @@ static int dhcp6_pd_check_ready(Link *link) { return 1; } -static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->dhcp6_pd_messages > 0); + assert(link->dhcp_pd_messages > 0); - link->dhcp6_pd_messages--; + link->dhcp_pd_messages--; - r = route_configure_handler_internal(rtnl, m, link, "Failed to add DHCPv6 Prefix Delegation route"); + r = route_configure_handler_internal(rtnl, m, link, "Failed to add prefix route for DHCP delegated subnet prefix"); if (r <= 0) return r; - r = dhcp6_pd_check_ready(link); + r = dhcp_pd_check_ready(link); if (r < 0) link_enter_failed(link); return 1; } -static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, usec_t lifetime_usec) { +static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, usec_t lifetime_usec) { _cleanup_(route_freep) Route *route = NULL; Route *existing; int r; @@ -307,54 +316,54 @@ static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, uin assert(link->network); assert(prefix); - if (link->network->dhcp6_pd_assign) + if (link->network->dhcp_pd_assign) return 0; r = route_new(&route); if (r < 0) return r; - route->source = NETWORK_CONFIG_SOURCE_DHCP6PD; + route->source = NETWORK_CONFIG_SOURCE_DHCP_PD; route->family = AF_INET6; route->dst.in6 = *prefix; route->dst_prefixlen = prefixlen; route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp6_pd_route_metric; + route->priority = link->network->dhcp_pd_route_metric; route->lifetime_usec = lifetime_usec; if (route_get(NULL, link, route, &existing) < 0) - link->dhcp6_pd_configured = false; + link->dhcp_pd_configured = false; else route_unmark(existing); - r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_messages, - dhcp6_pd_route_handler, NULL); + r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages, + dhcp_pd_route_handler, NULL); if (r < 0) - return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m"); + return log_link_error_errno(link, r, "Failed to request DHCP-PD prefix route: %m"); return 0; } -static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->dhcp6_pd_messages > 0); + assert(link->dhcp_pd_messages > 0); - link->dhcp6_pd_messages--; + link->dhcp_pd_messages--; - r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 delegated prefix address"); + r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCP-PD address"); if (r <= 0) return r; - r = dhcp6_pd_check_ready(link); + r = dhcp_pd_check_ready(link); if (r < 0) link_enter_failed(link); return 1; } -static void log_dhcp6_pd_address(Link *link, const Address *address) { +static void log_dhcp_pd_address(Link *link, const Address *address) { _cleanup_free_ char *buffer = NULL; int log_level; @@ -368,13 +377,13 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) { (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer); - log_link_full(link, log_level, "DHCPv6-PD address %s (valid %s, preferred %s)", + log_link_full(link, log_level, "DHCP-PD address %s (valid %s, preferred %s)", strna(buffer), FORMAT_LIFETIME(address->lifetime_valid_usec), FORMAT_LIFETIME(address->lifetime_preferred_usec)); } -static int dhcp6_pd_request_address( +static int dhcp_pd_request_address( Link *link, const struct in6_addr *prefix, uint8_t prefixlen, @@ -389,12 +398,12 @@ static int dhcp6_pd_request_address( assert(link->network); assert(prefix); - if (!link->network->dhcp6_pd_assign) + if (!link->network->dhcp_pd_assign) return 0; - r = dhcp6_pd_generate_addresses(link, prefix, prefixlen, &addresses); + r = dhcp_pd_generate_addresses(link, prefix, prefixlen, &addresses); if (r < 0) - return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCPv6 delegated prefix: %m"); + return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m"); SET_FOREACH(a, addresses) { _cleanup_(address_freep) Address *address = NULL; @@ -402,35 +411,35 @@ static int dhcp6_pd_request_address( r = address_new(&address); if (r < 0) - return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m"); + return log_link_error_errno(link, r, "Failed to allocate address for DHCP delegated prefix: %m"); - address->source = NETWORK_CONFIG_SOURCE_DHCP6PD; + address->source = NETWORK_CONFIG_SOURCE_DHCP_PD; address->family = AF_INET6; address->in_addr.in6 = *a; address->prefixlen = prefixlen; address->lifetime_preferred_usec = lifetime_preferred_usec; address->lifetime_valid_usec = lifetime_valid_usec; if (prefixlen == 64) - SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp6_pd_manage_temporary_address); - address->route_metric = link->network->dhcp6_pd_route_metric; + SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp_pd_manage_temporary_address); + address->route_metric = link->network->dhcp_pd_route_metric; - log_dhcp6_pd_address(link, address); + log_dhcp_pd_address(link, address); if (address_get(link, address, &existing) < 0) - link->dhcp6_pd_configured = false; + link->dhcp_pd_configured = false; else address_unmark(existing); - r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_messages, - dhcp6_pd_address_handler, NULL); + r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp_pd_messages, + dhcp_pd_address_handler, NULL); if (r < 0) - return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m"); + return log_link_error_errno(link, r, "Failed to request DHCP delegated prefix address: %m"); } return 0; } -static int dhcp6_pd_calculate_prefix( +static int dhcp_pd_calculate_subnet_prefix( const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, uint64_t subnet_id, @@ -456,7 +465,7 @@ static int dhcp6_pd_calculate_prefix( return 0; } -static int dhcp6_pd_get_preferred_prefix( +static int dhcp_pd_get_preferred_subnet_prefix( Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, @@ -471,16 +480,16 @@ static int dhcp6_pd_get_preferred_prefix( assert(link->network); assert(pd_prefix); - if (link->network->dhcp6_pd_subnet_id >= 0) { + if (link->network->dhcp_pd_subnet_id >= 0) { /* If the link has a preference for a particular subnet id try to allocate that */ - r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, link->network->dhcp6_pd_subnet_id, &prefix); + r = dhcp_pd_calculate_subnet_prefix(pd_prefix, pd_prefix_len, link->network->dhcp_pd_subnet_id, &prefix); if (r < 0) return log_link_warning_errno(link, r, "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", - link->network->dhcp6_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len)); + link->network->dhcp_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len)); - if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) >= 0 && + if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) >= 0 && assigned_link != link) { _cleanup_free_ char *assigned_buf = NULL; @@ -498,18 +507,18 @@ static int dhcp6_pd_get_preferred_prefix( /* If we do not have an allocation preference just iterate * through the address space and return the first free prefix. */ - r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, n, &prefix); + r = dhcp_pd_calculate_subnet_prefix(pd_prefix, pd_prefix_len, n, &prefix); if (r < 0) return log_link_warning_errno(link, r, "Couldn't find a suitable prefix. Ran out of address space."); /* Do not use explicitly requested subnet IDs. Note that the corresponding link may not * appear yet. So, we need to check the ID is not used in any .network files. */ - if (set_contains(link->manager->dhcp6_pd_subnet_ids, &n)) + if (set_contains(link->manager->dhcp_pd_subnet_ids, &n)) continue; /* Check that the prefix is not assigned to another link. */ - if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) < 0 || + if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) < 0 || assigned_link == link) { *ret = prefix; return 0; @@ -517,7 +526,7 @@ static int dhcp6_pd_get_preferred_prefix( } } -static int dhcp6_pd_assign_prefix( +static int dhcp_pd_assign_subnet_prefix( Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, @@ -532,13 +541,13 @@ static int dhcp6_pd_assign_prefix( assert(link->network); assert(pd_prefix); - if (dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0 && - dhcp6_pd_get_preferred_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0) + if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0 && + dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0) return 0; (void) in6_addr_prefix_to_string(&prefix, 64, &buf); - if (link_radv_enabled(link) && link->network->dhcp6_pd_announce) { + if (link_radv_enabled(link) && link->network->dhcp_pd_announce) { r = radv_add_prefix(link, &prefix, 64, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return log_link_warning_errno(link, r, @@ -546,19 +555,19 @@ static int dhcp6_pd_assign_prefix( strna(buf)); } - r = dhcp6_pd_request_route(link, &prefix, 64, lifetime_valid_usec); + r = dhcp_pd_request_route(link, &prefix, 64, lifetime_valid_usec); if (r < 0) return log_link_warning_errno(link, r, "Failed to assign/update route for prefix %s: %m", strna(buf)); - r = dhcp6_pd_request_address(link, &prefix, 64, lifetime_preferred_usec, lifetime_valid_usec); + r = dhcp_pd_request_address(link, &prefix, 64, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return log_link_warning_errno(link, r, "Failed to assign/update address for prefix %s: %m", strna(buf)); - r = link_add_dhcp6_pd_prefix(link, &prefix); + r = link_add_dhcp_pd_subnet_prefix(link, &prefix); if (r < 0) return log_link_warning_errno(link, r, "Failed to save assigned prefix %s: %m", @@ -568,7 +577,7 @@ static int dhcp6_pd_assign_prefix( return 1; } -static int dhcp6_pd_assign_prefix_on_uplink( +static int dhcp_pd_assign_prefix_on_uplink( Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, @@ -584,16 +593,16 @@ static int dhcp6_pd_assign_prefix_on_uplink( (void) in6_addr_prefix_to_string(pd_prefix, pd_prefix_len, &buf); - if (link->network->dhcp6_pd_announce) + if (link->network->dhcp_pd_announce) log_link_debug(link, "Ignoring Announce= setting on upstream interface."); - r = dhcp6_pd_request_route(link, pd_prefix, pd_prefix_len, lifetime_valid_usec); + r = dhcp_pd_request_route(link, pd_prefix, pd_prefix_len, lifetime_valid_usec); if (r < 0) return log_link_warning_errno(link, r, "Failed to assign/update route for prefix %s: %m", strna(buf)); - r = dhcp6_pd_request_address(link, pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); + r = dhcp_pd_request_address(link, pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return log_link_warning_errno(link, r, "Failed to assign/update address for prefix %s: %m", @@ -603,68 +612,68 @@ static int dhcp6_pd_assign_prefix_on_uplink( return 1; } -static int dhcp6_pd_prepare(Link *link) { +static int dhcp_pd_prepare(Link *link) { if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; - if (!link_dhcp6_pd_is_enabled(link)) + if (!link_dhcp_pd_is_enabled(link)) return 0; - if (link_radv_enabled(link) && link->network->dhcp6_pd_announce && !link->radv) + if (link_radv_enabled(link) && link->network->dhcp_pd_announce && !link->radv) return 0; - link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL); - link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL); + link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP_PD, NULL); + link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP_PD, NULL); return 1; } -static int dhcp6_pd_finalize(Link *link) { +static int dhcp_pd_finalize(Link *link) { int r; if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; - if (link->dhcp6_pd_messages == 0) { - link->dhcp6_pd_configured = false; + if (link->dhcp_pd_messages == 0) { + link->dhcp_pd_configured = false; - r = dhcp6_pd_remove(link, /* only_marked = */ true); + r = dhcp_pd_remove(link, /* only_marked = */ true); if (r < 0) return r; } - if (!link->dhcp6_pd_configured) + if (!link->dhcp_pd_configured) link_set_state(link, LINK_STATE_CONFIGURING); link_check_ready(link); return 0; } -void dhcp6_pd_prefix_lost(Link *dhcp6_link) { +void dhcp_pd_prefix_lost(Link *uplink) { Route *route; Link *link; int r; - assert(dhcp6_link); - assert(dhcp6_link->manager); + assert(uplink); + assert(uplink->manager); - HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { - if (!dhcp6_pd_is_uplink(link, dhcp6_link, /* accept_auto = */ true)) + HASHMAP_FOREACH(link, uplink->manager->links_by_index) { + if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true)) continue; - r = dhcp6_pd_remove(link, /* only_marked = */ false); + r = dhcp_pd_remove(link, /* only_marked = */ false); if (r < 0) link_enter_failed(link); } - SET_FOREACH(route, dhcp6_link->manager->routes) { - if (route->source != NETWORK_CONFIG_SOURCE_DHCP6) + SET_FOREACH(route, uplink->manager->routes) { + if (IN_SET(route->source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6)) continue; if (route->family != AF_INET6) continue; if (route->type != RTN_UNREACHABLE) continue; - if (!set_contains(dhcp6_link->dhcp6_pd_prefixes, + if (!set_contains(uplink->dhcp_pd_prefixes, &(struct in_addr_prefix) { .family = AF_INET6, .prefixlen = route->dst_prefixlen, @@ -673,13 +682,42 @@ void dhcp6_pd_prefix_lost(Link *dhcp6_link) { (void) route_remove(route); - route_cancel_request(route, dhcp6_link); + route_cancel_request(route, uplink); } - set_clear(dhcp6_link->dhcp6_pd_prefixes); + set_clear(uplink->dhcp_pd_prefixes); } -static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +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; assert(link); @@ -687,7 +725,7 @@ static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li link->dhcp6_messages--; - r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated subnet"); + r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCP delegated prefix"); if (r <= 0) return r; @@ -698,12 +736,15 @@ static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } -static int dhcp6_request_unreachable_route( +static int dhcp_request_unreachable_route( Link *link, const struct in6_addr *addr, uint8_t prefixlen, usec_t lifetime_usec, - const union in_addr_union *server_address) { + NetworkConfigSource source, + const union in_addr_union *server_address, + unsigned *counter, + link_netlink_message_handler_t callback) { _cleanup_(route_freep) Route *route = NULL; Route *existing; @@ -711,13 +752,16 @@ static int dhcp6_request_unreachable_route( assert(link); assert(addr); + assert(IN_SET(source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6)); assert(server_address); + assert(counter); + assert(callback); if (prefixlen >= 64) { _cleanup_free_ char *buf = NULL; (void) in6_addr_prefix_to_string(addr, prefixlen, &buf); - log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated prefix %s since the prefix has length >= 64.", + log_link_debug(link, "Not adding a blocking route for DHCP delegated prefix %s since the prefix has length >= 64.", strna(buf)); return 0; } @@ -726,7 +770,7 @@ static int dhcp6_request_unreachable_route( if (r < 0) return log_oom(); - route->source = NETWORK_CONFIG_SOURCE_DHCP6; + route->source = source; route->provider = *server_address; route->family = AF_INET6; route->dst.in6 = *addr; @@ -741,20 +785,43 @@ static int dhcp6_request_unreachable_route( else route_unmark(existing); - r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_messages, - dhcp6_route_handler, NULL); + r = link_request_route(link, TAKE_PTR(route), true, counter, callback, NULL); if (r < 0) { _cleanup_free_ char *buf = NULL; (void) in6_addr_prefix_to_string(addr, prefixlen, &buf); - return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m", + return log_link_error_errno(link, r, "Failed to request unreachable route for DHCP delegated prefix %s: %m", strna(buf)); } return 0; } -static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) { +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, + 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_DHCP6, server_address, + &link->dhcp6_messages, dhcp6_unreachable_route_handler); +} + +static int dhcp_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) { _cleanup_free_ char *buf = NULL; struct in_addr_prefix *p; int r; @@ -775,22 +842,245 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_ (void) in6_addr_prefix_to_string(prefix, prefixlen, &buf); log_link_full(link, - set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG : + set_contains(link->dhcp_pd_prefixes, p) ? LOG_DEBUG : prefixlen > 64 || prefixlen < 48 ? LOG_WARNING : LOG_INFO, - "DHCPv6: received PD Prefix %s%s", + "DHCP: received PD Prefix %s%s", strna(buf), prefixlen > 64 ? " with prefix length > 64, ignoring." : prefixlen < 48 ? " with prefix length < 48, looks unusual.": ""); /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */ - r = set_ensure_consume(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p); + r = set_ensure_consume(&link->dhcp_pd_prefixes, &in_addr_prefix_hash_ops_free, p); if (r < 0) - return log_link_error_errno(link, r, "Failed to store DHCPv6 PD prefix %s: %m", strna(buf)); + return log_link_error_errno(link, r, "Failed to store DHCP PD prefix %s: %m", strna(buf)); return 0; } -static int dhcp6_pd_assign_prefixes(Link *link, Link *uplink) { +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; @@ -798,7 +1088,7 @@ static int dhcp6_pd_assign_prefixes(Link *link, Link *uplink) { assert(uplink); assert(uplink->dhcp6_lease); - r = dhcp6_pd_prepare(link); + r = dhcp_pd_prepare(link); if (r <= 0) return r; @@ -829,41 +1119,41 @@ static int dhcp6_pd_assign_prefixes(Link *link, Link *uplink) { lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec); if (link == uplink) - r = dhcp6_pd_assign_prefix_on_uplink(link, &pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); + r = dhcp_pd_assign_prefix_on_uplink(link, &pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); else - r = dhcp6_pd_assign_prefix(link, &pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); + r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return r; } - return dhcp6_pd_finalize(link); + return dhcp_pd_finalize(link); } -int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { +int dhcp6_pd_prefix_acquired(Link *uplink) { union in_addr_union server_address; usec_t timestamp_usec; Link *link; int r; - assert(dhcp6_link); - assert(dhcp6_link->dhcp6_lease); + assert(uplink); + assert(uplink->dhcp6_lease); - r = sd_dhcp6_lease_get_server_address(dhcp6_link->dhcp6_lease, &server_address.in6); + r = sd_dhcp6_lease_get_server_address(uplink->dhcp6_lease, &server_address.in6); if (r < 0) - return log_link_warning_errno(dhcp6_link, r, "Failed to get server address of DHCPv6 lease: %m"); + return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv6 lease: %m"); - r = sd_dhcp6_lease_get_timestamp(dhcp6_link->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_dhcp6_lease_get_timestamp(uplink->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) - return log_link_warning_errno(dhcp6_link, r, "Failed to get timestamp of DHCPv6 lease: %m"); + return log_link_warning_errno(uplink, r, "Failed to get timestamp of DHCPv6 lease: %m"); /* First, logs acquired prefixes and request unreachable routes. */ - for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) { + for (sd_dhcp6_lease_reset_pd_prefix_iter(uplink->dhcp6_lease);;) { uint32_t lifetime_preferred_sec, lifetime_valid_sec; usec_t lifetime_valid_usec; struct in6_addr pd_prefix; uint8_t pd_prefix_len; - r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix, &pd_prefix_len, + r = sd_dhcp6_lease_get_pd(uplink->dhcp6_lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred_sec, &lifetime_valid_sec); if (r < 0) break; @@ -871,31 +1161,31 @@ int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { /* Mask prefix for safety. */ r = in6_addr_mask(&pd_prefix, pd_prefix_len); if (r < 0) - return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m"); + return log_link_error_errno(uplink, r, "Failed to mask DHCPv6 PD prefix: %m"); lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec); - r = dhcp6_pd_prefix_add(dhcp6_link, &pd_prefix, pd_prefix_len); + r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefix_len); if (r < 0) return r; - r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len, lifetime_valid_usec, &server_address); + r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len, lifetime_valid_usec, &server_address); if (r < 0) return r; } /* Then, assign subnet prefixes. */ - HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { - if (!dhcp6_pd_is_uplink(link, dhcp6_link, /* accept_auto = */ true)) + HASHMAP_FOREACH(link, uplink->manager->links_by_index) { + if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true)) continue; - r = dhcp6_pd_assign_prefixes(link, dhcp6_link); + r = dhcp6_pd_assign_subnet_prefixes(link, uplink); if (r < 0) { - /* When failed on the upstream interface (i.e., the case link == dhcp6_link), + /* 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 == dhcp6_link) + if (link == uplink) return r; link_enter_failed(link); @@ -905,6 +1195,30 @@ int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { 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); @@ -929,60 +1243,76 @@ static bool dhcp6_pd_uplink_is_ready(Link *link) { return dhcp6_lease_has_pd_prefix(link->dhcp6_lease); } -int dhcp6_pd_find_uplink(Link *link, Link **ret) { +int dhcp_pd_find_uplink(Link *link, Link **ret) { Link *uplink = NULL; int r = 0; assert(link); assert(link->manager); - assert(link_dhcp6_pd_is_enabled(link)); + assert(link_dhcp_pd_is_enabled(link)); assert(ret); - if (link->network->dhcp6_pd_uplink_name) - r = link_get_by_name(link->manager, link->network->dhcp6_pd_uplink_name, &uplink); - else if (link->network->dhcp6_pd_uplink_index > 0) - r = link_get_by_index(link->manager, link->network->dhcp6_pd_uplink_index, &uplink); - else if (link->network->dhcp6_pd_uplink_index == UPLINK_INDEX_SELF) + if (link->network->dhcp_pd_uplink_name) + r = link_get_by_name(link->manager, link->network->dhcp_pd_uplink_name, &uplink); + else if (link->network->dhcp_pd_uplink_index > 0) + r = link_get_by_index(link->manager, link->network->dhcp_pd_uplink_index, &uplink); + else if (link->network->dhcp_pd_uplink_index == UPLINK_INDEX_SELF) uplink = link; if (r < 0) 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; } -int dhcp6_request_prefix_delegation(Link *link) { +int dhcp_request_prefix_delegation(Link *link) { Link *uplink; + int r; assert(link); - if (!link_dhcp6_pd_is_enabled(link)) + if (!link_dhcp_pd_is_enabled(link)) return 0; - if (dhcp6_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_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_dhcp6_pd_subnet_id( +int config_parse_dhcp_pd_subnet_id( const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-dhcp-prefix-delegation.h b/src/network/networkd-dhcp-prefix-delegation.h index aeb21d68c6..d67f919ba7 100644 --- a/src/network/networkd-dhcp-prefix-delegation.h +++ b/src/network/networkd-dhcp-prefix-delegation.h @@ -3,17 +3,23 @@ #include +#include "sd-dhcp-lease.h" +#include "sd-dhcp6-lease.h" + #include "conf-parser.h" typedef struct Link Link; -bool link_dhcp6_pd_is_enabled(Link *link); -bool dhcp6_pd_is_uplink(Link *link, Link *target, bool accept_auto); -int dhcp6_pd_find_uplink(Link *link, Link **ret); +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 dhcp6_pd_remove(Link *link, bool only_marked); -int dhcp6_request_prefix_delegation(Link *link); -int dhcp6_pd_prefix_acquired(Link *dhcp6_link); -void dhcp6_pd_prefix_lost(Link *dhcp6_link); +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_dhcp6_pd_subnet_id); +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-dhcp6.c b/src/network/networkd-dhcp6.c index b9831e7fa1..9e90366993 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -36,7 +36,7 @@ static DHCP6ClientStartMode link_get_dhcp6_client_start_mode(Link *link) { return link->network->dhcp6_client_start_mode; /* When this interface itself is an uplink interface, then start dhcp6 client in managed mode. */ - if (dhcp6_pd_is_uplink(link, link, /* accept_auto = */ false)) + if (dhcp_pd_is_uplink(link, link, /* accept_auto = */ false)) return DHCP6_CLIENT_START_MODE_SOLICIT; /* Otherwise, start dhcp6 client when RA is received. */ @@ -323,7 +323,7 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { return r; } else if (dhcp6_lease_has_pd_prefix(lease_old)) /* When we had PD prefixes but not now, we need to remove them. */ - dhcp6_pd_prefix_lost(link); + dhcp_pd_prefix_lost(link); if (link->dhcp6_messages == 0) { link->dhcp6_configured = true; @@ -354,7 +354,7 @@ static int dhcp6_lease_lost(Link *link) { log_link_info(link, "DHCPv6 lease lost"); if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) - dhcp6_pd_prefix_lost(link); + dhcp_pd_prefix_lost(link); link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index c2b632ba5b..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); @@ -235,7 +236,7 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); - link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes); + link->dhcp_pd_prefixes = set_free(link->dhcp_pd_prefixes); link_free_engines(link); @@ -381,7 +382,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); - k = dhcp6_pd_remove(link, /* only_marked = */ false); + k = dhcp_pd_remove(link, /* only_marked = */ false); if (k < 0) r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); @@ -495,7 +496,7 @@ void link_check_ready(Link *link) { NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6, - NETWORK_CONFIG_SOURCE_DHCP6PD, + NETWORK_CONFIG_SOURCE_DHCP_PD, NETWORK_CONFIG_SOURCE_NDISC)) { has_dynamic_address = true; break; @@ -503,26 +504,26 @@ void link_check_ready(Link *link) { } if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || - (link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address) + (link_dhcp_pd_is_enabled(link) && link->network->dhcp_pd_assign)) && !has_dynamic_address) /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */ - return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6, DHCPv6PD or IPv4LL is enabled but no dynamic address is assigned yet.", __func__); + return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6, DHCP-PD or IPv4LL is enabled but no dynamic address is assigned yet.", __func__); /* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */ if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || - link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || + link_dhcp6_enabled(link) || link_dhcp_pd_is_enabled(link) || (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link))) { if (!link->ipv4ll_address_configured && !link->dhcp4_configured && - !link->dhcp6_configured && !link->dhcp6_pd_configured && !link->ndisc_configured) + !link->dhcp6_configured && !link->dhcp_pd_configured && !link->ndisc_configured) /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */ return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__); - log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCPv6PD:%s NDisc:%s", + log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCP-PD:%s NDisc:%s", __func__, yes_no(link->ipv4ll_address_configured), yes_no(link->dhcp4_configured), yes_no(link->dhcp6_configured), - yes_no(link->dhcp6_pd_configured), + yes_no(link->dhcp_pd_configured), yes_no(link->ndisc_configured)); } @@ -669,14 +670,14 @@ static int link_acquire_dynamic_conf(Link *link) { return r; } - if (!link_radv_enabled(link) || !link->network->dhcp6_pd_announce) { + if (!link_radv_enabled(link) || !link->network->dhcp_pd_announce) { /* DHCPv6PD downstream does not require IPv6LL address. But may require RADV to be * configured, and RADV may not be configured yet here. Only acquire subnet prefix when * RADV is disabled, or the announcement of the prefix is disabled. Otherwise, the * below will be called in radv_start(). */ - r = dhcp6_request_prefix_delegation(link); + r = dhcp_request_prefix_delegation(link); if (r < 0) - return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m"); + return log_link_warning_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m"); } if (link->lldp_tx) { diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 4f746db110..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; @@ -146,11 +147,12 @@ typedef struct Link { sd_dhcp6_client *dhcp6_client; sd_dhcp6_lease *dhcp6_lease; - Set *dhcp6_pd_prefixes; unsigned dhcp6_messages; - unsigned dhcp6_pd_messages; - bool dhcp6_configured:1; - bool dhcp6_pd_configured:1; + bool dhcp6_configured; + + Set *dhcp_pd_prefixes; + unsigned dhcp_pd_messages; + bool dhcp_pd_configured; /* This is about LLDP reception */ sd_lldp_rx *lldp_rx; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a122e08800..7e89366ae8 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -499,10 +499,10 @@ Manager* manager_free(Manager *m) { m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->links_by_name = hashmap_free(m->links_by_name); m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr); - m->links_by_dhcp6_pd_prefix = hashmap_free(m->links_by_dhcp6_pd_prefix); + m->links_by_dhcp_pd_subnet_prefix = hashmap_free(m->links_by_dhcp_pd_subnet_prefix); m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref); - m->dhcp6_pd_subnet_ids = set_free(m->dhcp6_pd_subnet_ids); + m->dhcp_pd_subnet_ids = set_free(m->dhcp_pd_subnet_ids); m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref); m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); @@ -586,7 +586,7 @@ int manager_load_config(Manager *m) { if (r < 0) return r; - return manager_build_dhcp6_pd_subnet_ids(m); + return manager_build_dhcp_pd_subnet_ids(m); } bool manager_should_reload(Manager *m) { diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index ae9d5adf1b..36313589a3 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -48,11 +48,11 @@ struct Manager { Hashmap *links_by_index; Hashmap *links_by_name; Hashmap *links_by_hw_addr; - Hashmap *links_by_dhcp6_pd_prefix; + Hashmap *links_by_dhcp_pd_subnet_prefix; Hashmap *netdevs; OrderedHashmap *networks; OrderedSet *address_pools; - Set *dhcp6_pd_subnet_ids; + Set *dhcp_pd_subnet_ids; usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 8de72911aa..4c3bf97311 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -140,7 +140,7 @@ Network.ConfigureWithoutCarrier, config_parse_bool, Network.IgnoreCarrierLoss, config_parse_ignore_carrier_loss, 0, 0 Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration) Network.IPv6SendRA, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation) -Network.DHCPv6PrefixDelegation, config_parse_tristate, 0, offsetof(Network, dhcp6_pd) +Network.DHCPPrefixDelegation, config_parse_tristate, 0, offsetof(Network, dhcp_pd) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 Address.Broadcast, config_parse_broadcast, 0, 0 @@ -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 @@ -329,13 +330,13 @@ BridgeMDB.VLANId, config_parse_mdb_vlan_id, BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 -DHCPv6PrefixDelegation.UplinkInterface, config_parse_uplink, 0, 0 -DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp6_pd_subnet_id, 0, offsetof(Network, dhcp6_pd_subnet_id) -DHCPv6PrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp6_pd_announce) -DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign) -DHCPv6PrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp6_pd_manage_temporary_address) -DHCPv6PrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp6_pd_tokens) -DHCPv6PrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp6_pd_route_metric) +DHCPPrefixDelegation.UplinkInterface, config_parse_uplink, 0, 0 +DHCPPrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id) +DHCPPrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce) +DHCPPrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp_pd_assign) +DHCPPrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp_pd_manage_temporary_address) +DHCPPrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp_pd_tokens) +DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric) IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec) IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) @@ -513,6 +514,7 @@ TrivialLinkEqualizer.Id, config_parse_trivial_link_equalizer Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) Network.IPv6Token, config_parse_address_generation_type, 0, offsetof(Network, ndisc_tokens) Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation) +Network.DHCPv6PrefixDelegation, config_parse_tristate, 0, offsetof(Network, dhcp_pd) IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) @@ -552,6 +554,12 @@ DHCPv4.CriticalConnection, config_parse_tristate, DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0 DHCPv6.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0 DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0 +DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id) +DHCPv6PrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce) +DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp_pd_assign) +DHCPv6PrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp_pd_manage_temporary_address) +DHCPv6PrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp_pd_tokens) +DHCPv6PrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric) IPv6AcceptRA.DenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) IPv6AcceptRA.BlackList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index a659f00f64..873ad2e703 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -412,12 +412,12 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_duid.type = _DUID_TYPE_INVALID, .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID, - .dhcp6_pd = -1, - .dhcp6_pd_announce = true, - .dhcp6_pd_assign = true, - .dhcp6_pd_manage_temporary_address = true, - .dhcp6_pd_subnet_id = -1, - .dhcp6_pd_route_metric = DHCP6PD_ROUTE_METRIC, + .dhcp_pd = -1, + .dhcp_pd_announce = true, + .dhcp_pd_assign = true, + .dhcp_pd_manage_temporary_address = true, + .dhcp_pd_subnet_id = -1, + .dhcp_pd_route_metric = DHCP6PD_ROUTE_METRIC, .dhcp_server_bind_to_interface = true, .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true, @@ -498,7 +498,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "DHCP\0" /* compat */ "DHCPv4\0" "DHCPv6\0" - "DHCPv6PrefixDelegation\0" + "DHCPv6PrefixDelegation\0" /* compat */ + "DHCPPrefixDelegation\0" "DHCPServer\0" "DHCPServerStaticLease\0" "IPv6AcceptRA\0" @@ -648,7 +649,7 @@ int network_reload(Manager *manager) { ordered_hashmap_free_with_destructor(manager->networks, network_unref); manager->networks = new_networks; - return manager_build_dhcp6_pd_subnet_ids(manager); + return manager_build_dhcp_pd_subnet_ids(manager); failure: ordered_hashmap_free_with_destructor(new_networks, network_unref); @@ -656,25 +657,25 @@ failure: return r; } -int manager_build_dhcp6_pd_subnet_ids(Manager *manager) { +int manager_build_dhcp_pd_subnet_ids(Manager *manager) { Network *n; int r; assert(manager); - set_clear(manager->dhcp6_pd_subnet_ids); + set_clear(manager->dhcp_pd_subnet_ids); ORDERED_HASHMAP_FOREACH(n, manager->networks) { if (n->unmanaged) continue; - if (!n->dhcp6_pd) + if (!n->dhcp_pd) continue; - if (n->dhcp6_pd_subnet_id < 0) + if (n->dhcp_pd_subnet_id < 0) continue; - r = set_ensure_put(&manager->dhcp6_pd_subnet_ids, &uint64_hash_ops, &n->dhcp6_pd_subnet_id); + r = set_ensure_put(&manager->dhcp_pd_subnet_ids, &uint64_hash_ops, &n->dhcp_pd_subnet_id); if (r < 0) return r; } @@ -756,7 +757,7 @@ static Network *network_free(Network *network) { free(network->dhcp_server_timezone); free(network->dhcp_server_uplink_name); free(network->router_uplink_name); - free(network->dhcp6_pd_uplink_name); + free(network->dhcp_pd_uplink_name); for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++) free(network->dhcp_server_emit[t].addresses); @@ -771,7 +772,7 @@ static Network *network_free(Network *network) { ordered_hashmap_free(network->dhcp_server_send_vendor_options); ordered_hashmap_free(network->dhcp6_client_send_options); ordered_hashmap_free(network->dhcp6_client_send_vendor_options); - set_free(network->dhcp6_pd_tokens); + set_free(network->dhcp_pd_tokens); set_free(network->ndisc_tokens); return mfree(network); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 295b2dbefa..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; @@ -229,16 +230,16 @@ struct Network { int router_uplink_index; char *router_uplink_name; - /* DHCPv6 Prefix Delegation support */ - int dhcp6_pd; - bool dhcp6_pd_announce; - bool dhcp6_pd_assign; - bool dhcp6_pd_manage_temporary_address; - int64_t dhcp6_pd_subnet_id; - uint32_t dhcp6_pd_route_metric; - Set *dhcp6_pd_tokens; - int dhcp6_pd_uplink_index; - char *dhcp6_pd_uplink_name; + /* DHCP Prefix Delegation support */ + int dhcp_pd; + bool dhcp_pd_announce; + bool dhcp_pd_assign; + bool dhcp_pd_manage_temporary_address; + int64_t dhcp_pd_subnet_id; + uint32_t dhcp_pd_route_metric; + Set *dhcp_pd_tokens; + int dhcp_pd_uplink_index; + char *dhcp_pd_uplink_name; /* Bridge Support */ int use_bpdu; @@ -366,7 +367,7 @@ int network_reload(Manager *manager); int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename); int network_verify(Network *network); -int manager_build_dhcp6_pd_subnet_ids(Manager *manager); +int manager_build_dhcp_pd_subnet_ids(Manager *manager); int network_get_by_name(Manager *manager, const char *name, Network **ret); void network_apply_anonymize_if_set(Network *network); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 558bc1ac01..6fabd8f5c5 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -27,9 +27,9 @@ void network_adjust_radv(Network *network) { /* After this function is called, network->router_prefix_delegation can be treated as a boolean. */ - if (network->dhcp6_pd < 0) + if (network->dhcp_pd < 0) /* For backward compatibility. */ - network->dhcp6_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6); + network->dhcp_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6); if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) { if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE) @@ -421,8 +421,8 @@ static int radv_find_uplink(Link *link, Link **ret) { return link_get_by_index(link->manager, link->network->router_uplink_index, ret); if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) { - if (link_dhcp6_pd_is_enabled(link)) - r = dhcp6_pd_find_uplink(link, ret); /* When DHCPv6PD is enabled, use its uplink. */ + if (link_dhcp_pd_is_enabled(link)) + r = dhcp_pd_find_uplink(link, ret); /* When DHCP-PD is enabled, use its uplink. */ else r = manager_find_uplink(link->manager, AF_INET6, link, ret); if (r < 0) @@ -642,10 +642,10 @@ int radv_start(Link *link) { if (sd_radv_is_running(link->radv)) return 0; - if (link->network->dhcp6_pd_announce) { - r = dhcp6_request_prefix_delegation(link); + if (link->network->dhcp_pd_announce) { + r = dhcp_request_prefix_delegation(link); if (r < 0) - return log_link_debug_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m"); + return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m"); } log_link_debug(link, "Starting IPv6 Router Advertisements"); diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 77451e39ae..e00cc1e589 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -1180,3 +1180,43 @@ int link_request_to_bring_up_or_down(Link *link, bool up) { log_link_debug(link, "Requested to bring link %s", up_or_down(up)); return 0; } + +static int link_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + + 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, "Could not remove interface, ignoring"); + + return 0; +} + +int link_remove(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Removing link."); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_DELLINK, link->ifindex); + if (r < 0) + return log_link_debug_errno(link, r, "Could not allocate RTM_DELLINK message: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_remove_handler, + 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; +} diff --git a/src/network/networkd-setlink.h b/src/network/networkd-setlink.h index 39a85a6871..47d87837e4 100644 --- a/src/network/networkd-setlink.h +++ b/src/network/networkd-setlink.h @@ -49,3 +49,5 @@ int link_request_to_activate(Link *link); int request_process_link_up_or_down(Request *req); int link_request_to_bring_up_or_down(Link *link, bool up); + +int link_remove(Link *link); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index a3adde5ca4..ac6f1680ca 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -17,7 +17,7 @@ static const char * const network_config_source_table[_NETWORK_CONFIG_SOURCE_MAX [NETWORK_CONFIG_SOURCE_IPV4LL] = "IPv4LL", [NETWORK_CONFIG_SOURCE_DHCP4] = "DHCPv4", [NETWORK_CONFIG_SOURCE_DHCP6] = "DHCPv6", - [NETWORK_CONFIG_SOURCE_DHCP6PD] = "DHCPv6-PD", + [NETWORK_CONFIG_SOURCE_DHCP_PD] = "DHCP-PD", [NETWORK_CONFIG_SOURCE_NDISC] = "NDisc", [NETWORK_CONFIG_SOURCE_RUNTIME] = "runtime", }; diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index ba03ddcc55..d94243855e 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -25,7 +25,7 @@ typedef enum NetworkConfigSource { NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6, - NETWORK_CONFIG_SOURCE_DHCP6PD, + NETWORK_CONFIG_SOURCE_DHCP_PD, NETWORK_CONFIG_SOURCE_NDISC, NETWORK_CONFIG_SOURCE_RUNTIME, /* through D-Bus method */ _NETWORK_CONFIG_SOURCE_MAX, diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 285febd0d4..4af4b45f2d 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -97,6 +97,7 @@ enum { SD_DHCP_OPTION_SIP_SERVER = 120, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, SD_DHCP_OPTION_MUD_URL = 161, + SD_DHCP_OPTION_6RD = 212, SD_DHCP_OPTION_PRIVATE_BASE = 224, /* Windows 10 option to send when Anonymize=true */ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 5abf9a406c..478bbfd7a6 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); +int sd_dhcp_lease_get_6rd( + sd_dhcp_lease *lease, + uint8_t *ret_ipv4masklen, + uint8_t *ret_prefixlen, + struct in6_addr *ret_prefix, + const struct in_addr **ret_br_addresses, + size_t *ret_n_br_addresses); int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 9889fbfad4..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= @@ -150,6 +151,13 @@ IAID= DUIDType= DUIDRawData= [DHCPv6PrefixDelegation] +SubnetId= +Announce= +Assign= +ManageTemporaryAddress= +Token= +RouteMetric= +[DHCPPrefixDelegation] UplinkInterface= SubnetId= Announce= @@ -240,6 +248,7 @@ VRF= IgnoreCarrierLoss= KeepConfiguration= DHCPv6PrefixDelegation= +DHCPPrefixDelegation= BatmanAdvanced= IPoIB= [IPv6Prefix] diff --git a/test/test-network/conf/80-6rd-tunnel.network b/test/test-network/conf/80-6rd-tunnel.network new file mode 100644 index 0000000000..2e479eb1ac --- /dev/null +++ b/test/test-network/conf/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/test/test-network/conf/dhcp6pd-downstream-dummy97.network b/test/test-network/conf/dhcp-pd-downstream-dummy97.network similarity index 79% rename from test/test-network/conf/dhcp6pd-downstream-dummy97.network rename to test/test-network/conf/dhcp-pd-downstream-dummy97.network index b53bc3cc4d..e3e41a07e8 100644 --- a/test/test-network/conf/dhcp6pd-downstream-dummy97.network +++ b/test/test-network/conf/dhcp-pd-downstream-dummy97.network @@ -6,9 +6,9 @@ Name=dummy97 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=veth99 SubnetId=1 Announce=no diff --git a/test/test-network/conf/dhcp6pd-downstream-dummy98.network b/test/test-network/conf/dhcp-pd-downstream-dummy98.network similarity index 79% rename from test/test-network/conf/dhcp6pd-downstream-dummy98.network rename to test/test-network/conf/dhcp-pd-downstream-dummy98.network index ea66d49a2f..31090682fe 100644 --- a/test/test-network/conf/dhcp6pd-downstream-dummy98.network +++ b/test/test-network/conf/dhcp-pd-downstream-dummy98.network @@ -6,9 +6,9 @@ Name=dummy98 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=veth99 SubnetId=2 Announce=no diff --git a/test/test-network/conf/dhcp6pd-downstream-dummy99.network b/test/test-network/conf/dhcp-pd-downstream-dummy99.network similarity index 76% rename from test/test-network/conf/dhcp6pd-downstream-dummy99.network rename to test/test-network/conf/dhcp-pd-downstream-dummy99.network index ad56f26262..01c1d559aa 100644 --- a/test/test-network/conf/dhcp6pd-downstream-dummy99.network +++ b/test/test-network/conf/dhcp-pd-downstream-dummy99.network @@ -6,9 +6,9 @@ Name=dummy99 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=veth99 Assign=no Announce=no diff --git a/test/test-network/conf/dhcp6pd-downstream-test1.network b/test/test-network/conf/dhcp-pd-downstream-test1.network similarity index 79% rename from test/test-network/conf/dhcp6pd-downstream-test1.network rename to test/test-network/conf/dhcp-pd-downstream-test1.network index 78ddc72e2d..a8720c3d18 100644 --- a/test/test-network/conf/dhcp6pd-downstream-test1.network +++ b/test/test-network/conf/dhcp-pd-downstream-test1.network @@ -6,9 +6,9 @@ Name=test1 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=veth99 SubnetId=0 Announce=no diff --git a/test/test-network/conf/dhcp6pd-downstream-veth97-peer.network b/test/test-network/conf/dhcp-pd-downstream-veth97-peer.network similarity index 100% rename from test/test-network/conf/dhcp6pd-downstream-veth97-peer.network rename to test/test-network/conf/dhcp-pd-downstream-veth97-peer.network diff --git a/test/test-network/conf/dhcp6pd-downstream-veth97.network b/test/test-network/conf/dhcp-pd-downstream-veth97.network similarity index 82% rename from test/test-network/conf/dhcp6pd-downstream-veth97.network rename to test/test-network/conf/dhcp-pd-downstream-veth97.network index 5c41ea9837..daf1e9dbbd 100644 --- a/test/test-network/conf/dhcp6pd-downstream-veth97.network +++ b/test/test-network/conf/dhcp-pd-downstream-veth97.network @@ -6,10 +6,10 @@ Name=veth97 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes IPv6SendRA=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] SubnetId=8 Announce=yes Token=eui64 diff --git a/test/test-network/conf/dhcp6pd-downstream-veth98-peer.network b/test/test-network/conf/dhcp-pd-downstream-veth98-peer.network similarity index 100% rename from test/test-network/conf/dhcp6pd-downstream-veth98-peer.network rename to test/test-network/conf/dhcp-pd-downstream-veth98-peer.network diff --git a/test/test-network/conf/dhcp6pd-downstream-veth98.network b/test/test-network/conf/dhcp-pd-downstream-veth98.network similarity index 83% rename from test/test-network/conf/dhcp6pd-downstream-veth98.network rename to test/test-network/conf/dhcp-pd-downstream-veth98.network index a977099389..ef37700041 100644 --- a/test/test-network/conf/dhcp6pd-downstream-veth98.network +++ b/test/test-network/conf/dhcp-pd-downstream-veth98.network @@ -6,10 +6,10 @@ Name=veth98 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=no -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes IPv6SendRA=yes -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=veth99 SubnetId=9 Announce=yes diff --git a/test/test-network/conf/dhcp4-6rd-server.network b/test/test-network/conf/dhcp4-6rd-server.network new file mode 100644 index 0000000000..91dc71a255 --- /dev/null +++ b/test/test-network/conf/dhcp4-6rd-server.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=veth-peer + +[Network] +IPv6AcceptRA=no +Address=10.0.0.1/8 diff --git a/test/test-network/conf/dhcp4-6rd-upstream.network b/test/test-network/conf/dhcp4-6rd-upstream.network new file mode 100644 index 0000000000..e146337c5e --- /dev/null +++ b/test/test-network/conf/dhcp4-6rd-upstream.network @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=veth99 + +[Network] +IPv6PrivacyExtensions=yes +IPv6AcceptRA=no +DHCP=ipv4 +DHCPPrefixDelegation=yes + +[DHCPv4] +Use6RD=yes + +[DHCPPrefixDelegation] +UplinkInterface=:self +SubnetId=10 +Announce=no +Token=eui64 +Token=::1a:2b:3c:4d diff --git a/test/test-network/conf/dhcp6pd-upstream.network b/test/test-network/conf/dhcp6pd-upstream.network index dd26f2ae5f..4b8cd7d324 100644 --- a/test/test-network/conf/dhcp6pd-upstream.network +++ b/test/test-network/conf/dhcp6pd-upstream.network @@ -6,12 +6,12 @@ Name=veth99 IPv6PrivacyExtensions=yes IPv6AcceptRA=no DHCP=ipv6 -DHCPv6PrefixDelegation=yes +DHCPPrefixDelegation=yes [DHCPv6] WithoutRA=solicit -[DHCPv6PrefixDelegation] +[DHCPPrefixDelegation] UplinkInterface=:self SubnetId=10 Announce=no diff --git a/test/test-network/conf/isc-dhcpd-6rd.conf b/test/test-network/conf/isc-dhcpd-6rd.conf new file mode 100644 index 0000000000..0b32f4b696 --- /dev/null +++ b/test/test-network/conf/isc-dhcpd-6rd.conf @@ -0,0 +1,22 @@ +default-lease-time 2592000; +preferred-lifetime 604800; + +default-lease-time 600; +max-lease-time 7200; + +option domain-name-servers 10.0.0.2; +option domain-search "test.example.com","example.com"; + +# ipv4masklen = 8 +# 6rd prefix = 2001:db8::/32 +# BR address = 10.0.0.1 +option option-6rd code 212 = { + integer 8, integer 8, ip6-address, array of ip-address +}; + +option option-6rd 8 32 2001:db8:: 10.0.0.1; + +subnet 10.0.0.0 netmask 255.0.0.0 { + # Addresses available to clients + range 10.100.100.100 10.100.100.199; +} diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index f9af5f14d3..68cdad30ac 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4994,7 +4994,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') -class NetworkdDHCP6PDTests(unittest.TestCase, Utilities): +class NetworkdDHCPPDTests(unittest.TestCase, Utilities): links = [ 'dummy97', 'dummy98', @@ -5012,14 +5012,17 @@ class NetworkdDHCP6PDTests(unittest.TestCase, Utilities): '25-veth.netdev', '25-veth-downstream-veth97.netdev', '25-veth-downstream-veth98.netdev', - 'dhcp6pd-downstream-dummy97.network', - 'dhcp6pd-downstream-dummy98.network', - 'dhcp6pd-downstream-dummy99.network', - 'dhcp6pd-downstream-test1.network', - 'dhcp6pd-downstream-veth97.network', - 'dhcp6pd-downstream-veth97-peer.network', - 'dhcp6pd-downstream-veth98.network', - 'dhcp6pd-downstream-veth98-peer.network', + '80-6rd-tunnel.network', + 'dhcp-pd-downstream-dummy97.network', + 'dhcp-pd-downstream-dummy98.network', + 'dhcp-pd-downstream-dummy99.network', + 'dhcp-pd-downstream-test1.network', + 'dhcp-pd-downstream-veth97.network', + 'dhcp-pd-downstream-veth97-peer.network', + 'dhcp-pd-downstream-veth98.network', + 'dhcp-pd-downstream-veth98-peer.network', + 'dhcp4-6rd-server.network', + 'dhcp4-6rd-upstream.network', 'dhcp6pd-server.network', 'dhcp6pd-upstream.network', ] @@ -5037,12 +5040,12 @@ class NetworkdDHCP6PDTests(unittest.TestCase, Utilities): def test_dhcp6pd(self): copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp6pd-server.network', 'dhcp6pd-upstream.network', - '25-veth-downstream-veth97.netdev', 'dhcp6pd-downstream-veth97.network', 'dhcp6pd-downstream-veth97-peer.network', - '25-veth-downstream-veth98.netdev', 'dhcp6pd-downstream-veth98.network', 'dhcp6pd-downstream-veth98-peer.network', - '11-dummy.netdev', 'dhcp6pd-downstream-test1.network', - 'dhcp6pd-downstream-dummy97.network', - '12-dummy.netdev', 'dhcp6pd-downstream-dummy98.network', - '13-dummy.netdev', 'dhcp6pd-downstream-dummy99.network') + '25-veth-downstream-veth97.netdev', 'dhcp-pd-downstream-veth97.network', 'dhcp-pd-downstream-veth97-peer.network', + '25-veth-downstream-veth98.netdev', 'dhcp-pd-downstream-veth98.network', 'dhcp-pd-downstream-veth98-peer.network', + '11-dummy.netdev', 'dhcp-pd-downstream-test1.network', + 'dhcp-pd-downstream-dummy97.network', + '12-dummy.netdev', 'dhcp-pd-downstream-dummy98.network', + '13-dummy.netdev', 'dhcp-pd-downstream-dummy99.network') start_networkd() self.wait_online(['veth-peer:routable']) @@ -5229,6 +5232,242 @@ class NetworkdDHCP6PDTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto dhcp metric [0-9]* expires') + def test_dhcp4_6rd(self): + copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp4-6rd-server.network', 'dhcp4-6rd-upstream.network', + '25-veth-downstream-veth97.netdev', 'dhcp-pd-downstream-veth97.network', 'dhcp-pd-downstream-veth97-peer.network', + '25-veth-downstream-veth98.netdev', 'dhcp-pd-downstream-veth98.network', 'dhcp-pd-downstream-veth98-peer.network', + '11-dummy.netdev', 'dhcp-pd-downstream-test1.network', + 'dhcp-pd-downstream-dummy97.network', + '12-dummy.netdev', 'dhcp-pd-downstream-dummy98.network', + '13-dummy.netdev', 'dhcp-pd-downstream-dummy99.network', + '80-6rd-tunnel.network') + + start_networkd() + self.wait_online(['veth-peer:routable']) + start_isc_dhcpd('veth-peer', 'isc-dhcpd-6rd.conf', ip='-4') + self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', + 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) + + print('### ip -4 address show dev veth-peer scope global') + output = check_output('ip -4 address show dev veth-peer scope global') + print(output) + self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output) + + ''' + Link Subnet IDs + test1: 0x00 + dummy97: 0x01 (The link will appear later) + dummy98: 0x02 + dummy99: auto -> 0x03 (No address assignment) + veth97: 0x08 + veth98: 0x09 + veth99: 0x10 + ''' + + print('### ip -4 address show dev veth99 scope global') + output = check_output('ip -4 address show dev veth99 scope global') + print(output) + self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99') + + print('### ip -6 address show dev veth99 scope global') + output = check_output('ip -6 address show dev veth99 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (Token=eui64) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + # Note that the temporary addresses may appear after the link enters configured state + self.wait_address('veth99', 'inet6 2001:db8:6464:[0-9a-f]+10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev test1 scope global') + output = check_output('ip -6 address show dev test1 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('test1', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev dummy98 scope global') + output = check_output('ip -6 address show dev dummy98 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev dummy99 scope global') + output = check_output('ip -6 address show dev dummy99 scope global') + print(output) + # Assign=no + self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+03') + + print('### ip -6 address show dev veth97 scope global') + output = check_output('ip -6 address show dev veth97 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (Token=eui64) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('veth97', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev veth97-peer scope global') + output = check_output('ip -6 address show dev veth97-peer scope global') + print(output) + # NDisc address (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + # NDisc address (Token=eui64) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr') + # NDisc address (temporary) + self.wait_address('veth97-peer', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev veth98 scope global') + output = check_output('ip -6 address show dev veth98 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (Token=eui64) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('veth98', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev veth98-peer scope global') + output = check_output('ip -6 address show dev veth98-peer scope global') + print(output) + # NDisc address (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + # NDisc address (Token=eui64) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr') + # NDisc address (temporary) + self.wait_address('veth98-peer', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 route show type unreachable') + output = check_output('ip -6 route show type unreachable') + print(output) + self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp') + + print('### ip -6 route show dev veth99') + output = check_output('ip -6 route show dev veth99') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev test1') + output = check_output('ip -6 route show dev test1') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev dummy98') + output = check_output('ip -6 route show dev dummy98') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+02::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev dummy99') + output = check_output('ip -6 route show dev dummy99') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+03::/64 proto dhcp metric [0-9]* expires') + + print('### ip -6 route show dev veth97') + output = check_output('ip -6 route show dev veth97') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev veth97-peer') + output = check_output('ip -6 route show dev veth97-peer') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires') + + print('### ip -6 route show dev veth98') + output = check_output('ip -6 route show dev veth98') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev veth98-peer') + output = check_output('ip -6 route show dev veth98-peer') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires') + + # Test case for a downstream which appears later + check_output('ip link add dummy97 type dummy') + self.wait_online(['dummy97:routable']) + + print('### ip -6 address show dev dummy97 scope global') + output = check_output('ip -6 address show dev dummy97 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('dummy97', 'inet6 2001:db8:6464:[0-9a-f]+01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 route show dev dummy97') + output = check_output('ip -6 route show dev dummy97') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires') + + # Test case for reconfigure + check_output(*networkctl_cmd, 'reconfigure', 'dummy98', 'dummy99', env=env) + self.wait_online(['dummy98:routable']) + + print('### ip -6 address show dev dummy98 scope global') + output = check_output('ip -6 address show dev dummy98 scope global') + print(output) + # address in IA_PD (Token=static) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + # address in IA_PD (temporary) + self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + + print('### ip -6 address show dev dummy99 scope global') + output = check_output('ip -6 address show dev dummy99 scope global') + print(output) + # Assign=no + self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+03') + + print('### ip -6 route show dev dummy98') + output = check_output('ip -6 route show dev dummy98') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+02::/64 proto kernel metric [0-9]* expires') + + print('### ip -6 route show dev dummy99') + output = check_output('ip -6 route show dev dummy99') + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+03::/64 proto dhcp metric [0-9]* expires') + + tunnel_name = None + for name in os.listdir('/sys/class/net/'): + if name.startswith('6rd-'): + tunnel_name = name + break + + self.wait_online(['{}:routable'.format(tunnel_name)]) + + print('### ip -d link show dev {}'.format(tunnel_name)) + output = check_output('ip -d link show dev {}'.format(tunnel_name)) + print(output) + self.assertIn('link/sit 10.100.100.', output) + self.assertIn('local 10.100.100.', output) + self.assertIn('dev veth99', output) + self.assertIn('ttl 64', output) + self.assertIn('6rd-prefix 2001:db8::/32', output) + self.assertIn('6rd-relay_prefix 10.0.0.0/8', output) + + print('### ip -6 address show dev {}'.format(tunnel_name)) + output = check_output('ip -6 address show dev {}'.format(tunnel_name)) + print(output) + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/56 (metric 256 |)scope global dynamic') + self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global') + + print('### ip -6 route show dev {}'.format(tunnel_name)) + output = check_output('ip -6 route show dev {}'.format(tunnel_name)) + print(output) + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/56 proto kernel metric [0-9]* expires') + self.assertRegex(output, '::/96 proto kernel metric [0-9]*') + + print('### ip -6 route show default') + output = check_output('ip -6 route show default') + print(output) + self.assertIn('default', output) + self.assertIn('via ::10.0.0.1 dev {}'.format(tunnel_name), output) + class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): links = [ 'dummy98',