From 1925f829ab17cee7d65cc8c350d8281f8f41588e Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Fri, 28 Jul 2023 22:51:50 +0530 Subject: [PATCH] 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:: ``` --- man/systemd.network.xml | 25 +++ src/libsystemd-network/radv-internal.h | 34 +++++ src/libsystemd-network/sd-radv.c | 150 ++++++++++++++++++ src/network/networkd-network-gperf.gperf | 2 + src/network/networkd-network.c | 5 + src/network/networkd-network.h | 1 + src/network/networkd-radv.c | 185 +++++++++++++++++++++++ src/network/networkd-radv.h | 13 ++ src/systemd/sd-radv.h | 9 ++ 9 files changed, 424 insertions(+) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 93830b5935b..87c6c60e296 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -3249,6 +3249,31 @@ Token=prefixstable:2002:da8:1:: + + [IPv6PREF64Prefix] Section Options + One or more [IPv6PREF64Prefix] sections contain the IPv6 PREF64 (or NAT64) prefixes that are announced via Router + Advertisements. See RFC 8781 for further + details. + + + + + Prefix= + + 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 + / character. Valid NAT64 prefix length are 96, 64, 56, 48, 40, and 32 bits. + + + LifetimeSec= + Lifetime for the prefix measured in seconds. Should be greater than or equal to RouterLifetimeSec=. + LifetimeSec= defaults to 1800 seconds. + + + + [Bridge] Section Options The [Bridge] section accepts the following keys: diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 4bb32f21283..534d7959827 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 5d4dfdef028..983c01b202f 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 40e78c91b30..022bd597583 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 b5eef894be9..bdf5acceb55 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 cb8626f008e..7270ace45d3 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 1bf6fc99d97..57fd68f5a0f 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 e1c22d054f2..8ea1a85b26c 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 98fda297a72..882613c0b3f 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;