summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSusant Sahani <ssahani@gmail.com>2023-07-28 19:21:50 +0200
committerDaan De Meyer <daan.j.demeyer@gmail.com>2023-08-25 15:13:06 +0200
commit1925f829ab17cee7d65cc8c350d8281f8f41588e (patch)
treea829ebb3063153e99dc16dfc7a33dda022b5a9ac
parentMerge pull request #28917 from yuwata/network-address-pool (diff)
downloadsystemd-1925f829ab17cee7d65cc8c350d8281f8f41588e.tar.xz
systemd-1925f829ab17cee7d65cc8c350d8281f8f41588e.zip
network: sd-radv - Introduce pref64 support (RFC8781)
Implements: https://datatracker.ietf.org/doc/html/rfc8781 ``` [IPv6PREF64Prefix] Prefix=2003:da8:1:0::/64 ValidLifetimeSec=30m Frame 16: 126 bytes on wire (1008 bits), 126 bytes captured (1008 bits) on interface veth99, id 0 Ethernet II, Src: 06:c7:41:95:1d:7f (06:c7:41:95:1d:7f), Dst: IPv6mcast_01 (33:33:00:00:00:01) Internet Protocol Version 6, Src: fe80::4c7:41ff:fe95:1d7f, Dst: ff02::1 Internet Control Message Protocol v6 Type: Router Advertisement (134) Code: 0 Checksum: 0x0ca0 [correct] [Checksum Status: Good] Cur hop limit: 0 Flags: 0x00, Prf (Default Router Preference): Medium Router lifetime (s): 1800 Reachable time (ms): 0 Retrans timer (ms): 0 ICMPv6 Option (Source link-layer address : 06:c7:41:95:1d:7f) ICMPv6 Option (Prefix information : 2002:da8:1::/64) ICMPv6 Option (PREF64 Option) Type: PREF64 Option (38) Length: 2 (16 bytes) 0000 0111 0000 1... = Scaled Lifetime: 225 .... .... .... .001 = PLC (Prefix Length Code): 64 bits prefix length (0x1) Prefix: 64:ff9b:: ```
-rw-r--r--man/systemd.network.xml25
-rw-r--r--src/libsystemd-network/radv-internal.h34
-rw-r--r--src/libsystemd-network/sd-radv.c150
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c5
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/network/networkd-radv.c185
-rw-r--r--src/network/networkd-radv.h13
-rw-r--r--src/systemd/sd-radv.h9
9 files changed, 424 insertions, 0 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 93830b5935..87c6c60e29 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -3250,6 +3250,31 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
</refsect1>
<refsect1>
+ <title>[IPv6PREF64Prefix] Section Options</title>
+ <para>One or more [IPv6PREF64Prefix] sections contain the IPv6 PREF64 (or NAT64) prefixes that are announced via Router
+ Advertisements. See <ulink url="https://tools.ietf.org/html/rfc8781">RFC 8781</ulink> for further
+ details.</para>
+
+ <variablelist class='network-directives'>
+
+ <varlistentry>
+ <term><varname>Prefix=</varname></term>
+
+ <listitem><para>The IPv6 PREF64 (or NAT64) prefix that is to be distributed to hosts. The setting holds
+ an IPv6 prefix that should be set up for NAT64 translation (PLAT) to allow 464XLAT on the network segment.
+ Use multiple [IPv6PREF64Prefix] sections to configure multiple IPv6 prefixes since prefix lifetime may differ
+ from one prefix to another. The prefix is an address with a prefix length, separated by a slash
+ <literal>/</literal> character. Valid NAT64 prefix length are 96, 64, 56, 48, 40, and 32 bits.</para></listitem></varlistentry>
+
+ <varlistentry>
+ <term><varname>LifetimeSec=</varname></term>
+ <listitem><para>Lifetime for the prefix measured in seconds. Should be greater than or equal to <varname>RouterLifetimeSec=</varname>.
+ <varname>LifetimeSec=</varname> defaults to 1800 seconds.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
<title>[Bridge] Section Options</title>
<para>The [Bridge] section accepts the following keys:</para>
<variablelist class='network-directives'>
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 4bb32f2128..534d795982 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -55,10 +55,15 @@
#define RADV_MAX_FINAL_RTR_ADVERTISEMENTS 3
#define RADV_MIN_DELAY_BETWEEN_RAS 3
#define RADV_MAX_RA_DELAY_TIME_USEC (500 * USEC_PER_MSEC)
+/* From RFC 8781 section 4.1
+ * By default, the value of the Scaled Lifetime field SHOULD be set to the lesser of 3 x MaxRtrAdvInterval */
+#define RADV_DEFAULT_PRE64_LIFETIME_USEC (3 * RADV_DEFAULT_MAX_TIMEOUT_USEC)
#define RADV_OPT_ROUTE_INFORMATION 24
#define RADV_OPT_RDNSS 25
#define RADV_OPT_DNSSL 31
+/* Pref64 option type (RFC8781, section 4) */
+#define RADV_OPT_PREF64 38
enum RAdvState {
RADV_STATE_IDLE = 0,
@@ -101,6 +106,9 @@ struct sd_radv {
unsigned n_route_prefixes;
LIST_HEAD(sd_radv_route_prefix, route_prefixes);
+ unsigned n_pref64_prefixes;
+ LIST_HEAD(sd_radv_pref64_prefix, pref64_prefixes);
+
size_t n_rdnss;
struct sd_radv_opt_dns *rdnss;
struct sd_radv_opt_dns *dnssl;
@@ -172,6 +180,32 @@ struct sd_radv_route_prefix {
usec_t valid_until;
};
+/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PLC (Prefix Length Code): 3-bit unsigned integer */
+#define radv_pref64_prefix_opt__contents { \
+ uint8_t type; \
+ uint8_t length; \
+ uint16_t lifetime_and_plc; \
+ uint8_t prefix[12]; \
+}
+
+struct radv_pref64_prefix_opt radv_pref64_prefix_opt__contents;
+
+struct radv_pref64_prefix_opt__packed radv_pref64_prefix_opt__contents _packed_;
+assert_cc(sizeof(struct radv_pref64_prefix_opt) == sizeof(struct radv_pref64_prefix_opt__packed));
+
+struct sd_radv_pref64_prefix {
+ unsigned n_ref;
+
+ struct radv_pref64_prefix_opt opt;
+
+ struct in6_addr in6_addr;
+ uint8_t prefixlen;
+
+ usec_t lifetime_usec;
+
+ LIST_FIELDS(struct sd_radv_pref64_prefix, prefix);
+};
+
#define log_radv_errno(radv, error, fmt, ...) \
log_interface_prefix_full_errno( \
"RADV: ", \
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 5d4dfdef02..983c01b202 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -25,6 +25,7 @@
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
+#include "unaligned.h"
int sd_radv_new(sd_radv **ret) {
_cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
@@ -220,6 +221,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));
}
+ LIST_FOREACH(prefix, p, ra->pref64_prefixes)
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
+
if (ra->rdnss)
iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
@@ -738,6 +742,78 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
return 0;
}
+int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
+ sd_radv_pref64_prefix *found = NULL;
+ int r;
+
+ assert_return(ra, -EINVAL);
+ assert_return(p, -EINVAL);
+
+ const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen);
+
+ LIST_FOREACH(prefix, cur, ra->pref64_prefixes) {
+ r = in_addr_prefix_intersect(AF_INET6,
+ (const union in_addr_union*) &cur->in6_addr,
+ cur->prefixlen,
+ (const union in_addr_union*) &p->in6_addr,
+ p->prefixlen);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (cur->prefixlen == p->prefixlen) {
+ found = cur;
+ break;
+ }
+
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
+ "IPv6 PREF64 prefix %s conflicts with %s, ignoring.",
+ addr_p,
+ IN6_ADDR_PREFIX_TO_STRING(&cur->in6_addr, cur->prefixlen));
+ }
+
+ if (found) {
+ /* p and cur may be equivalent. First increment the reference counter. */
+ sd_radv_pref64_prefix_ref(p);
+
+ /* Then, remove the old entry. */
+ LIST_REMOVE(prefix, ra->pref64_prefixes, found);
+ sd_radv_pref64_prefix_unref(found);
+
+ /* Finally, add the new entry. */
+ LIST_APPEND(prefix, ra->pref64_prefixes, p);
+
+ log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
+ strna(addr_p),
+ FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
+ } else {
+ /* The route prefix is new. Let's simply add it. */
+
+ sd_radv_pref64_prefix_ref(p);
+ LIST_APPEND(prefix, ra->pref64_prefixes, p);
+ ra->n_pref64_prefixes++;
+
+ log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
+ }
+
+ if (ra->state == RADV_STATE_IDLE)
+ return 0;
+
+ if (ra->ra_sent == 0)
+ return 0;
+
+ /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
+ r = radv_send(ra, NULL, ra->lifetime_usec);
+ if (r < 0)
+ log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m",
+ strna(addr_p));
+ else
+ log_radv(ra, "Sent Router Advertisement for added PREF64 prefix %s.", strna(addr_p));
+
+ return 0;
+}
+
int sd_radv_set_rdnss(
sd_radv *ra,
uint32_t lifetime,
@@ -983,3 +1059,77 @@ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime
return 0;
}
+
+int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret) {
+ sd_radv_pref64_prefix *p;
+
+ assert_return(ret, -EINVAL);
+
+ p = new(sd_radv_pref64_prefix, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (sd_radv_pref64_prefix) {
+ .n_ref = 1,
+
+ .opt.type = RADV_OPT_PREF64,
+ .opt.length = 2,
+ };
+
+ *ret = p;
+ return 0;
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix, mfree);
+
+int sd_radv_pref64_prefix_set_prefix(
+ sd_radv_pref64_prefix *p,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint64_t lifetime_usec) {
+
+ uint16_t pref64_lifetime;
+ uint8_t prefixlen_code;
+
+ assert_return(p, -EINVAL);
+ assert_return(prefix, -EINVAL);
+
+ switch (prefixlen) {
+ case 96:
+ prefixlen_code = 0;
+ break;
+ case 64:
+ prefixlen_code = 1;
+ break;
+ case 56:
+ prefixlen_code = 2;
+ break;
+ case 48:
+ prefixlen_code = 3;
+ break;
+ case 40:
+ prefixlen_code = 4;
+ break;
+ case 32:
+ prefixlen_code = 5;
+ break;
+ default:
+ log_radv(NULL, "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
+ return -EINVAL;
+ }
+
+ if (lifetime_usec == USEC_INFINITY || DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13)
+ return -EINVAL;
+
+ /* RFC 8781 - 4.1 rounding up lifetime to multiply of 8 */
+ pref64_lifetime = DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) << 3;
+ pref64_lifetime |= prefixlen_code;
+
+ unaligned_write_be16(&p->opt.lifetime_and_plc, pref64_lifetime);
+ memcpy(&p->opt.prefix, prefix, sizeof(p->opt.prefix));
+
+ p->in6_addr = *prefix;
+ p->prefixlen = prefixlen;
+
+ return 0;
+}
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 40e78c91b3..022bd59758 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -389,6 +389,8 @@ IPv6Prefix.RouteMetric, config_parse_prefix_metric,
IPv6Prefix.Token, config_parse_prefix_token, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
+IPv6PREF64Prefix.Prefix, config_parse_pref64_prefix, 0, 0
+IPv6PREF64Prefix.LifetimeSec, config_parse_pref64_prefix_lifetime, 0, 0
LLDP.MUDURL, config_parse_mud_url, 0, offsetof(Network, lldp_mudurl)
CAN.BitRate, config_parse_can_bitrate, 0, offsetof(Network, can_bitrate)
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index b5eef894be..bdf5acceb5 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -529,6 +529,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"IPv6PrefixDelegation\0"
"IPv6Prefix\0"
"IPv6RoutePrefix\0"
+ "IPv6PREF64Prefix\0"
"LLDP\0"
"TrafficControlQueueingDiscipline\0"
"CAN\0"
@@ -779,6 +780,7 @@ static Network *network_free(Network *network) {
hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
+ hashmap_free_with_destructor(network->pref64_prefixes_by_section, pref64_prefix_free);
hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
@@ -844,6 +846,9 @@ bool network_has_static_ipv6_configurations(Network *network) {
if (!hashmap_isempty(network->route_prefixes_by_section))
return true;
+ if (!hashmap_isempty(network->pref64_prefixes_by_section))
+ return true;
+
return false;
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index cb8626f008..7270ace45d 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -358,6 +358,7 @@ struct Network {
Hashmap *address_labels_by_section;
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
+ Hashmap *pref64_prefixes_by_section;
Hashmap *rules_by_section;
Hashmap *dhcp_static_leases_by_section;
Hashmap *qdiscs_by_section;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 1bf6fc99d9..57fd68f5a0 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -48,6 +48,7 @@ void network_adjust_radv(Network *network) {
if (!FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_STATIC)) {
network->prefixes_by_section = hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
network->route_prefixes_by_section = hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
+ network->pref64_prefixes_by_section = hashmap_free_with_destructor(network->pref64_prefixes_by_section, pref64_prefix_free);
}
}
@@ -177,6 +178,61 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig
return 0;
}
+pref64Prefix *pref64_prefix_free(pref64Prefix *prefix) {
+ if (!prefix)
+ return NULL;
+
+ if (prefix->network) {
+ assert(prefix->section);
+ hashmap_remove(prefix->network->pref64_prefixes_by_section, prefix->section);
+ }
+
+ config_section_free(prefix->section);
+
+ return mfree(prefix);
+}
+
+DEFINE_SECTION_CLEANUP_FUNCTIONS(pref64Prefix, pref64_prefix_free);
+
+static int pref64_prefix_new_static(Network *network, const char *filename, unsigned section_line, pref64Prefix **ret) {
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
+ _cleanup_(pref64_prefix_freep) pref64Prefix *prefix = NULL;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(filename);
+ assert(section_line > 0);
+
+ r = config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ prefix = hashmap_get(network->pref64_prefixes_by_section, n);
+ if (prefix) {
+ *ret = TAKE_PTR(prefix);
+ return 0;
+ }
+
+ prefix = new(pref64Prefix, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ *prefix = (pref64Prefix) {
+ .network = network,
+ .section = TAKE_PTR(n),
+
+ .lifetime = RADV_DEFAULT_PRE64_LIFETIME_USEC,
+ };
+
+ r = hashmap_ensure_put(&network->pref64_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(prefix);
+ return 0;
+}
+
int link_request_radv_addresses(Link *link) {
Prefix *p;
int r;
@@ -294,6 +350,25 @@ static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
return sd_radv_add_route_prefix(link->radv, p);
}
+static int radv_set_pref64_prefix(Link *link, pref64Prefix *prefix) {
+ _cleanup_(sd_radv_pref64_prefix_unrefp) sd_radv_pref64_prefix *p = NULL;
+ int r;
+
+ assert(link);
+ assert(link->radv);
+ assert(prefix);
+
+ r = sd_radv_pref64_prefix_new(&p);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_pref64_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen, prefix->lifetime);
+ if (r < 0)
+ return r;
+
+ return sd_radv_add_pref64_prefix(link->radv, p);
+}
+
static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
_cleanup_free_ struct in6_addr *addresses = NULL;
size_t n_addresses = 0;
@@ -441,6 +516,7 @@ static int radv_find_uplink(Link *link, Link **ret) {
static int radv_configure(Link *link) {
Link *uplink = NULL;
RoutePrefix *q;
+ pref64Prefix *n;
Prefix *p;
int r;
@@ -508,6 +584,12 @@ static int radv_configure(Link *link) {
return r;
}
+ HASHMAP_FOREACH(n, link->network->pref64_prefixes_by_section) {
+ r = radv_set_pref64_prefix(link, n);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
(void) radv_find_uplink(link, &uplink);
r = radv_set_dns(link, uplink);
@@ -792,6 +874,16 @@ void network_drop_invalid_route_prefixes(Network *network) {
route_prefix_free(p);
}
+void network_drop_invalid_pref64_prefixes(Network *network) {
+ pref64Prefix *p;
+
+ assert(network);
+
+ HASHMAP_FOREACH(p, network->pref64_prefixes_by_section)
+ if (section_is_invalid(p->section))
+ pref64_prefix_free(p);
+}
+
int config_parse_prefix(
const char *unit,
const char *filename,
@@ -1083,6 +1175,99 @@ int config_parse_route_prefix_lifetime(
return 0;
}
+int config_parse_pref64_prefix(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
+ Network *network = ASSERT_PTR(userdata);
+ union in_addr_union a;
+ uint8_t prefixlen;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = pref64_prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return log_oom();
+
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "PREF64 prefix is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (!IN_SET(prefixlen, 96, 64, 56, 48, 40, 32)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "PREF64 prefixlen is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ (void) in6_addr_mask(&a.in6,prefixlen);
+ p->prefix = a.in6;
+ p->prefixlen = prefixlen;
+
+ TAKE_PTR(p);
+ return 0;
+}
+
+int config_parse_pref64_prefix_lifetime(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(pref64_prefix_free_or_set_invalidp) pref64Prefix *p = NULL;
+ Network *network = ASSERT_PTR(userdata);
+ usec_t usec;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = pref64_prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return log_oom();
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "PREF64 lifetime is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (usec == USEC_INFINITY || DIV_ROUND_UP(usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "PREF64 lifetime is too long, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ p->lifetime = usec;
+
+ TAKE_PTR(p);
+ return 0;
+}
+
int config_parse_radv_dns(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index e1c22d054f..8ea1a85b26 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -52,11 +52,22 @@ typedef struct RoutePrefix {
usec_t lifetime;
} RoutePrefix;
+typedef struct pref64Prefix {
+ Network *network;
+ ConfigSection *section;
+
+ struct in6_addr prefix;
+ uint8_t prefixlen;
+ usec_t lifetime;
+} pref64Prefix;
+
Prefix *prefix_free(Prefix *prefix);
RoutePrefix *route_prefix_free(RoutePrefix *prefix);
+pref64Prefix *pref64_prefix_free(pref64Prefix *prefix);
void network_drop_invalid_prefixes(Network *network);
void network_drop_invalid_route_prefixes(Network *network);
+void network_drop_invalid_pref64_prefixes(Network *network);
void network_adjust_radv(Network *network);
int link_request_radv_addresses(Link *link);
@@ -85,3 +96,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_pref64_prefix);
+CONFIG_PARSER_PROTOTYPE(config_parse_pref64_prefix_lifetime);
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 98fda297a7..882613c0b3 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -33,6 +33,7 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_radv sd_radv;
typedef struct sd_radv_prefix sd_radv_prefix;
typedef struct sd_radv_route_prefix sd_radv_route_prefix;
+typedef struct sd_radv_pref64_prefix sd_radv_pref64_prefix;
/* Router Advertisement */
int sd_radv_new(sd_radv **ret);
@@ -60,6 +61,7 @@ int sd_radv_set_other_information(sd_radv *ra, int other);
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p);
+int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p);
void sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, unsigned char prefixlen);
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns);
@@ -87,9 +89,16 @@ sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra);
int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
+int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret);
+int sd_radv_pref64_prefix_set_prefix(sd_radv_pref64_prefix *p, const struct in6_addr *prefix,
+ uint8_t prefixlen, uint64_t lifetime_usec);
+sd_radv_pref64_prefix *sd_radv_pref64_prefix_ref(sd_radv_pref64_prefix *ra);
+sd_radv_pref64_prefix *sd_radv_pref64_prefix_unref(sd_radv_pref64_prefix *ra);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_route_prefix, sd_radv_route_prefix_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix_unref);
_SD_END_DECLARATIONS;